mirror of
https://github.com/hexedtech/codemp-nvim.git
synced 2024-11-22 23:44:55 +01:00
feat: mostly implemented but it bugs out
if attached/joined vim will freeze on exit, must be killed text sync is janky and will fail due to wrong lengths cursor sync seems broken
This commit is contained in:
parent
9b83666a36
commit
242cccd93c
1 changed files with 80 additions and 23 deletions
101
src/lib.rs
101
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 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::{prelude::*, Controller, tokio::sync::mpsc, errors::IgnorableError, proto::{RowCol, CursorEvent}, buffer::factory::OperationFactory};
|
||||||
use codemp::instance::RUNTIME;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CursorStorage {
|
struct CursorStorage {
|
||||||
|
@ -52,6 +51,10 @@ fn multiline_set_text(buf: &mut Buffer, change: CodempTextChange) -> oxi::Result
|
||||||
Ok(())
|
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<CodempCursorPosition> {
|
fn cursor_position() -> oxi::Result<CodempCursorPosition> {
|
||||||
let buf = oxi::api::get_current_buf().get_name()?;
|
let buf = oxi::api::get_current_buf().get_name()?;
|
||||||
let cur = oxi::api::get_current_win().get_cursor()?;
|
let cur = oxi::api::get_current_win().get_cursor()?;
|
||||||
|
@ -64,13 +67,17 @@ fn cursor_position() -> oxi::Result<CodempCursorPosition> {
|
||||||
|
|
||||||
fn buffer_content(buf: &Buffer) -> oxi::Result<String> {
|
fn buffer_content(buf: &Buffer) -> oxi::Result<String> {
|
||||||
let mut out = String::new();
|
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_str(&line.to_string_lossy());
|
||||||
out.push('\n');
|
out.push('\n');
|
||||||
}
|
}
|
||||||
Ok(out.trim().to_string())
|
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 {
|
impl CursorStorage {
|
||||||
pub fn update(&mut self, user: &str, pos: Option<CodempCursorPosition>) -> oxi::Result<()> {
|
pub fn update(&mut self, user: &str, pos: Option<CodempCursorPosition>) -> oxi::Result<()> {
|
||||||
let mut buf = oxi::api::get_current_buf();
|
let mut buf = oxi::api::get_current_buf();
|
||||||
|
@ -97,8 +104,8 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
|args: CommandArgs| {
|
|args: CommandArgs| {
|
||||||
let addr = args.args.unwrap_or("http://127.0.0.1:50051".into());
|
let addr = args.args.unwrap_or("http://127.0.0.1:50051".into());
|
||||||
|
|
||||||
RUNTIME.block_on(CODEMP_INSTANCE.connect(&addr))
|
CODEMP_INSTANCE.connect(&addr)
|
||||||
.map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not connect: {}", e)))?;
|
.map_err(|e| err(e, "xx could not connect: {}"))?;
|
||||||
|
|
||||||
oxi::print!("++ connected to {}", addr);
|
oxi::print!("++ connected to {}", addr);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -114,20 +121,24 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
|args: CommandArgs| {
|
|args: CommandArgs| {
|
||||||
let workspace = args.args.unwrap_or("default".into());
|
let workspace = args.args.unwrap_or("default".into());
|
||||||
|
|
||||||
let controller = RUNTIME.block_on(CODEMP_INSTANCE.join(&workspace))
|
let controller = CODEMP_INSTANCE.join(&workspace)
|
||||||
.map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not join: {}", e)))?;
|
.map_err(|e| err(e, "xx could not join"))?;
|
||||||
|
|
||||||
let (tx, mut rx) = mpsc::unbounded_channel::<CursorEvent>();
|
let (tx, mut rx) = mpsc::unbounded_channel::<CursorEvent>();
|
||||||
let mut container = CursorStorage::default();
|
let mut container = CursorStorage::default();
|
||||||
|
|
||||||
let handle = AsyncHandle::new(move || {
|
let handle = AsyncHandle::new(move || {
|
||||||
while let Ok(x) = rx.try_recv() { // TODO do this inside oxi::schedule() to not block vim
|
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)?;
|
container.update(&x.user, x.position)?;
|
||||||
}
|
}
|
||||||
Ok::<_, oxi::Error>(())
|
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");
|
tx.send(x).unwrap_or_warn("could not enqueue callback");
|
||||||
handle.send().unwrap_or_warn("could not wake async handle");
|
handle.send().unwrap_or_warn("could not wake async handle");
|
||||||
});
|
});
|
||||||
|
@ -139,8 +150,28 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
.group(au)
|
.group(au)
|
||||||
.desc("update cursor position")
|
.desc("update cursor position")
|
||||||
.callback(move |_x| {
|
.callback(move |_x| {
|
||||||
RUNTIME.block_on(controller.send(cursor_position()?))
|
let cur = cursor_position()?;
|
||||||
.map_err(|e| oxi::api::Error::Other(format!("could not send cursor position: {}", e)))?;
|
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::<bool, oxi::Error>(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::<bool, oxi::Error>(true)
|
Ok::<bool, oxi::Error>(true)
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
|
@ -160,22 +191,31 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
|args: CommandArgs| {
|
|args: CommandArgs| {
|
||||||
let buffer = args.args.expect("one arg required but not provided");
|
let buffer = args.args.expect("one arg required but not provided");
|
||||||
|
|
||||||
let controller = RUNTIME.block_on(CODEMP_INSTANCE.attach(&buffer))
|
let controller = CODEMP_INSTANCE.attach(&buffer)
|
||||||
.map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not attach: {}", e)))?;
|
.map_err(|e| err(e, "xx could not attach"))?;
|
||||||
|
|
||||||
let buf = oxi::api::get_current_buf();
|
let buf = oxi::api::get_current_buf();
|
||||||
let mut buf_m = buf.clone();
|
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::<CodempTextChange>();
|
let (tx, mut rx) = mpsc::unbounded_channel::<CodempTextChange>();
|
||||||
// let mut container = CursorStorage::default();
|
|
||||||
|
|
||||||
let handle = AsyncHandle::new(move || {
|
let handle = AsyncHandle::new(move || {
|
||||||
while let Ok(change) = rx.try_recv() { // TODO do this inside oxi::schedule() to not block vim
|
while let Ok(_change) = rx.try_recv() { // TODO do this inside oxi::schedule() to not block vim
|
||||||
multiline_set_text(&mut buf_m, change)?;
|
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>(())
|
Ok::<_, oxi::Error>(())
|
||||||
}).map_err(|e| oxi::api::Error::Other(format!("xx could not create handle: {}", e)))?;
|
}).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");
|
tx.send(x).unwrap_or_warn("could not enqueue callback");
|
||||||
handle.send().unwrap_or_warn("could not wake async handle");
|
handle.send().unwrap_or_warn("could not wake async handle");
|
||||||
});
|
});
|
||||||
|
@ -187,9 +227,14 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
.group(au)
|
.group(au)
|
||||||
.desc("update cursor position")
|
.desc("update cursor position")
|
||||||
.callback(move |_x| {
|
.callback(move |_x| {
|
||||||
if let Some(op) = controller.replace(&buffer_content(&buf)?) {
|
let content = buffer_content(&buf)?;
|
||||||
RUNTIME.block_on(controller.send(op))
|
tracing::info!("running buffer callback -- {}", content);
|
||||||
.map_err(|e| oxi::api::Error::Other(format!("could not send cursor position: {}", e)))?;
|
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::<bool, oxi::Error>(true)
|
Ok::<bool, oxi::Error>(true)
|
||||||
} else {
|
} else {
|
||||||
Ok::<bool, oxi::Error>(false)
|
Ok::<bool, oxi::Error>(false)
|
||||||
|
@ -199,6 +244,18 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
.build()
|
.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::<bool, oxi::Error>(true)
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)?;
|
||||||
|
|
||||||
oxi::print!("++ attached to buffer '{}'", buffer);
|
oxi::print!("++ attached to buffer '{}'", buffer);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -213,7 +270,7 @@ fn codemp_nvim() -> oxi::Result<()> {
|
||||||
|args: CommandArgs| {
|
|args: CommandArgs| {
|
||||||
let path = args.args.expect("one arg required but not provided");
|
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)))?;
|
.map_err(|e| nvim_oxi::api::Error::Other(format!("xx could not attach: {}", e)))?;
|
||||||
|
|
||||||
oxi::print!("++ created buffer '{}'", path);
|
oxi::print!("++ created buffer '{}'", path);
|
||||||
|
|
Loading…
Reference in a new issue