diff --git a/src/lib.rs b/src/lib.rs index 3519523..246ff58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,9 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, fs::File, time::{SystemTime, UNIX_EPOCH}}; use nvim_oxi as oxi; -use oxi::{api::{opts::{CreateCommandOpts, CreateAugroupOpts, CreateAutocmdOpts, GetTextOpts}, types::{CommandNArgs, CommandArgs}, Buffer}, libuv::AsyncHandle}; +use oxi::{api::{opts::{CreateCommandOpts, CreateAugroupOpts, CreateAutocmdOpts}, types::{CommandNArgs, CommandArgs}, Buffer}, libuv::AsyncHandle}; use codemp::{prelude::*, Controller, tokio::sync::mpsc, errors::IgnorableError, proto::{RowCol, CursorEvent}, buffer::factory::OperationFactory}; -use codemp::instance::RUNTIME; #[derive(Default)] struct CursorStorage { @@ -52,6 +51,10 @@ fn multiline_set_text(buf: &mut Buffer, change: CodempTextChange) -> oxi::Result Ok(()) } +fn err(e: impl std::error::Error, msg: &str) -> oxi::api::Error { + oxi::api::Error::Other(format!("{} -- {}", msg, e)) +} + fn cursor_position() -> oxi::Result { let buf = oxi::api::get_current_buf().get_name()?; let cur = oxi::api::get_current_win().get_cursor()?; @@ -64,13 +67,17 @@ fn cursor_position() -> oxi::Result { fn buffer_content(buf: &Buffer) -> oxi::Result { let mut out = String::new(); - for line in buf.get_text(0.., 0, 0, &GetTextOpts::default())? { + for line in buf.get_lines(0.., false)? { out.push_str(&line.to_string_lossy()); out.push('\n'); } Ok(out.trim().to_string()) } +fn buffer_set(buf: &mut Buffer, content: &str) -> Result<(), oxi::api::Error> { + buf.set_lines(0.., false, content.split('\n')) +} + impl CursorStorage { pub fn update(&mut self, user: &str, pos: Option) -> oxi::Result<()> { let mut buf = oxi::api::get_current_buf(); @@ -97,8 +104,8 @@ fn codemp_nvim() -> oxi::Result<()> { |args: CommandArgs| { let addr = args.args.unwrap_or("http://127.0.0.1:50051".into()); - RUNTIME.block_on(CODEMP_INSTANCE.connect(&addr)) - .map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not connect: {}", e)))?; + CODEMP_INSTANCE.connect(&addr) + .map_err(|e| err(e, "xx could not connect: {}"))?; oxi::print!("++ connected to {}", addr); Ok(()) @@ -114,33 +121,57 @@ fn codemp_nvim() -> oxi::Result<()> { |args: CommandArgs| { let workspace = args.args.unwrap_or("default".into()); - let controller = RUNTIME.block_on(CODEMP_INSTANCE.join(&workspace)) - .map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not join: {}", e)))?; + let controller = CODEMP_INSTANCE.join(&workspace) + .map_err(|e| err(e, "xx could not join"))?; let (tx, mut rx) = mpsc::unbounded_channel::(); let mut container = CursorStorage::default(); let handle = AsyncHandle::new(move || { while let Ok(x) = rx.try_recv() { // TODO do this inside oxi::schedule() to not block vim + tracing::info!("cursor move: {:?}", x); + oxi::print!("cursor>> {:?}", x); container.update(&x.user, x.position)?; } Ok::<_, oxi::Error>(()) - }).map_err(|e| oxi::api::Error::Other(format!("xx could not create handle: {}", e)))?; + }).map_err(|e| err(e, "xx could not create handle"))?; - controller.clone().callback(&RUNTIME, move |x| { + let (stop, stop_rx) = mpsc::unbounded_channel(); + + controller.clone().callback(CODEMP_INSTANCE.rt(), stop_rx, move |x| { tx.send(x).unwrap_or_warn("could not enqueue callback"); handle.send().unwrap_or_warn("could not wake async handle"); }); let au = oxi::api::create_augroup(&workspace, &CreateAugroupOpts::builder().clear(true).build())?; oxi::api::create_autocmd( - [ "CursorMovedI", "CursorMoved", "CompleteDone", "InsertEnter", "InsertLeave" ], + [ "CursorMovedI", "CursorMoved", "CompleteDone", "InsertEnter", "InsertLeave" ], &CreateAutocmdOpts::builder() .group(au) .desc("update cursor position") .callback(move |_x| { - RUNTIME.block_on(controller.send(cursor_position()?)) - .map_err(|e| oxi::api::Error::Other(format!("could not send cursor position: {}", e)))?; + let cur = cursor_position()?; + tracing::info!("running cursor callback: {:?}", cur); + let _c = controller.clone(); + CODEMP_INSTANCE.rt().spawn(async move { + _c.send(cur).await.unwrap_or_warn("could not enqueue cursor update"); + }); + Ok::(true) + }) + .build() + )?; + + oxi::api::create_autocmd( + [ "ExitPre" ], + &CreateAutocmdOpts::builder() + .group(au) + .desc("remove cursor callbacks") + .callback(move |_x| { + oxi::print!("stopping cursor worker"); + stop.send(()).unwrap_or_warn("could not stop cursor callback worker"); + CODEMP_INSTANCE.leave_workspace().unwrap_or_warn("could not leave workspace"); + tracing::info!("left workspace"); + oxi::print!("stopped cursor worker and leaving workspace"); Ok::(true) }) .build() @@ -160,22 +191,31 @@ fn codemp_nvim() -> oxi::Result<()> { |args: CommandArgs| { let buffer = args.args.expect("one arg required but not provided"); - let controller = RUNTIME.block_on(CODEMP_INSTANCE.attach(&buffer)) - .map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not attach: {}", e)))?; + let controller = CODEMP_INSTANCE.attach(&buffer) + .map_err(|e| err(e, "xx could not attach"))?; let buf = oxi::api::get_current_buf(); let mut buf_m = buf.clone(); + + buffer_set(&mut buf_m, &controller.content())?; + + let controller_m = controller.clone(); let (tx, mut rx) = mpsc::unbounded_channel::(); - // let mut container = CursorStorage::default(); let handle = AsyncHandle::new(move || { - while let Ok(change) = rx.try_recv() { // TODO do this inside oxi::schedule() to not block vim - multiline_set_text(&mut buf_m, change)?; + while let Ok(_change) = rx.try_recv() { // TODO do this inside oxi::schedule() to not block vim + tracing::info!("buf change: {:?}", _change); + oxi::print!("change>> {:?}", _change); + // multiline_set_text(&mut buf_m, change)?; + buffer_set(&mut buf_m, &controller_m.content())?; } + oxi::api::exec("redraw!", false)?; Ok::<_, oxi::Error>(()) }).map_err(|e| oxi::api::Error::Other(format!("xx could not create handle: {}", e)))?; - controller.clone().callback(&RUNTIME, move |x| { + let (stop, stop_rx) = mpsc::unbounded_channel(); + + controller.clone().callback(CODEMP_INSTANCE.rt(), stop_rx, move |x| { tx.send(x).unwrap_or_warn("could not enqueue callback"); handle.send().unwrap_or_warn("could not wake async handle"); }); @@ -187,9 +227,14 @@ fn codemp_nvim() -> oxi::Result<()> { .group(au) .desc("update cursor position") .callback(move |_x| { - if let Some(op) = controller.replace(&buffer_content(&buf)?) { - RUNTIME.block_on(controller.send(op)) - .map_err(|e| oxi::api::Error::Other(format!("could not send cursor position: {}", e)))?; + let content = buffer_content(&buf)?; + tracing::info!("running buffer callback -- {}", content); + if let Some(op) = controller.replace(&content) { + tracing::info!("it's diferent!"); + let _c = controller.clone(); + CODEMP_INSTANCE.rt().spawn(async move { + _c.send(op).await.unwrap_or_warn("could not enqueue text change"); + }); Ok::(true) } else { Ok::(false) @@ -199,6 +244,18 @@ fn codemp_nvim() -> oxi::Result<()> { .build() )?; + oxi::api::create_autocmd( + [ "ExitPre" ], + &CreateAutocmdOpts::builder() + .group(au) + .desc("remove buffer callbacks") + .callback(move |_x| { + stop.send(()).unwrap_or_warn("could not stop cursor callback worker"); + Ok::(true) + }) + .build() + )?; + oxi::print!("++ attached to buffer '{}'", buffer); Ok(()) }, @@ -213,7 +270,7 @@ fn codemp_nvim() -> oxi::Result<()> { |args: CommandArgs| { let path = args.args.expect("one arg required but not provided"); - RUNTIME.block_on(CODEMP_INSTANCE.create(&path, None)) + CODEMP_INSTANCE.create(&path, None) .map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not attach: {}", e)))?; oxi::print!("++ created buffer '{}'", path);