From 1b747491bc13ba2f9d30e65a55819e1d65856d43 Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 9 Jul 2023 03:44:39 +0200 Subject: [PATCH] feat: updated nvim and vscode to new controller api --- client/nvim/src/main.rs | 84 ++++++++++++++++++---------------- client/vscode/src/extension.js | 22 ++++----- client/vscode/src/lib.rs | 36 ++++++++------- 3 files changed, 73 insertions(+), 69 deletions(-) diff --git a/client/nvim/src/main.rs b/client/nvim/src/main.rs index 10ac735..a585225 100644 --- a/client/nvim/src/main.rs +++ b/client/nvim/src/main.rs @@ -1,9 +1,10 @@ use std::sync::Arc; use std::{net::TcpStream, sync::Mutex, collections::BTreeMap}; -use codemp::cursor::{CursorSubscriber, CursorControllerHandle}; -use codemp::operation::{OperationController, OperationFactory, OperationProcessor}; use codemp::client::CodempClient; +use codemp::controller::buffer::{OperationControllerHandle, OperationControllerSubscriber}; +use codemp::controller::cursor::{CursorControllerHandle, CursorSubscriber}; +use codemp::factory::OperationFactory; use codemp::proto::buffer_client::BufferClient; use codemp::tokio; @@ -16,8 +17,8 @@ use tracing::{error, warn, debug, info}; #[derive(Clone)] struct NeovimHandler { client: CodempClient, - factories: Arc>>>, - cursors: Arc>>>, + factories: Arc>>, + cursors: Arc>>, } fn nullable_optional_str(args: &[Value], index: usize) -> Option { @@ -37,11 +38,11 @@ fn default_zero_number(args: &[Value], index: usize) -> i64 { } impl NeovimHandler { - fn buffer_controller(&self, path: &String) -> Option> { + fn buffer_controller(&self, path: &String) -> Option { Some(self.factories.lock().unwrap().get(path)?.clone()) } - fn cursor_controller(&self, path: &String) -> Option> { + fn cursor_controller(&self, path: &String) -> Option { Some(self.cursors.lock().unwrap().get(path)?.clone()) } } @@ -89,11 +90,9 @@ impl Handler for NeovimHandler { match self.buffer_controller(&path) { None => Err(Value::from("no controller for given path")), Some(controller) => { - match controller.apply(controller.insert(&txt, pos as u64)) { - Err(e) => Err(Value::from(format!("could not send insert: {}", e))), - Ok(_res) => Ok(Value::Nil), - } - } + controller.apply(controller.insert(&txt, pos as u64)).await; + Ok(Value::Nil) + }, } }, @@ -107,9 +106,9 @@ impl Handler for NeovimHandler { match self.buffer_controller(&path) { None => Err(Value::from("no controller for given path")), - Some(controller) => match controller.apply(controller.delete(pos, count)) { - Err(e) => Err(Value::from(format!("could not send delete: {}", e))), - Ok(_res) => Ok(Value::Nil), + Some(controller) => { + controller.apply(controller.delete(pos, count)).await; + Ok(Value::Nil) } } }, @@ -123,9 +122,11 @@ impl Handler for NeovimHandler { match self.buffer_controller(&path) { None => Err(Value::from("no controller for given path")), - Some(controller) => match controller.apply(controller.replace(&txt)) { - Err(e) => Err(Value::from(format!("could not send replace: {}", e))), - Ok(_res) => Ok(Value::Nil), + Some(controller) => { + if let Some(op) = controller.replace(&txt) { + controller.apply(op).await; + } + Ok(Value::Nil) } } }, @@ -145,17 +146,15 @@ impl Handler for NeovimHandler { match c.attach(path.clone()).await { Err(e) => Err(Value::from(format!("could not attach to stream: {}", e))), Ok(controller) => { - let _controller = controller.clone(); + let mut _controller = controller.clone(); let lines : Vec = _controller.content().split('\n').map(|x| x.to_string()).collect(); match buffer.set_lines(0, -1, false, lines).await { Err(e) => Err(Value::from(format!("could not sync buffer: {}", e))), Ok(()) => { tokio::spawn(async move { - loop { - if !_controller.run() { break debug!("buffer updater clean exit") } - let _span = _controller.wait().await; - // TODO only change lines affected! + while let Some(_change) = _controller.poll().await { let lines : Vec = _controller.content().split('\n').map(|x| x.to_string()).collect(); + // TODO only change lines affected! if let Err(e) = buffer.set_lines(0, -1, false, lines).await { error!("could not update buffer: {}", e); } @@ -170,14 +169,15 @@ impl Handler for NeovimHandler { }, "detach" => { - if args.is_empty() { - return Err(Value::from("no path given")); - } - let path = default_empty_str(&args, 0); - match self.buffer_controller(&path) { - None => Err(Value::from("no controller for given path")), - Some(controller) => Ok(Value::from(controller.stop())), - } + Err(Value::String("not implemented".into())) + // if args.is_empty() { + // return Err(Value::from("no path given")); + // } + // let path = default_empty_str(&args, 0); + // match self.buffer_controller(&path) { + // None => Err(Value::from("no controller for given path")), + // Some(controller) => Ok(Value::from(controller.stop())), + // } }, "listen" => { @@ -185,10 +185,6 @@ impl Handler for NeovimHandler { return Err(Value::from("no path given")); } let path = default_empty_str(&args, 0); - let controller = match self.buffer_controller(&path) { - None => return Err(Value::from("no controller for given path")), - Some(c) => c, - }; let ns = nvim.create_namespace("Cursor").await .map_err(|e| Value::from(format!("could not create namespace: {}", e)))?; @@ -200,19 +196,25 @@ impl Handler for NeovimHandler { match c.listen().await { Err(e) => Err(Value::from(format!("could not listen cursors: {}", e))), Ok(mut cursor) => { - self.cursors.lock().unwrap().insert(path, cursor.clone().into()); + self.cursors.lock().unwrap().insert(path, cursor.clone()); debug!("spawning cursor processing worker"); tokio::spawn(async move { while let Some(cur) = cursor.poll().await { - if !controller.run() { break } if let Err(e) = buf.clear_namespace(ns, 0, -1).await { error!("could not clear previous cursor highlight: {}", e); } + let start = cur.start(); + let end = cur.end(); + let end_col = if start.row == end.row { + end.col + } else { + 0 // TODO what the fuck + }; if let Err(e) = buf.add_highlight( ns, "ErrorMsg", - (cur.start().row-1) as i64, - cur.start().col as i64, - (cur.start().col+1) as i64 + start.row as i64 - 1, + start.col as i64, + end_col as i64 ).await { error!("could not create highlight for cursor: {}", e); } @@ -233,11 +235,13 @@ impl Handler for NeovimHandler { let path = default_empty_str(&args, 0); let row = default_zero_number(&args, 1) as i32; let col = default_zero_number(&args, 2) as i32; + let row_end = default_zero_number(&args, 3) as i32; + let col_end = default_zero_number(&args, 4) as i32; match self.cursor_controller(&path) { None => Err(Value::from("no path given")), Some(cur) => { - cur.send(&path, (row, col).into(), (0, 0).into()).await; + cur.send(&path, (row, col).into(), (row_end, col_end).into()).await; Ok(Value::Nil) } } diff --git a/client/vscode/src/extension.js b/client/vscode/src/extension.js index 9be1764..93be77b 100644 --- a/client/vscode/src/extension.js +++ b/client/vscode/src/extension.js @@ -92,36 +92,34 @@ async function _attach(path) { DECORATION = null } const range_start = new vscode.Position(start[0] - 1, start[1]); - const range_end = new vscode.Position(start[0] - 1, start[1] + 1); + const range_end = new vscode.Position(end[0] - 1, end[1]); const decorationRange = new vscode.Range(range_start, range_end); DECORATION = vscode.window.createTextEditorDecorationType( {backgroundColor: 'red', color: 'white'} ) editor.setDecorations(DECORATION, [decorationRange]) } catch (err) { - vscode.window.showErrorMessage("fuck! " + err) + vscode.window.showErrorMessage("error setting cursor decoration: " + err) } }) vscode.window.onDidChangeTextEditorSelection(async (e) => { let buf = e.textEditor.document.uri.toString() let selection = e.selections[0] // TODO there may be more than one cursor!! let anchor = [selection.anchor.line+1, selection.anchor.character] - let position = [selection.active.line+1, selection.active.character] + let position = [selection.active.line+1, selection.active.character+1] // (anchor, position) = _order_tuples(anchor, position) await CURSOR.send(buf, anchor, position) }) CONTROLLER = await CLIENT.attach(path) - CONTROLLER.callback((start, end) => { - // TODO only change affected document range - let content = CONTROLLER.content() - let range = new vscode.Range( - editor.document.positionAt(0), - editor.document.positionAt(editor.document.getText().length) - ) + CONTROLLER.callback((start, end, text) => { try { - OP_CACHE.add((range, content)) - editor.edit(editBuilder => editBuilder.replace(range, content)) + let range = new vscode.Range( + editor.document.positionAt(start), + editor.document.positionAt(end) + ) + OP_CACHE.add((range, text)) + editor.edit(editBuilder => editBuilder.replace(range, text)) } catch (err) { vscode.window.showErrorMessage("could not set buffer: " + err) } diff --git a/client/vscode/src/lib.rs b/client/vscode/src/lib.rs index 4993fe5..7ad69ef 100644 --- a/client/vscode/src/lib.rs +++ b/client/vscode/src/lib.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use neon::prelude::*; use once_cell::sync::OnceCell; use codemp::{ - cursor::{CursorControllerHandle, CursorSubscriber}, client::CodempClient, operation::{OperationController, OperationFactory, OperationProcessor}, - proto::buffer_client::BufferClient, + controller::{cursor::{CursorSubscriber, CursorControllerHandle}, buffer::{OperationControllerHandle, OperationControllerSubscriber}}, + client::CodempClient, + proto::buffer_client::BufferClient, factory::OperationFactory, }; use codemp::tokio::{runtime::Runtime, sync::Mutex}; @@ -126,7 +127,7 @@ fn attach_client(mut cx: FunctionContext) -> JsResult { Ok(controller) => { deferred.settle_with(&channel, move |mut cx| { let obj = cx.empty_object(); - let boxed_value = cx.boxed(OperationControllerHandle(controller)); + let boxed_value = cx.boxed(OperationControllerJs(controller)); obj.set(&mut cx, "boxed", boxed_value)?; let apply_method = JsFunction::new(&mut cx, apply_operation)?; obj.set(&mut cx, "apply", apply_method)?; @@ -145,12 +146,12 @@ fn attach_client(mut cx: FunctionContext) -> JsResult { } -struct OperationControllerHandle(Arc); -impl Finalize for OperationControllerHandle {} +struct OperationControllerJs(OperationControllerHandle); +impl Finalize for OperationControllerJs {} fn apply_operation(mut cx: FunctionContext) -> JsResult { let this = cx.this(); - let boxed : Handle> = this.get(&mut cx, "boxed")?; + let boxed : Handle> = this.get(&mut cx, "boxed")?; let skip = cx.argument::(0)?.value(&mut cx).round() as usize; let text = cx.argument::(1)?.value(&mut cx); let tail = cx.argument::(2)?.value(&mut cx).round() as usize; @@ -160,10 +161,11 @@ fn apply_operation(mut cx: FunctionContext) -> JsResult { let channel = cx.channel(); runtime(&mut cx)?.spawn(async move { - let op = rc.delta(skip, text.as_str(), tail); - match rc.apply(op) { - Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<_, Handle>(format!("could not apply operation: {}", e))), - Ok(span) => deferred.settle_with(&channel, move |mut cx| tuple(&mut cx, span.start as i32, span.end as i32)), + if let Some(op) = rc.delta(skip, text.as_str(), tail) { + rc.apply(op).await; + deferred.settle_with(&channel, move |mut cx| Ok(cx.boolean(true))); + } else { + deferred.settle_with(&channel, move |mut cx| Ok(cx.undefined())); } }); @@ -172,28 +174,28 @@ fn apply_operation(mut cx: FunctionContext) -> JsResult { fn content_operation(mut cx: FunctionContext) -> JsResult { let this = cx.this(); - let boxed : Handle> = this.get(&mut cx, "boxed")?; + let boxed : Handle> = this.get(&mut cx, "boxed")?; Ok(cx.string(boxed.0.content())) } fn callback_operation(mut cx: FunctionContext) -> JsResult { let this = cx.this(); - let boxed : Handle> = this.get(&mut cx, "boxed")?; + let boxed : Handle> = this.get(&mut cx, "boxed")?; let callback = Arc::new(cx.argument::(0)?.root(&mut cx)); - let rc = boxed.0.clone(); + let mut rc = boxed.0.clone(); let channel = cx.channel(); // TODO when garbage collecting OperationController stop this worker runtime(&mut cx)?.spawn(async move { - loop{ - let span = rc.wait().await; + while let Some(edit) = rc.poll().await { let cb = callback.clone(); channel.send(move |mut cx| { cb.to_inner(&mut cx) .call_with(&cx) - .arg(cx.number(span.start as i32)) - .arg(cx.number(span.end as i32)) + .arg(cx.number(edit.span.start as i32)) + .arg(cx.number(edit.span.end as i32)) + .arg(cx.string(edit.content)) .apply::(&mut cx)?; Ok(()) });