diff --git a/.bundle.sh b/.bundle.sh deleted file mode 100755 index 2f38b63..0000000 --- a/.bundle.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -rm codemp.vsix -mkdir -p .vsix/extension -cp package.json .vsix/extension/package.json -cp README.md .vsix/extension/README.md -mkdir .vsix/extension/out -cp -R src/*.js .vsix/extension/out -cp -R codemp.node .vsix/extension/out/codemp.node -cd .vsix/ -zip ../codemp.vsix -r * -cd .. -rm -rf .vsix/ diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..f9b22b7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/naming-convention": "warn", + "@typescript-eslint/semi": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-throw-literal": "warn", + "semi": "off" + }, + "ignorePatterns": [ + "out", + "dist", + "**/*.d.ts" + ] +} diff --git a/.gitignore b/.gitignore index ad591a0..19da0e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,13 @@ -/target +.vscode +.vscodeignore -Cargo.lock - -# vscode extension build files -/client/vscode/node_modules/ -/client/vscode/*.vsix -/client/vscode/codemp.node node_modules +package-lock.json +target +Cargo.lock +codemp.node +index.d.ts +index.node +out diff --git a/Cargo.toml b/Cargo.toml index 89e9473..2dfa517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,12 @@ edition = "2021" [lib] crate-type = ["cdylib"] +path = "src/rust/lib.rs" [dependencies] codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag = "v0.4.5", features = ["global"] } tracing = "0.1" -tracing-subscriber = "0.3" +tracing-subscriber = "0.3.17" uuid = { version = "1.3.1", features = ["v4"] } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/package.json b/package.json index da7e2b2..f52e4a2 100644 --- a/package.json +++ b/package.json @@ -1,96 +1,56 @@ { - "name": "@codemp/vscode", - "version": "0.2.0", - "description": "codemp bindings for vscode plugin", - "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" - ] - } - }, + "name": "codempvscode", + "displayName": "codempvscode", + "description": "", + "version": "0.0.1", "engines": { - "node": ">= 10" + "vscode": "^1.81.0" }, - "scripts": { - "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": { - "@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" - }, - "lint-staged": { - "*.@(js|ts|tsx)": [ - "eslint -c .eslintrc.yml --fix" - ], - "*.@(js|ts|tsx|yml|yaml|md|json)": [ - "prettier --write" - ], - "*.toml": [ - "taplo format" + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:extension.openMyExtension" + ], + "main": "./out/extension.js", + "contributes": { + "commands": [ + { + "command": "codempvscode.connect", + "title": "Connect to a codemp host" + }, + { + "command": "codempvscode.join", + "title": "Join a codemp workspace" + }, + { + "command": "codempvscode.helloWorld", + "title": "Hello World (debug)" + } ] }, - "ava": { - "require": [ - "@swc-node/register" - ], - "extensions": [ - "ts" - ], - "timeout": "2m", - "workerThreads": false, - "environmentVariables": { - "TS_NODE_PROJECT": "./tsconfig.json" - } + "scripts": { + "vscode:prepublish": "npm run compile", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile && npm run lint", + "lint": "eslint src --ext ts", + "test": "node ./out/test/runTest.js", + "compile": "napi build && tsc -p ./" + }, - "prettier": { - "printWidth": 120, - "semi": false, - "trailingComma": "all", - "singleQuote": true, - "arrowParens": "always" + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "16.x", + "@types/vscode": "^1.81.0", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "@typescript-eslint/parser": "^6.4.1", + "@vscode/test-electron": "^2.3.4", + "eslint": "^8.47.0", + "glob": "^10.3.3", + "mocha": "^10.2.0", + "typescript": "^5.1.6" }, - "packageManager": "yarn@3.6.2" + "dependencies": { + "@napi-rs/cli": "^2.16.3" + } } diff --git a/src/extension.js b/src/extension.js deleted file mode 100644 index 93be77b..0000000 --- a/src/extension.js +++ /dev/null @@ -1,146 +0,0 @@ -const vscode = require("vscode"); -const codemp = require("./codemp.node"); - -var CLIENT = null -var CONTROLLER -var CURSOR -var DECORATION = null -var OP_CACHE = new Set() - -async function activate(context) { - context.subscriptions.push( - vscode.commands.registerCommand("codemp.connect", connect), - vscode.commands.registerCommand("codemp.share", share), - vscode.commands.registerCommand("codemp.join", join), - ) -} - -async function connect() { - let host = await vscode.window.showInputBox({prompt: "server host (default to http://fantabos.co:50051)"}) - if (host === undefined) return // user cancelled with ESC - if (host.length == 0) host = "http://fantabos.co:50051" - CLIENT = await codemp.connect(host) - vscode.window.showInformationMessage(`Connected to codemp @[${host}]`); -} - -async function share() { - if (CLIENT === null) { - vscode.window.showErrorMessage("No connected client"); - } - - let path = await vscode.window.showInputBox({prompt: "buffer uri (default to file path)"}) - if (path === undefined) return // user cancelled with ESC - if (path.length == 0) path = doc.uri.toString() - - let doc = vscode.window.activeTextEditor.document; - - try { - if (!await CLIENT.create(path, doc.getText())) { - vscode.window.showErrorMessage("Could not share buffer"); - } - - await _attach(path) - - vscode.window.showInformationMessage(`Shared document on buffer "${path}"`); - } catch (err) { - vscode.window.showErrorMessage("Error sharing: " + err) - } -} - -async function join() { - if (CLIENT === null) { - vscode.window.showErrorMessage("No connected client"); - } - - let path = await vscode.window.showInputBox({prompt: "buffer uri"}) - - try { - let controller = await _attach(path) - - vscode.window.showInformationMessage(`Joined buffer "${path}"`); - - let editor = vscode.window.activeTextEditor - - let range = new vscode.Range( - editor.document.positionAt(0), - editor.document.positionAt(editor.document.getText().length) - ) - let content = controller.content() - OP_CACHE.add((range, content)) - editor.edit(editBuilder => editBuilder.replace(range, content)) - } catch (err) { - vscode.window.showErrorMessage("error joining " + err) - } -} - -function _order_tuples(a, b) { - if (a[0] < b[0]) return (a, b) - if (a[0] > b[0]) return (b, a) - if (a[1] < b[1]) return (a, b) - return (b, a) -} - -async function _attach(path) { - let editor = vscode.window.activeTextEditor - let doc = editor.document; - - CURSOR = await CLIENT.listen() - CURSOR.callback((usr, path, start, end) => { - try { - if (DECORATION != null) { - DECORATION.dispose() - DECORATION = null - } - const range_start = new vscode.Position(start[0] - 1, start[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("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+1] - // (anchor, position) = _order_tuples(anchor, position) - await CURSOR.send(buf, anchor, position) - }) - - CONTROLLER = await CLIENT.attach(path) - CONTROLLER.callback((start, end, text) => { - try { - 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) - } - }) - vscode.workspace.onDidChangeTextDocument(async (e) => { - if (e.document != doc) return - for (let change of e.contentChanges) { - if (OP_CACHE.has((change.range, change.text))) { - OP_CACHE.delete((change.range, change.text)) - continue - } - try { - await CONTROLLER.apply(change.rangeOffset, change.text, change.rangeOffset + change.rangeLength) - } catch (err) { - vscode.window.showErrorMessage("failed sending change: " + err) - } - } - }) - return CONTROLLER -} - -module.exports = { - activate, -} diff --git a/src/extension.ts b/src/extension.ts index c6107b3..4f53d6a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,23 +1,8 @@ -/* - - -vscode -+ src - + glue.rs - + extension.ts -+ Cargo.toml -+ package.json - - -*/ - - - // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode'; //import * as codempp from '/home/***REMOVED***/projects/codemp/mine/codempvscode/codemp.node'; -const codemp = require("/home/***REMOVED***/projects/codemp/mine/vscode/target/debug/libcodemp_vscode.node"); +const codemp = require("/home/***REMOVED***/projects/codemp/mine/codempvscode/index.node"); // import * as codemp from "/home/***REMOVED***/projects/codemp/mine/vscode/target/debug/libcodemp_vscode.node"; // This method is called when your extension is activated diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e8a5419..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,199 +0,0 @@ -#![deny(clippy::all)] - -use std::sync::Arc; -use futures::prelude::*; -use napi::bindgen_prelude::*; -use codemp::{ - prelude::*, - proto::{RowCol, CursorEvent}, - buffer::factory::OperationFactory, ot::OperationSeq -}; -use napi_derive::napi; -use napi::tokio::{self, fs}; - -#[derive(Debug)] -struct JsCodempError(CodempError); - -impl From:: for napi::Error { - fn from(value: JsCodempError) -> Self { - napi::Error::new(Status::GenericFailure, &format!("CodempError: {:?}", value)) - } -} - -#[napi] -pub async fn connect(addr: String) -> napi::Result<()> { - CODEMP_INSTANCE.connect(&addr).await - .map_err(|e| JsCodempError(e).into()) -} - - - -/// CURSOR - -#[napi] -pub async fn join(session: String) -> napi::Result { - let controller = CODEMP_INSTANCE.join(&session).await - .map_err(|e| napi::Error::from(JsCodempError(e)))?; - Ok(controller.into()) -} - -#[napi] -pub struct JsCursorController(Arc); - -impl From::> for JsCursorController { - fn from(value: Arc) -> Self { - JsCursorController(value) - } -} - -#[napi] -impl JsCursorController { - - #[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, 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) - .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:: 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:: 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, -} - -impl From:: 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:: for JsCodempOperationSeq{ - fn from(value: OperationSeq) -> Self { - JsCodempOperationSeq(value) - } - - -} - - -impl From::> for JsBufferController { - fn from(value: Arc) -> Self { - JsBufferController(value) - } -} - - -#[napi] -pub struct JsBufferController(Arc); - -#[napi(js_name = "CodempOperationSeq")] -pub struct JsCodempOperationSeq(CodempOperationSeq); - - - - -#[napi] -impl JsBufferController { - - #[napi] - pub fn delta(&self, start: i64, txt: String, end: i64) -> Option { - self.0.delta(start as usize, &txt, end as usize).map(|x| x.into()) - } - - - - #[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: &JsCodempOperationSeq) -> napi::Result<()> { - // TODO might be nice to take ownership of the opseq - self.0.send(op.0.clone()) - .map_err(|e| napi::Error::from(JsCodempError(e))) - } - - -} - -#[napi] -pub async fn create(path: String, content: Option) -> napi::Result<()> { - CODEMP_INSTANCE.create(&path, content.as_deref()).await - .map_err(|e| napi::Error::from(JsCodempError(e))) -} - -#[napi] -pub async fn attach(path: String) -> napi::Result { - Ok( - CODEMP_INSTANCE.attach(&path).await - .map_err(|e| napi::Error::from(JsCodempError(e)))? - .into() - ) -} - - - - - diff --git a/src/rust/lib.rs b/src/rust/lib.rs index fbfefae..befe705 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -29,7 +29,7 @@ impl From:: for napi::Error { #[napi] pub async fn connect(addr: String) -> napi::Result<()> { - let f = std::fs::File::create("/home/***REMOVED***/projects/codemp/mine/vscode/log.txt").unwrap(); + let f = std::fs::File::create("/home/***REMOVED***/projects/codemp/mine/vscode/***REMOVED***.txt").unwrap(); tracing_subscriber::fmt() .with_ansi(false) .with_max_level(tracing::Level::INFO) @@ -81,9 +81,7 @@ impl JsCursorController { tokio::spawn(async move { loop { let event = _controller.recv().await.expect("could not receive cursor event!"); - tracing::info!("printing '{:?}' event", event); // works? tsfn.call(event.clone(), ThreadsafeFunctionCallMode::NonBlocking); //check this shit with tracing also we could use Ok(event) to get the error - tracing::info!("printing '{:?}' event after tsfn", event); // works? } }); Ok(()) diff --git a/tsconfig.json b/tsconfig.json index 315af7e..5b7343d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,5 +13,6 @@ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - } + }, + "exclude": ["index.d.ts"] }