[tui] WIP neovim editor.
This commit is contained in:
156
src/tui/editor.rs
Normal file
156
src/tui/editor.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user