mirror of
https://github.com/hexedtech/codemp-vscode.git
synced 2024-11-24 16:34:48 +01:00
feat: switched from neon to napi
Co-authored-by: alemi <me@alemi.dev>
This commit is contained in:
parent
6681a53da9
commit
7f9422103a
6 changed files with 247 additions and 262 deletions
|
@ -1,2 +1,6 @@
|
|||
[net]
|
||||
git-fetch-with-cli = true
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=-crt-static"]
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -7,3 +7,5 @@ Cargo.lock
|
|||
/client/vscode/*.vsix
|
||||
/client/vscode/codemp.node
|
||||
|
||||
node_modules
|
||||
|
||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -3,20 +3,27 @@ name = "codemp-vscode"
|
|||
version = "0.0.1"
|
||||
description = "VSCode extension for CodeMP"
|
||||
edition = "2021"
|
||||
exclude = ["index.node"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag = "v0.3" }
|
||||
# codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag = "v0.3" }
|
||||
codemp = { path = "../../lib", features = ["global"]}
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
uuid = { version = "1.3.1", features = ["v4"] }
|
||||
once_cell = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
rmpv = "1"
|
||||
clap = { version = "4.2.1", features = ["derive"] }
|
||||
async-trait = "0.1.68"
|
||||
neon = { version = "0.10.1", default-features = false, features = ["channel-api", "napi-6", "promise-api"] }
|
||||
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
|
||||
|
|
5
build.rs
Normal file
5
build.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
110
package.json
110
package.json
|
@ -1,38 +1,96 @@
|
|||
{
|
||||
"name": "@codemp/vscode",
|
||||
"version": "0.2.0",
|
||||
"description": "codemp bindings for vscode plugin",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"napi": {
|
||||
"name": "codemp-vscode",
|
||||
"version": "0.0.1",
|
||||
"description": "VSCode extension for CodeMP",
|
||||
"main": "./out/extension.js",
|
||||
"triples": {
|
||||
"defaults": true,
|
||||
"additional": [
|
||||
"x86_64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"i686-pc-windows-msvc",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-linux-android",
|
||||
"x86_64-unknown-freebsd",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"armv7-linux-androideabi"
|
||||
]
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.32.0"
|
||||
"node": ">= 10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cargo-cp-artifact --artifact cdylib codemp-vscode codemp.node -- cargo build --release --message-format=json-render-diagnostics",
|
||||
"install": "npm run build",
|
||||
"test": "cargo test"
|
||||
"artifacts": "napi artifacts",
|
||||
"bench": "node -r @swc-node/register benchmark/bench.ts",
|
||||
"build": "napi build --platform --release --pipe \"prettier -w\"",
|
||||
"build:debug": "napi build --platform --pipe \"prettier -w\"",
|
||||
"format": "run-p format:prettier format:rs format:toml",
|
||||
"format:prettier": "prettier . -w",
|
||||
"format:toml": "taplo format",
|
||||
"format:rs": "cargo fmt",
|
||||
"lint": "eslint . -c ./.eslintrc.yml",
|
||||
"prepublishOnly": "napi prepublish -t npm",
|
||||
"test": "ava",
|
||||
"version": "napi version"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
"@napi-rs/cli": "^2.14.6",
|
||||
"@swc-node/register": "^1.5.5",
|
||||
"@swc/core": "^1.3.32",
|
||||
"@taplo/cli": "^0.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"ava": "^5.1.1",
|
||||
"benny": "^3.7.1",
|
||||
"chalk": "^5.2.0",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^14.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "codemp.connect",
|
||||
"title": "Connect to CodeMP"
|
||||
"lint-staged": {
|
||||
"*.@(js|ts|tsx)": [
|
||||
"eslint -c .eslintrc.yml --fix"
|
||||
],
|
||||
"*.@(js|ts|tsx|yml|yaml|md|json)": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.toml": [
|
||||
"taplo format"
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": "codemp.join",
|
||||
"title": "Join remote session"
|
||||
},
|
||||
{
|
||||
"command": "codemp.share",
|
||||
"title": "Share local session"
|
||||
"ava": {
|
||||
"require": [
|
||||
"@swc-node/register"
|
||||
],
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"timeout": "2m",
|
||||
"workerThreads": false,
|
||||
"environmentVariables": {
|
||||
"TS_NODE_PROJECT": "./tsconfig.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"activationEvents": [
|
||||
"onCommand:codemp.connect",
|
||||
"onCommand:codemp.join",
|
||||
"onCommand:codemp.share"
|
||||
]
|
||||
"prettier": {
|
||||
"printWidth": 120,
|
||||
"semi": false,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always"
|
||||
},
|
||||
"packageManager": "yarn@3.6.2"
|
||||
}
|
||||
|
|
403
src/lib.rs
403
src/lib.rs
|
@ -1,268 +1,177 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use neon::prelude::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
use futures::prelude::*;
|
||||
use napi::bindgen_prelude::*;
|
||||
use codemp::{
|
||||
controller::cursor::{CursorSubscriber, CursorControllerHandle},
|
||||
controller::buffer::{OperationControllerHandle, OperationControllerSubscriber},
|
||||
client::CodempClient,
|
||||
factory::OperationFactory,
|
||||
proto::buffer_client::BufferClient,
|
||||
prelude::*,
|
||||
proto::{RowCol, CursorEvent},
|
||||
buffer::factory::OperationFactory, ot::OperationSeq
|
||||
};
|
||||
use codemp::tokio::{runtime::Runtime, sync::Mutex};
|
||||
use napi_derive::napi;
|
||||
use napi::tokio::{self, fs};
|
||||
|
||||
fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> {
|
||||
static RUNTIME: OnceCell<Runtime> = OnceCell::new();
|
||||
#[derive(Debug)]
|
||||
struct JsCodempError(CodempError);
|
||||
|
||||
RUNTIME.get_or_try_init(|| {
|
||||
Runtime::new()
|
||||
.or_else(|err| cx.throw_error(err.to_string()))
|
||||
})
|
||||
}
|
||||
|
||||
fn tuple<'a, C: Context<'a>>(cx: &mut C, a: i32, b: i32) -> NeonResult<Handle<'a, JsArray>> {
|
||||
let obj = cx.empty_array();
|
||||
let a_val = cx.number(a);
|
||||
obj.set(cx, 0, a_val)?;
|
||||
let b_val = cx.number(b);
|
||||
obj.set(cx, 1, b_val)?;
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
fn unpack_tuple<'a, C: Context<'a>>(cx: &mut C, arr: Handle<'a, JsArray>) -> NeonResult<(i32, i32)> {
|
||||
Ok((
|
||||
arr.get::<JsNumber, _, u32>(cx, 0)?.value(cx) as i32,
|
||||
arr.get::<JsNumber, _, u32>(cx, 1)?.value(cx) as i32,
|
||||
))
|
||||
}
|
||||
|
||||
struct ClientHandle(Arc<Mutex<CodempClient>>);
|
||||
impl Finalize for ClientHandle {}
|
||||
|
||||
fn connect(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
let host = cx.argument::<JsString>(0).ok().map(|x| x.value(&mut cx));
|
||||
|
||||
let (deferred, promise) = cx.promise();
|
||||
let channel = cx.channel();
|
||||
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
match BufferClient::connect(host.unwrap_or("".into())).await {
|
||||
Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<String, neon::handle::Handle<JsString>>(format!("{}", e))),
|
||||
Ok(c) => deferred.settle_with(&channel, |mut cx| {
|
||||
let obj = cx.empty_object();
|
||||
let boxed_value = cx.boxed(ClientHandle(Arc::new(Mutex::new(c.into()))));
|
||||
obj.set(&mut cx, "boxed", boxed_value)?;
|
||||
let method_create = JsFunction::new(&mut cx, create_client)?;
|
||||
obj.set(&mut cx, "create", method_create)?;
|
||||
let method_listen = JsFunction::new(&mut cx, listen_client)?;
|
||||
obj.set(&mut cx, "listen", method_listen)?;
|
||||
let method_attach = JsFunction::new(&mut cx, attach_client)?;
|
||||
obj.set(&mut cx, "attach", method_attach)?;
|
||||
Ok(obj)
|
||||
}),
|
||||
impl From::<JsCodempError> for napi::Error {
|
||||
fn from(value: JsCodempError) -> Self {
|
||||
napi::Error::new(Status::GenericFailure, &format!("CodempError: {:?}", value))
|
||||
}
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
}
|
||||
|
||||
fn create_client(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
let path = cx.argument::<JsString>(0)?.value(&mut cx);
|
||||
let content = cx.argument::<JsString>(1).ok().map(|x| x.value(&mut cx));
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<ClientHandle>> = this.get(&mut cx, "boxed")?;
|
||||
#[napi]
|
||||
pub fn connect(addr: String) -> napi::Result<()> {
|
||||
CODEMP_INSTANCE.connect(&addr)
|
||||
.map_err(|e| JsCodempError(e).into())
|
||||
}
|
||||
|
||||
let rc = boxed.0.clone();
|
||||
let (deferred, promise) = cx.promise();
|
||||
let channel = cx.channel();
|
||||
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
match rc.lock().await.create(path, content).await {
|
||||
Ok(accepted) => deferred.settle_with(&channel, move |mut cx| Ok(cx.boolean(accepted))),
|
||||
Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<String, neon::handle::Handle<JsString>>(e.to_string())),
|
||||
|
||||
/// CURSOR
|
||||
|
||||
#[napi]
|
||||
pub fn join(session: String) -> napi::Result<JsCursorController> {
|
||||
let controller = CODEMP_INSTANCE.join(&session)
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))?;
|
||||
Ok(controller.into())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub struct JsCursorController(Arc<CodempCursorController>);
|
||||
|
||||
impl From::<Arc<CodempCursorController>> for JsCursorController {
|
||||
fn from(value: Arc<CodempCursorController>) -> Self {
|
||||
JsCursorController(value)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
}
|
||||
|
||||
fn listen_client(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<ClientHandle>> = this.get(&mut cx, "boxed")?;
|
||||
#[napi]
|
||||
impl JsCursorController {
|
||||
|
||||
let rc = boxed.0.clone();
|
||||
let (deferred, promise) = cx.promise();
|
||||
let channel = cx.channel();
|
||||
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
match rc.lock().await.listen().await {
|
||||
Ok(controller) => {
|
||||
deferred.settle_with(&channel, move |mut cx| {
|
||||
let obj = cx.empty_object();
|
||||
let boxed_value = cx.boxed(CursorEventsHandle(controller));
|
||||
obj.set(&mut cx, "boxed", boxed_value)?;
|
||||
let callback_method = JsFunction::new(&mut cx, callback_cursor)?;
|
||||
obj.set(&mut cx, "callback", callback_method)?;
|
||||
let send_method = JsFunction::new(&mut cx, send_cursor)?;
|
||||
obj.set(&mut cx, "send", send_method)?;
|
||||
Ok(obj)
|
||||
})
|
||||
},
|
||||
Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<String, neon::handle::Handle<JsString>>(e.to_string())),
|
||||
}
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
}
|
||||
|
||||
fn attach_client(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<ClientHandle>> = this.get(&mut cx, "boxed")?;
|
||||
let path = cx.argument::<JsString>(0)?.value(&mut cx);
|
||||
|
||||
let rc = boxed.0.clone();
|
||||
let (deferred, promise) = cx.promise();
|
||||
let channel = cx.channel();
|
||||
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
match rc.lock().await.attach(path).await {
|
||||
Ok(controller) => {
|
||||
deferred.settle_with(&channel, move |mut cx| {
|
||||
let obj = cx.empty_object();
|
||||
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)?;
|
||||
let content_method = JsFunction::new(&mut cx, content_operation)?;
|
||||
obj.set(&mut cx, "content", content_method)?;
|
||||
let callback_method = JsFunction::new(&mut cx, callback_operation)?;
|
||||
obj.set(&mut cx, "callback", callback_method)?;
|
||||
Ok(obj)
|
||||
})
|
||||
},
|
||||
Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<String, neon::handle::Handle<JsString>>(e.to_string())),
|
||||
}
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
}
|
||||
|
||||
|
||||
struct OperationControllerJs(OperationControllerHandle);
|
||||
impl Finalize for OperationControllerJs {}
|
||||
|
||||
fn apply_operation(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<OperationControllerJs>> = this.get(&mut cx, "boxed")?;
|
||||
let skip = cx.argument::<JsNumber>(0)?.value(&mut cx).round() as usize;
|
||||
let text = cx.argument::<JsString>(1)?.value(&mut cx);
|
||||
let tail = cx.argument::<JsNumber>(2)?.value(&mut cx).round() as usize;
|
||||
|
||||
let rc = boxed.0.clone();
|
||||
let (deferred, promise) = cx.promise();
|
||||
let channel = cx.channel();
|
||||
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
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()));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
}
|
||||
|
||||
fn content_operation(mut cx: FunctionContext) -> JsResult<JsString> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<OperationControllerJs>> = this.get(&mut cx, "boxed")?;
|
||||
Ok(cx.string(boxed.0.content()))
|
||||
}
|
||||
|
||||
fn callback_operation(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<OperationControllerJs>> = this.get(&mut cx, "boxed")?;
|
||||
let callback = Arc::new(cx.argument::<JsFunction>(0)?.root(&mut cx));
|
||||
|
||||
let mut rc = boxed.0.clone();
|
||||
let channel = cx.channel();
|
||||
|
||||
// TODO when garbage collecting OperationController stop this worker
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
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(edit.span.start as i32))
|
||||
.arg(cx.number(edit.span.end as i32))
|
||||
.arg(cx.string(edit.content))
|
||||
.apply::<JsUndefined, _>(&mut cx)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Ok(cx.undefined())
|
||||
}
|
||||
|
||||
struct CursorEventsHandle(CursorControllerHandle);
|
||||
impl Finalize for CursorEventsHandle {}
|
||||
|
||||
fn callback_cursor(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<CursorEventsHandle>> = this.get(&mut cx, "boxed")?;
|
||||
let callback = Arc::new(cx.argument::<JsFunction>(0)?.root(&mut cx));
|
||||
|
||||
let mut rc = boxed.0.clone();
|
||||
let channel = cx.channel();
|
||||
|
||||
// TODO when garbage collecting OperationController stop this worker
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
while let Some(op) = rc.poll().await {
|
||||
let cb = callback.clone();
|
||||
channel.send(move |mut cx| {
|
||||
cb.to_inner(&mut cx)
|
||||
.call_with(&cx)
|
||||
.arg(cx.string(&op.user))
|
||||
.arg(cx.string(&op.buffer))
|
||||
.arg(tuple(&mut cx, op.start().row, op.start().col)?)
|
||||
.arg(tuple(&mut cx, op.end().row, op.end().col)?)
|
||||
.apply::<JsUndefined, _>(&mut cx)?;
|
||||
Ok(())
|
||||
});
|
||||
#[napi]
|
||||
pub async fn recv(&self) -> napi::Result<JsCursorEvent> {
|
||||
Ok(
|
||||
self.0.recv().await
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))?
|
||||
.into()
|
||||
)
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Ok(cx.undefined())
|
||||
#[napi]
|
||||
pub async fn send(&self, buffer: String, start: (i32, i32), end: (i32, i32)) -> napi::Result<()> {
|
||||
let pos = CodempCursorPosition { buffer, start: Some(RowCol::from(start)), end: Some(RowCol::from(end)) };
|
||||
self.0.send(pos).await
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))
|
||||
}
|
||||
}
|
||||
|
||||
fn send_cursor(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
||||
let this = cx.this();
|
||||
let boxed : Handle<JsBox<CursorEventsHandle>> = this.get(&mut cx, "boxed")?;
|
||||
let path = cx.argument::<JsString>(0)?.value(&mut cx);
|
||||
let start_obj = cx.argument::<JsArray>(1)?;
|
||||
let start = unpack_tuple(&mut cx, start_obj)?;
|
||||
let end_obj = cx.argument::<JsArray>(2)?;
|
||||
let end = unpack_tuple(&mut cx, end_obj)?;
|
||||
|
||||
let rc = boxed.0.clone();
|
||||
let (deferred, promise) = cx.promise();
|
||||
let channel = cx.channel();
|
||||
|
||||
runtime(&mut cx)?.spawn(async move {
|
||||
rc.send(&path, start.into(), end.into()).await;
|
||||
deferred.settle_with(&channel, |mut cx| Ok(cx.undefined()))
|
||||
});
|
||||
|
||||
Ok(promise)
|
||||
#[napi(object)]
|
||||
pub struct JsCursorEvent {
|
||||
pub user: String,
|
||||
pub buffer: String,
|
||||
pub start: JsRowCol,
|
||||
pub end: JsRowCol,
|
||||
}
|
||||
|
||||
#[neon::main]
|
||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
cx.export_function("connect", connect)?;
|
||||
|
||||
Ok(())
|
||||
impl From::<CursorEvent> for JsCursorEvent {
|
||||
fn from(value: CursorEvent) -> Self {
|
||||
let pos = value.position.unwrap_or_default();
|
||||
let start = pos.start.unwrap_or_default();
|
||||
let end = pos.end.unwrap_or_default();
|
||||
JsCursorEvent {
|
||||
user: value.user,
|
||||
buffer: pos.buffer,
|
||||
start: JsRowCol { row: start.row, col: start.col },
|
||||
end: JsRowCol { row: end.row, col: end.col },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
pub struct JsRowCol {
|
||||
pub row: i32,
|
||||
pub col: i32
|
||||
}
|
||||
|
||||
impl From::<RowCol> for JsRowCol {
|
||||
fn from(value: RowCol) -> Self {
|
||||
JsRowCol { row: value.row, col: value.col }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// BUFFER
|
||||
#[napi(object)]
|
||||
pub struct JsTextChange {
|
||||
pub span: JSRange,
|
||||
pub content: String,
|
||||
}
|
||||
#[napi(object)]
|
||||
pub struct JSRange{
|
||||
pub start: i32,
|
||||
pub end: Option<i32>,
|
||||
}
|
||||
|
||||
impl From::<CodempTextChange> for JsTextChange {
|
||||
fn from(value: CodempTextChange) -> Self {
|
||||
JsTextChange {
|
||||
// TODO how is x.. represented ? span.end can never be None
|
||||
span: JSRange { start: value.span.start as i32, end: Some(value.span.end as i32) },
|
||||
content: value.content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From::<Arc<CodempBufferController>> for JsBufferController {
|
||||
fn from(value: Arc<CodempBufferController>) -> Self {
|
||||
JsBufferController(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[napi]
|
||||
pub struct JsBufferController(Arc<CodempBufferController>);
|
||||
|
||||
|
||||
#[napi]
|
||||
impl JsBufferController {
|
||||
|
||||
#[napi]
|
||||
pub async fn recv(&self) -> napi::Result<JsTextChange> {
|
||||
Ok(
|
||||
self.0.recv().await
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))?
|
||||
.into()
|
||||
)
|
||||
}
|
||||
|
||||
//#[napi]
|
||||
pub async fn send(&self, op: OperationSeq) -> napi::Result<()> {
|
||||
self.0.send(op).await
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn create(path: String, content: Option<String>) -> napi::Result<()> {
|
||||
CODEMP_INSTANCE.create(&path, content.as_deref())
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn attach(path: String) -> napi::Result<JsBufferController> {
|
||||
Ok(
|
||||
CODEMP_INSTANCE.attach(&path)
|
||||
.map_err(|e| napi::Error::from(JsCodempError(e)))?
|
||||
.into()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue