diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 073c368..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "codemp-vscode" -version = "0.0.1" -description = "VSCode extension for CodeMP" -edition = "2021" - -[lib] -crate-type = ["cdylib"] -path = "src/rust/lib.rs" - -[dependencies] -codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag="v0.6.0", features = ["client", "woot"] } -tracing = "0.1" -tracing-subscriber = "0.3.17" -uuid = { version = "1.3.1", features = ["v4"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -rmpv = "1" -async-trait = "0.1.68" -napi = { version = "2", features = ["full"] } -napi-derive = "2" -futures = "0.3.28" -tokio = {version = "1.32.0", features = ["full"] } - -[build-dependencies] -napi-build = "2" - -[profile.release] -lto = true diff --git a/build.rs b/build.rs deleted file mode 100644 index f8bfd67..0000000 --- a/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate napi_build; - -fn main() { - napi_build::setup(); -} diff --git a/src/rust/buffer.rs b/src/rust/buffer.rs deleted file mode 100644 index 2254ffc..0000000 --- a/src/rust/buffer.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::sync::Arc; -use napi::threadsafe_function::{ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode}; -use napi_derive::napi; -use codemp::api::Controller; -use crate::JsCodempError; - -/// BUFFER -#[napi(object)] -pub struct JsTextChange { - pub span: JsRange, - pub content: String, -} - -#[napi(object)] -pub struct JsRange{ - pub start: i32, - pub end: i32, -} - -impl From:: for JsTextChange { - fn from(value: codemp::api::TextChange) -> Self { - JsTextChange { - // TODO how is x.. represented ? span.end can never be None - span: JsRange { start: value.span.start as i32, end: value.span.end as i32 }, - content: value.content, - } - } -} - - -impl From::> for JsBufferController { - fn from(value: Arc) -> Self { - JsBufferController(value) - } -} - - -#[napi] -pub struct JsBufferController(Arc); - - -/*#[napi] -pub fn delta(string : String, start: i64, txt: String, end: i64 ) -> Option { - Some(JsCodempOperationSeq(string.diff(start as usize, &txt, end as usize)?)) -}*/ - - - - - - -#[napi] -impl JsBufferController { - - - #[napi(ts_args_type = "fun: (event: JsTextChange) => void")] - pub fn callback(&self, fun: napi::JsFunction) -> napi::Result<()>{ - let tsfn : ThreadsafeFunction = - fun.create_threadsafe_function(0, - |ctx : ThreadSafeCallContext| { - Ok(vec![JsTextChange::from(ctx.value)]) - } - )?; - let _controller = self.0.clone(); - tokio::spawn(async move { - //tokio::time::sleep(std::time::Duration::from_secs(1)).await; - loop { - tokio::time::sleep(std::time::Duration::from_millis(200)).await; - match _controller.recv().await { - Ok(event) => { - tsfn.call(event, ThreadsafeFunctionCallMode::NonBlocking); //check this shit with tracing also we could use Ok(event) to get the error - }, - Err(codemp::Error::Deadlocked) => continue, - Err(e) => break tracing::warn!("error receiving: {}", e), - } - } - }); - Ok(()) - } - - - #[napi] - pub fn content(&self) -> String { - self.0.content() - } - - #[napi] - pub fn get_name(&self) -> String { - self.0.name().to_string() - } - - #[napi] - pub async fn recv(&self) -> napi::Result { - Ok( - self.0.recv().await - .map_err(|e| napi::Error::from(JsCodempError(e)))? - .into() - ) - } - - #[napi] - pub fn send(&self, op: JsTextChange) -> napi::Result<()> { - // TODO might be nice to take ownership of the opseq - let new_text_change = codemp::api::TextChange { - span: op.span.start as usize .. op.span.end as usize, - content: op.content, - }; - Ok(self.0.send(new_text_change).map_err(JsCodempError)?) - } -} \ No newline at end of file diff --git a/src/rust/client.rs b/src/rust/client.rs deleted file mode 100644 index 2394d40..0000000 --- a/src/rust/client.rs +++ /dev/null @@ -1,45 +0,0 @@ -use napi_derive::napi; -use crate::JsCodempError; -use crate::workspace::JsWorkspace; - -#[napi] -/// main codemp client session -pub struct JsCodempClient(tokio::sync::RwLock); - -#[napi] -/// connect to codemp servers and return a client session -pub async fn connect(addr: Option) -> napi::Result{ - let client = codemp::Client::new(addr.as_deref().unwrap_or("http://codemp.alemi.dev:50053")) - .await - .map_err(JsCodempError)?; - - Ok(JsCodempClient(tokio::sync::RwLock::new(client))) -} - -#[napi] -impl JsCodempClient { - #[napi] - /// login against AuthService with provided credentials, optionally requesting access to a workspace - pub async fn login(&self, username: String, password: String, workspace_id: Option) -> napi::Result<()> { - self.0.read().await.login(username, password, workspace_id).await.map_err(JsCodempError)?; - Ok(()) - } - - #[napi] - /// join workspace with given id (will start its cursor controller) - pub async fn join_workspace(&self, workspace: String) -> napi::Result { - Ok(JsWorkspace::from(self.0.write().await.join_workspace(&workspace).await.map_err(JsCodempError)?)) - } - - #[napi] - /// get workspace with given id, if it exists - pub async fn get_workspace(&self, workspace: String) -> Option { - self.0.read().await.get_workspace(&workspace).map(|w| JsWorkspace::from(w)) - } - - #[napi] - /// return current sessions's user id - pub async fn user_id(&self) -> String { - self.0.read().await.user_id().to_string() - } -} \ No newline at end of file diff --git a/src/rust/cursor.rs b/src/rust/cursor.rs deleted file mode 100644 index d311a80..0000000 --- a/src/rust/cursor.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::sync::Arc; -use napi_derive::napi; -use uuid::Uuid; -use napi::threadsafe_function::{ThreadsafeFunction, ThreadSafeCallContext, ThreadsafeFunctionCallMode, ErrorStrategy}; -use codemp::api::Controller; -use crate::JsCodempError; - -#[napi] -pub struct JsCursorController(Arc); - -impl From::> for JsCursorController { - fn from(value: Arc) -> Self { - JsCursorController(value) - } -} - -#[napi] -impl JsCursorController { - - #[napi(ts_args_type = "fun: (event: JsCursorEvent) => void")] - pub fn callback(&self, fun: napi::JsFunction) -> napi::Result<()>{ - let tsfn : ThreadsafeFunction = - fun.create_threadsafe_function(0, - |ctx : ThreadSafeCallContext| { - Ok(vec![JsCursorEvent::from(ctx.value)]) - } - )?; - let _controller = self.0.clone(); - tokio::spawn(async move { - loop { - match _controller.recv().await { - Ok(event) => { - tsfn.call(event.clone(), ThreadsafeFunctionCallMode::NonBlocking); //check this shit with tracing also we could use Ok(event) to get the error - }, - Err(codemp::Error::Deadlocked) => continue, - Err(e) => break tracing::warn!("error receiving: {}", e), - } - } - }); - Ok(()) - } - - #[napi] - pub fn send(&self, buffer: String, start: (i32, i32), end: (i32, i32)) -> napi::Result<()> { - let pos = codemp::proto::cursor::CursorPosition { - buffer: buffer.into(), - start: codemp::proto::cursor::RowCol::from(start), - end: codemp::proto::cursor::RowCol::from(end), - }; - Ok(self.0.send(pos).map_err(JsCodempError)?) - } -} - - - -#[derive(Debug)] -#[napi(object)] -pub struct JsCursorEvent { - pub user: String, - pub buffer: String, - pub start: JsRowCol, - pub end: JsRowCol, -} - -impl From:: for JsCursorEvent { - fn from(value: codemp::proto::cursor::CursorEvent) -> Self { - let pos = value.position; - let start = pos.start; - let end = pos.end; - JsCursorEvent { - user: Uuid::from(value.user).to_string(), - buffer: pos.buffer.into(), - start: JsRowCol { row: start.row, col: start.col }, - end: JsRowCol { row: end.row, col: end.col }, - } - } -} - -#[derive(Debug)] -#[napi(object)] -pub struct JsRowCol { - pub row: i32, - pub col: i32 -} - -impl From:: for JsRowCol { - fn from(value: codemp::proto::cursor::RowCol) -> Self { - JsRowCol { row: value.row, col: value.col } - } -} \ No newline at end of file diff --git a/src/rust/lib.rs b/src/rust/lib.rs deleted file mode 100644 index 97875bf..0000000 --- a/src/rust/lib.rs +++ /dev/null @@ -1,68 +0,0 @@ -#![deny(clippy::all)] - -pub mod client; -pub mod workspace; -pub mod cursor; -pub mod buffer; -pub mod op_cache; - -#[derive(Debug)] -struct JsCodempError(codemp::Error); - -impl From:: for napi::Error { - fn from(value: JsCodempError) -> Self { - napi::Error::new(napi::Status::GenericFailure, &format!("CodempError: {:?}", value)) - } -} - -use napi_derive::napi; - -#[napi] -pub struct JsLogger(std::sync::Arc>>); - -#[napi] -impl JsLogger { - #[napi(constructor)] - pub fn new(debug: Option) -> JsLogger { - let (tx, rx) = tokio::sync::mpsc::channel(256); - let level = if debug.unwrap_or(false) { tracing::Level::DEBUG } else {tracing::Level::INFO }; //TODO: study this tracing subscriber and customize it - let format = tracing_subscriber::fmt::format() - .with_level(true) - .with_target(true) - .with_thread_ids(false) - .with_thread_names(false) - .with_ansi(false) - .with_file(false) - .with_line_number(false) - .with_source_location(false) - .compact(); - tracing_subscriber::fmt() - .event_format(format) - .with_max_level(level) - .with_writer(std::sync::Mutex::new(JsLoggerProducer(tx))) - .init(); - JsLogger(std::sync::Arc::new(tokio::sync::Mutex::new(rx))) - } - - #[napi] - pub async fn message(&self) -> Option { - self.0 - .lock() - .await - .recv() - .await - } -} - -#[derive(Debug, Clone)] -struct JsLoggerProducer(tokio::sync::mpsc::Sender); - -impl std::io::Write for JsLoggerProducer { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - // TODO this is a LOSSY logger!! - let _ = self.0.try_send(String::from_utf8_lossy(buf).to_string()); // ignore: logger disconnected or with full buffer - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { Ok(()) } -} \ No newline at end of file diff --git a/src/rust/op_cache.rs b/src/rust/op_cache.rs deleted file mode 100644 index 67ca47c..0000000 --- a/src/rust/op_cache.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::collections::HashMap; -use napi_derive::napi; - -pub type OpTuple = (String, u32, String, u32); // buf_path, start, text, end - -#[napi] -pub struct OpCache { - store: HashMap -} - -#[napi] -impl OpCache { - #[napi(constructor)] - pub fn new() -> Self { - OpCache { - store: HashMap::new() - } - } - - #[napi] - pub fn to_string(&self) -> String { - self.store.iter() - .map(|(k, v)| format!("{}x Op(@{} {}:{} '{}')", k.0, v, k.1, k.3, k.2)) - .collect::>() - .join(", ") - } - - #[napi] - pub fn put(&mut self, buf: String, start: u32, text: String, end: u32) -> i32 { - let op = (buf, start, text, end); - match self.store.get_mut(&op) { - Some(val) => { - if *val < 0 { *val = 0 } - *val += 1; - *val - }, - None => { - self.store.insert(op, 1); - return 1; - } - } - } - - #[napi] - pub fn get(&mut self, buf: String, start: u32, text: String, end: u32) -> bool { - let op = (buf, start, text, end); - match self.store.get_mut(&op) { - Some(val) => { - *val -= 1; - *val >= 0 - } - None => { - tracing::warn!("never seen this op: {:?}", op); - self.store.insert(op, -1); - false - }, - } - } -} -//a -//consume a -//a - - - - -#[cfg(test)] -mod test { - #[test] - fn opcache_put_increments_internal_counter() { - let mut op = super::OpCache::new(); - assert_eq!(op.put("default".into(), 0, "hello world".into(), 0), 1); // 1: did not already contain it - assert_eq!(op.put("default".into(), 0, "hello world".into(), 0), 2); // 2: already contained it - } - #[test] - fn op_cache_get_checks_count() { - let mut op = super::OpCache::new(); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), false); - assert_eq!(op.put("default".into(), 0, "hello world".into(), 0), 1); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), true); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), false); - } - #[test] - fn op_cache_get_works_for_multiple_puts() { - let mut op = super::OpCache::new(); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), false); - assert_eq!(op.put("default".into(), 0, "hello world".into(), 0), 1); - assert_eq!(op.put("default".into(), 0, "hello world".into(), 0), 2); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), true); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), true); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), false); - } - - #[test] - fn op_cache_different_keys(){ - let mut op = super::OpCache::new(); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), false); - assert_eq!(op.put("default".into(), 0, "hello world".into(), 0), 1); - assert_eq!(op.get("workspace".into(), 0, "hi".into(), 0), false); - assert_eq!(op.put("workspace".into(), 0, "hi".into(), 0), 1); - assert_eq!(op.get("workspace".into(), 0, "hi".into(), 0), true); - assert_eq!(op.get("default".into(), 0, "hello world".into(), 0), true); - } -} \ No newline at end of file diff --git a/src/rust/workspace.rs b/src/rust/workspace.rs deleted file mode 100644 index ae77449..0000000 --- a/src/rust/workspace.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::sync::Arc; - -use napi_derive::napi; - -use crate::{JsCodempError, buffer::JsBufferController, cursor::JsCursorController}; - - -#[napi] -/// a reference to a codemp workspace -pub struct JsWorkspace(Arc); - -impl From> for JsWorkspace { - fn from(value: Arc) -> Self { - JsWorkspace(value) - } -} - -#[napi] -impl JsWorkspace { - - #[napi] - pub fn id(&self) -> String { - self.0.id() - } - - #[napi] - pub fn filetree(&self) -> Vec { - self.0.filetree() - } - - #[napi] - pub fn cursor(&self) -> JsCursorController { - JsCursorController::from(self.0.cursor()) - } - - #[napi] - pub fn buffer_by_name(&self, path: String) -> Option { - self.0.buffer_by_name(&path).map(|b| JsBufferController::from(b)) - } - - #[napi] - pub async fn create(&self, path: String) -> napi::Result<()> { - Ok(self.0.create(&path).await.map_err(JsCodempError)?) - } - - #[napi] - pub async fn attach(&self, path: String) -> napi::Result { - Ok(JsBufferController::from(self.0.attach(&path).await.map_err(JsCodempError)?)) - } - - /*#[napi] - pub async fn delete(&self, path: String) -> napi::Result<>{ - self.0.delete(&path) - }*/ - -} \ No newline at end of file