diff --git a/Cargo.lock b/Cargo.lock index 86a6948..3ac7f69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,17 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "atomic" version = "0.6.1" @@ -133,6 +144,28 @@ version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + [[package]] name = "castaway" version = "0.2.4" @@ -227,9 +260,11 @@ dependencies = [ "cxx-qt-lib", "dirs", "log", + "nvim-rs", "ratatui", "structopt", "syntect", + "tokio", "tui-tree-widget", "uuid", ] @@ -595,7 +630,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -626,7 +661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -699,6 +734,103 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures 0.1.31", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", + "tokio-io", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -811,6 +943,15 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1010,7 +1151,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1071,6 +1212,21 @@ dependencies = [ "libc", ] +[[package]] +name = "nvim-rs" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010294fd782a554d4b9b17608305f7498809e857a6ed7a3e858588ad8e5691f5" +dependencies = [ + "async-trait", + "futures 0.3.31", + "log", + "rmp", + "rmpv", + "tokio", + "tokio-util", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1232,6 +1388,18 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.32" @@ -1480,6 +1648,24 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmpv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417" +dependencies = [ + "rmp", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -1499,7 +1685,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1644,12 +1830,28 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1910,6 +2112,59 @@ dependencies = [ "time-core", ] +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes 1.11.0", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes 1.11.0", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tui-tree-widget" version = "0.24.0" @@ -2181,7 +2436,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -2196,6 +2451,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -2205,6 +2469,71 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index 0f88e17..f50d4c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ ratatui = "0.30.0" anyhow = "1.0.100" tui-tree-widget = "0.24.0" uuid = { version = "1.19.0", features = ["v4"] } +nvim-rs = { version = "0.9.2", features = ["use_tokio"] } +tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros", "process"] } [build-dependencies] # The link_qt_object_files feature is required for statically linking Qt 6. diff --git a/src/tui.rs b/src/tui.rs index 32a106e..cfafaf9 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,6 +1,7 @@ pub mod app; mod component; mod explorer; +mod editor; use anyhow::{Context, Result}; diff --git a/src/tui/editor.rs b/src/tui/editor.rs new file mode 100644 index 0000000..66939d3 --- /dev/null +++ b/src/tui/editor.rs @@ -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, + 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 = grid + .iter() + .map(|row| Line::from(row.iter().collect::())) + .collect(); + + Paragraph::new(lines).render(area, buf); + } +} + +impl ClideComponent for Editor {} + +#[derive(Default, Clone)] +pub struct NvimUI { + pub grid: Arc>>>, + pub cursor: Arc>, +} + +impl Handler for NvimUI { + type Writer = Compat; + + async fn handle_notify( + &self, + _name: String, + _args: Vec, + _neovim: Neovim>, + ) -> 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( + handler: H, + ) -> Result>> { + 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>, + 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(()) + } +}