157 lines
4.0 KiB
Rust
157 lines
4.0 KiB
Rust
|
|
use crate::tui::component::ClideComponent;
|
||
|
|
use anyhow::Result;
|
||
|
|
use nvim_rs::compat::tokio::Compat;
|
||
|
|
use nvim_rs::{Handler, Neovim, UiAttachOptions, Value};
|
||
|
|
use ratatui::buffer::Buffer;
|
||
|
|
use ratatui::layout::Rect;
|
||
|
|
use ratatui::text::Line;
|
||
|
|
use ratatui::widgets::{Paragraph, Widget};
|
||
|
|
use std::process::Stdio;
|
||
|
|
use std::sync::{Arc, Mutex};
|
||
|
|
use tokio::process::Command;
|
||
|
|
|
||
|
|
struct Editor {
|
||
|
|
ui: Arc<NvimUI>,
|
||
|
|
height: usize,
|
||
|
|
width: usize,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Editor {
|
||
|
|
fn new(height: usize, width: usize) -> Self {
|
||
|
|
let editor = Editor {
|
||
|
|
ui: Arc::new(NvimUI::default()),
|
||
|
|
height,
|
||
|
|
width,
|
||
|
|
};
|
||
|
|
editor
|
||
|
|
.ui
|
||
|
|
.grid
|
||
|
|
.lock()
|
||
|
|
.unwrap()
|
||
|
|
.resize(height, vec![' '; width]);
|
||
|
|
editor
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a> Widget for &Editor {
|
||
|
|
fn render(self, area: Rect, buf: &mut Buffer)
|
||
|
|
where
|
||
|
|
Self: Sized,
|
||
|
|
{
|
||
|
|
let grid = self.ui.grid.lock().unwrap();
|
||
|
|
|
||
|
|
let lines: Vec<Line> = grid
|
||
|
|
.iter()
|
||
|
|
.map(|row| Line::from(row.iter().collect::<String>()))
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
Paragraph::new(lines).render(area, buf);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ClideComponent for Editor {}
|
||
|
|
|
||
|
|
#[derive(Default, Clone)]
|
||
|
|
pub struct NvimUI {
|
||
|
|
pub grid: Arc<Mutex<Vec<Vec<char>>>>,
|
||
|
|
pub cursor: Arc<Mutex<(usize, usize)>>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Handler for NvimUI {
|
||
|
|
type Writer = Compat<tokio::process::ChildStdin>;
|
||
|
|
|
||
|
|
async fn handle_notify(
|
||
|
|
&self,
|
||
|
|
_name: String,
|
||
|
|
_args: Vec<Value>,
|
||
|
|
_neovim: Neovim<Compat<tokio::process::ChildStdin>>,
|
||
|
|
) -> Result<()> {
|
||
|
|
if _name != "redraw" {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
for event in _args {
|
||
|
|
if let Value::Array(items) = event {
|
||
|
|
if items.is_empty() {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
let event_name = items[0].as_str().unwrap_or("");
|
||
|
|
|
||
|
|
match event_name {
|
||
|
|
"grid_line" => self.handle_grid_line(&items),
|
||
|
|
"cursor_goto" => self.handle_cursor(&items),
|
||
|
|
_ => {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl NvimUI {
|
||
|
|
fn handle_grid_line(&self, items: &[Value]) {
|
||
|
|
// ["grid_line", grid, row, col, cells]
|
||
|
|
let row = items[2].as_u64().unwrap() as usize;
|
||
|
|
let col = items[3].as_u64().unwrap() as usize;
|
||
|
|
|
||
|
|
let cells = items[4].as_array().unwrap();
|
||
|
|
|
||
|
|
let mut grid = self.grid.lock().unwrap();
|
||
|
|
let mut c = col;
|
||
|
|
|
||
|
|
for cell in cells {
|
||
|
|
let cell_arr = cell.as_array().unwrap();
|
||
|
|
let text = cell_arr[0].as_str().unwrap();
|
||
|
|
let repeat = cell_arr.get(2).and_then(|v| v.as_u64()).unwrap_or(1);
|
||
|
|
|
||
|
|
for _ in 0..repeat {
|
||
|
|
if let Some(ch) = text.chars().next() {
|
||
|
|
grid[row][c] = ch;
|
||
|
|
}
|
||
|
|
c += 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn handle_cursor(&self, items: &[Value]) {
|
||
|
|
let row = items[2].as_u64().unwrap() as usize;
|
||
|
|
let col = items[3].as_u64().unwrap() as usize;
|
||
|
|
*self.cursor.lock().unwrap() = (row, col);
|
||
|
|
}
|
||
|
|
|
||
|
|
pub async fn spawn_nvim<H: Handler + Send + 'static>(
|
||
|
|
handler: H,
|
||
|
|
) -> Result<Neovim<Compat<tokio::process::ChildStdin>>> {
|
||
|
|
let mut child = Command::new("nvim")
|
||
|
|
.arg("--embed")
|
||
|
|
.arg("--headless")
|
||
|
|
.stdin(Stdio::piped())
|
||
|
|
.stdout(Stdio::piped())
|
||
|
|
.spawn()?;
|
||
|
|
|
||
|
|
let stdin = child.stdin.take().unwrap();
|
||
|
|
let mut stdout = child.stdout.take().unwrap();
|
||
|
|
|
||
|
|
let (nvim, io_handler) = Neovim::new(stdout, stdin, handler);
|
||
|
|
|
||
|
|
tokio::spawn(async move {
|
||
|
|
nvim_rs::compat::tokio::spawn(stdout.compat(), io_handler).await;
|
||
|
|
});
|
||
|
|
|
||
|
|
Ok(nvim)
|
||
|
|
}
|
||
|
|
|
||
|
|
pub async fn init_ui(
|
||
|
|
nvim: &Neovim<nvim_rs::compat::tokio::Compat<tokio::process::ChildStdin>>,
|
||
|
|
w: i64,
|
||
|
|
h: i64,
|
||
|
|
) -> anyhow::Result<()> {
|
||
|
|
let mut opts = UiAttachOptions::default();
|
||
|
|
opts.set_rgb(true).set_linegrid_external(true);
|
||
|
|
|
||
|
|
nvim.ui_attach(w, h, &opts).await?;
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|