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]
|
[net]
|
||||||
git-fetch-with-cli = true
|
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/*.vsix
|
||||||
/client/vscode/codemp.node
|
/client/vscode/codemp.node
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -3,20 +3,27 @@ name = "codemp-vscode"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
description = "VSCode extension for CodeMP"
|
description = "VSCode extension for CodeMP"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
exclude = ["index.node"]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[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 = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
uuid = { version = "1.3.1", features = ["v4"] }
|
uuid = { version = "1.3.1", features = ["v4"] }
|
||||||
once_cell = "1"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
rmpv = "1"
|
rmpv = "1"
|
||||||
clap = { version = "4.2.1", features = ["derive"] }
|
|
||||||
async-trait = "0.1.68"
|
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();
|
||||||
|
}
|
114
package.json
114
package.json
|
@ -1,38 +1,96 @@
|
||||||
{
|
{
|
||||||
"name": "codemp-vscode",
|
"name": "@codemp/vscode",
|
||||||
"version": "0.0.1",
|
"version": "0.2.0",
|
||||||
"description": "VSCode extension for CodeMP",
|
"description": "codemp bindings for vscode plugin",
|
||||||
"main": "./out/extension.js",
|
"main": "index.js",
|
||||||
|
"files": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"napi": {
|
||||||
|
"name": "codemp-vscode",
|
||||||
|
"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": {
|
"engines": {
|
||||||
"vscode": "^1.32.0"
|
"node": ">= 10"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cargo-cp-artifact --artifact cdylib codemp-vscode codemp.node -- cargo build --release --message-format=json-render-diagnostics",
|
"artifacts": "napi artifacts",
|
||||||
"install": "npm run build",
|
"bench": "node -r @swc-node/register benchmark/bench.ts",
|
||||||
"test": "cargo test"
|
"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": {
|
"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": {
|
"lint-staged": {
|
||||||
"commands": [
|
"*.@(js|ts|tsx)": [
|
||||||
{
|
"eslint -c .eslintrc.yml --fix"
|
||||||
"command": "codemp.connect",
|
],
|
||||||
"title": "Connect to CodeMP"
|
"*.@(js|ts|tsx|yml|yaml|md|json)": [
|
||||||
},
|
"prettier --write"
|
||||||
{
|
],
|
||||||
"command": "codemp.join",
|
"*.toml": [
|
||||||
"title": "Join remote session"
|
"taplo format"
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "codemp.share",
|
|
||||||
"title": "Share local session"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"ava": {
|
||||||
"onCommand:codemp.connect",
|
"require": [
|
||||||
"onCommand:codemp.join",
|
"@swc-node/register"
|
||||||
"onCommand:codemp.share"
|
],
|
||||||
]
|
"extensions": [
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"timeout": "2m",
|
||||||
|
"workerThreads": false,
|
||||||
|
"environmentVariables": {
|
||||||
|
"TS_NODE_PROJECT": "./tsconfig.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleQuote": true,
|
||||||
|
"arrowParens": "always"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@3.6.2"
|
||||||
}
|
}
|
||||||
|
|
367
src/lib.rs
367
src/lib.rs
|
@ -1,268 +1,177 @@
|
||||||
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use futures::prelude::*;
|
||||||
use neon::prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use codemp::{
|
use codemp::{
|
||||||
controller::cursor::{CursorSubscriber, CursorControllerHandle},
|
prelude::*,
|
||||||
controller::buffer::{OperationControllerHandle, OperationControllerSubscriber},
|
proto::{RowCol, CursorEvent},
|
||||||
client::CodempClient,
|
buffer::factory::OperationFactory, ot::OperationSeq
|
||||||
factory::OperationFactory,
|
|
||||||
proto::buffer_client::BufferClient,
|
|
||||||
};
|
};
|
||||||
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> {
|
#[derive(Debug)]
|
||||||
static RUNTIME: OnceCell<Runtime> = OnceCell::new();
|
struct JsCodempError(CodempError);
|
||||||
|
|
||||||
RUNTIME.get_or_try_init(|| {
|
impl From::<JsCodempError> for napi::Error {
|
||||||
Runtime::new()
|
fn from(value: JsCodempError) -> Self {
|
||||||
.or_else(|err| cx.throw_error(err.to_string()))
|
napi::Error::new(Status::GenericFailure, &format!("CodempError: {:?}", value))
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tuple<'a, C: Context<'a>>(cx: &mut C, a: i32, b: i32) -> NeonResult<Handle<'a, JsArray>> {
|
#[napi]
|
||||||
let obj = cx.empty_array();
|
pub fn connect(addr: String) -> napi::Result<()> {
|
||||||
let a_val = cx.number(a);
|
CODEMP_INSTANCE.connect(&addr)
|
||||||
obj.set(cx, 0, a_val)?;
|
.map_err(|e| JsCodempError(e).into())
|
||||||
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,
|
/// CURSOR
|
||||||
arr.get::<JsNumber, _, u32>(cx, 1)?.value(cx) as i32,
|
|
||||||
))
|
#[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())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ClientHandle(Arc<Mutex<CodempClient>>);
|
#[napi]
|
||||||
impl Finalize for ClientHandle {}
|
pub struct JsCursorController(Arc<CodempCursorController>);
|
||||||
|
|
||||||
fn connect(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
impl From::<Arc<CodempCursorController>> for JsCursorController {
|
||||||
let host = cx.argument::<JsString>(0).ok().map(|x| x.value(&mut cx));
|
fn from(value: Arc<CodempCursorController>) -> Self {
|
||||||
|
JsCursorController(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (deferred, promise) = cx.promise();
|
#[napi]
|
||||||
let channel = cx.channel();
|
impl JsCursorController {
|
||||||
|
|
||||||
runtime(&mut cx)?.spawn(async move {
|
#[napi]
|
||||||
match BufferClient::connect(host.unwrap_or("".into())).await {
|
pub async fn recv(&self) -> napi::Result<JsCursorEvent> {
|
||||||
Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<String, neon::handle::Handle<JsString>>(format!("{}", e))),
|
Ok(
|
||||||
Ok(c) => deferred.settle_with(&channel, |mut cx| {
|
self.0.recv().await
|
||||||
let obj = cx.empty_object();
|
.map_err(|e| napi::Error::from(JsCodempError(e)))?
|
||||||
let boxed_value = cx.boxed(ClientHandle(Arc::new(Mutex::new(c.into()))));
|
.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)?;
|
#[napi]
|
||||||
obj.set(&mut cx, "listen", method_listen)?;
|
pub async fn send(&self, buffer: String, start: (i32, i32), end: (i32, i32)) -> napi::Result<()> {
|
||||||
let method_attach = JsFunction::new(&mut cx, attach_client)?;
|
let pos = CodempCursorPosition { buffer, start: Some(RowCol::from(start)), end: Some(RowCol::from(end)) };
|
||||||
obj.set(&mut cx, "attach", method_attach)?;
|
self.0.send(pos).await
|
||||||
Ok(obj)
|
.map_err(|e| napi::Error::from(JsCodempError(e)))
|
||||||
}),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
pub struct JsCursorEvent {
|
||||||
|
pub user: String,
|
||||||
|
pub buffer: String,
|
||||||
|
pub start: JsRowCol,
|
||||||
|
pub end: JsRowCol,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 },
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
Ok(promise)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_client(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
#[napi(object)]
|
||||||
let path = cx.argument::<JsString>(0)?.value(&mut cx);
|
pub struct JsRowCol {
|
||||||
let content = cx.argument::<JsString>(1).ok().map(|x| x.value(&mut cx));
|
pub row: i32,
|
||||||
let this = cx.this();
|
pub col: i32
|
||||||
let boxed : Handle<JsBox<ClientHandle>> = this.get(&mut cx, "boxed")?;
|
}
|
||||||
|
|
||||||
let rc = boxed.0.clone();
|
impl From::<RowCol> for JsRowCol {
|
||||||
let (deferred, promise) = cx.promise();
|
fn from(value: RowCol) -> Self {
|
||||||
let channel = cx.channel();
|
JsRowCol { row: value.row, col: value.col }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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))),
|
/// BUFFER
|
||||||
Err(e) => deferred.settle_with(&channel, move |mut cx| cx.throw_error::<String, neon::handle::Handle<JsString>>(e.to_string())),
|
#[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,
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
Ok(promise)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listen_client(mut cx: FunctionContext) -> JsResult<JsPromise> {
|
impl From::<Arc<CodempBufferController>> for JsBufferController {
|
||||||
let this = cx.this();
|
fn from(value: Arc<CodempBufferController>) -> Self {
|
||||||
let boxed : Handle<JsBox<ClientHandle>> = this.get(&mut cx, "boxed")?;
|
JsBufferController(value)
|
||||||
|
}
|
||||||
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);
|
#[napi]
|
||||||
impl Finalize for OperationControllerJs {}
|
pub struct JsBufferController(Arc<CodempBufferController>);
|
||||||
|
|
||||||
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();
|
#[napi]
|
||||||
let (deferred, promise) = cx.promise();
|
impl JsBufferController {
|
||||||
let channel = cx.channel();
|
|
||||||
|
#[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)))
|
||||||
|
}
|
||||||
|
|
||||||
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> {
|
#[napi]
|
||||||
let this = cx.this();
|
pub fn create(path: String, content: Option<String>) -> napi::Result<()> {
|
||||||
let boxed : Handle<JsBox<OperationControllerJs>> = this.get(&mut cx, "boxed")?;
|
CODEMP_INSTANCE.create(&path, content.as_deref())
|
||||||
Ok(cx.string(boxed.0.content()))
|
.map_err(|e| napi::Error::from(JsCodempError(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn callback_operation(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
#[napi]
|
||||||
let this = cx.this();
|
pub fn attach(path: String) -> napi::Result<JsBufferController> {
|
||||||
let boxed : Handle<JsBox<OperationControllerJs>> = this.get(&mut cx, "boxed")?;
|
Ok(
|
||||||
let callback = Arc::new(cx.argument::<JsFunction>(0)?.root(&mut cx));
|
CODEMP_INSTANCE.attach(&path)
|
||||||
|
.map_err(|e| napi::Error::from(JsCodempError(e)))?
|
||||||
let mut rc = boxed.0.clone();
|
.into()
|
||||||
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(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(cx.undefined())
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[neon::main]
|
|
||||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
|
||||||
cx.export_function("connect", connect)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue