diff --git a/package.json b/package.json index a74a27b..7355a10 100644 --- a/package.json +++ b/package.json @@ -23,13 +23,21 @@ "command": "codempvscode.connect", "title": "Connect to a codemp host" }, + { + "command": "codempvscode.login", + "title": "Log into a codemp account" + }, { "command": "codempvscode.join", "title": "Join a codemp workspace" }, { "command": "codempvscode.createBuffer", - "title": "create a codemp buffer" + "title": "Create a codemp buffer" + }, + { + "command": "codempvscode.listBuffers", + "title": "List all buffers of joined Workspace" }, { "command": "codempvscode.attach", @@ -37,7 +45,7 @@ }, { "command": "codempvscode.disconnectBuffer", - "title": "disconnect from a codemp Buffer" + "title": "disconnect from a codemp Buffer (unused)" }, { "command": "codempvscode.sync", diff --git a/src/codemp.ts b/src/codemp.ts index 2d5ce41..00545a9 100644 --- a/src/codemp.ts +++ b/src/codemp.ts @@ -1,36 +1,57 @@ import * as vscode from 'vscode'; import * as codemp from '../index'; // TODO why won't it work with a custom name??? +class BufferMapping { + codemp: string; + vscode: vscode.TextEditor; + + constructor(codemp_path: string, editor: vscode.TextEditor) { + this.codemp = codemp_path; + this.vscode = editor; + } +} var CACHE = new codemp.OpCache(); -var BUFFERS : string[][] = []; +var BUFFERS : BufferMapping[] = []; let smallNumberDecorationType = vscode.window.createTextEditorDecorationType({}); +let client : codemp.JsCodempClient | null = null; +let workspace : codemp.JsWorkspace | null = null; + export async function connect() { - let host = await vscode.window.showInputBox({prompt: "server host (default to http://alemi.dev:50052)"}); - if (host === undefined) return // user cancelled with ESC - if (host.length == 0) host = "http://alemi.dev:50052" - await codemp.connect(host); - vscode.window.showInformationMessage(`Connected to codemp @[${host}]`); + /*let host = await vscode.window.showInputBox({prompt: "server host (default to http://codemp.alemi.dev:50053)"}); + if(host===null) host="http://codemp.alemi.dev:50053"; + client = await codemp.connect(host); + vscode.window.showInformationMessage(`Connected to codemp @[${host}]`);*/ + client = await codemp.connect(); + vscode.window.showInformationMessage('Connected to codemp with default host'); +} + +export async function login(){ + let username = await vscode.window.showInputBox({prompt: "enter username"}); + let workspace_name = await vscode.window.showInputBox({prompt: "enter workspace name"}); + if(client===null) throw "connect first"; + if(workspace_name===null) workspace_name="asd"; + await client.login(username!,"lmaodefaultpassword",workspace_name); + vscode.window.showInformationMessage("Logged with username " + username + " into workspace " + workspace_name); } export async function join() { - let workspace = await vscode.window.showInputBox({prompt: "workspace to attach (default to default)"}); + let workspace_id = await vscode.window.showInputBox({prompt: "workspace to attach (default to default)"}); let buffer : string = (await vscode.window.showInputBox({prompt: "buffer name for the file needed to update other clients cursors"}))!; //let editor = vscode.window.activeTextEditor; - if (workspace === undefined) return // user cancelled with ESC - if (workspace.length == 0) workspace = "default" + if (workspace_id === undefined) return // user cancelled with ESC + if (workspace_id.length == 0) workspace_id = "asd" if (buffer === undefined) return // user cancelled with ESC - if (buffer.length == 0) {workspace = "default"; buffer="fucl"; } + if (buffer.length == 0) {workspace_id = "asd"; buffer="fucl"; } - let controller : codemp.JsCursorController = await codemp.join(workspace) - controller.callback(( event:any) => { - let buf : string = event.textEditor.document.uri.toString() - let curPos = vscode.window.activeTextEditor?.selection.active; - let PosNumber : number = curPos?.line as number; - let posizione : vscode.Position = new vscode.Position(0, PosNumber); + if(client===null) throw "connect first"; + workspace = await client.joinWorkspace(workspace_id) + let controller = workspace.cursor(); + controller.callback((event: codemp.JsCursorEvent) => { + console.log(`received cursor event, im on ${event.buffer}`) let range_start : vscode.Position = new vscode.Position(event.start.row , event.start.col); // -1? let range_end : vscode.Position = new vscode.Position(event.end.row, event.end.col); // -1? idk if this works it's kinda funny, should test with someone with a working version of codemp const decorationRange = new vscode.Range(range_start, range_end); @@ -49,57 +70,40 @@ export async function join() { borderColor: 'lightblue' //should create this color based on event.user (uuid) } }); - for (let tuple of BUFFERS) { - if (tuple[0].toString() === buf) { - vscode.window.activeTextEditor?.setDecorations(smallNumberDecorationType, [decorationRange]); + for (let mapping of BUFFERS) { + console.log(`checking tuple ${mapping}`); + if (mapping.codemp === event.buffer) { + mapping.vscode.setDecorations(smallNumberDecorationType, [decorationRange]); + return } } + console.log(`wtf buffers didn't contain it???? ${BUFFERS}`) }); vscode.window.onDidChangeTextEditorSelection((event: vscode.TextEditorSelectionChangeEvent) => { if (event.kind == vscode.TextEditorSelectionChangeKind.Command) return; // TODO commands might move cursor too - let buf : string = event.textEditor.document.uri.toString() + let buf = event.textEditor.document.uri; let selection : vscode.Selection = event.selections[0] // TODO there may be more than one cursor!! let anchor : [number, number] = [selection.anchor.line, selection.anchor.character]; let position : [number, number] = [selection.active.line, selection.active.character+1]; - for (let tuple of BUFFERS) { - if (tuple[0].toString() === buf) { - controller.send(tuple[1], anchor, position); + for (let mapping of BUFFERS) { + if (mapping.vscode.document.uri === buf) { + controller.send(mapping.codemp, anchor, position); } } }); + console.log("workspace id \n"); + console.log(workspace.id()); vscode.window.showInformationMessage(`Connected to workspace @[${workspace}]`); } export async function createBuffer() { - let workspace="default";//ask which workspace let bufferName : any = (await vscode.window.showInputBox({prompt: "path of the buffer to create"}))!; - codemp.create(bufferName); + if(workspace===null) throw "join a workspace first" + workspace.create(bufferName); console.log("new buffer created ", bufferName, "\n"); - let editor = vscode.window.activeTextEditor; - - if (editor === undefined) { return } // TODO say something!!!!!! - - /*let range = new vscode.Range( - editor.document.positionAt(0), - editor.document.positionAt(editor.document.getText().length) - )*/ - let buffer : codemp.JsBufferController = await codemp.attach(bufferName); - console.log("buffer"); - console.log(buffer); - //let opSeq = {range.start,editor.document.getText(),range.end} - //buffer.send(range.start,editor.document.getText(),range.end); //test it plz coded this at 10am :( - buffer.send({ - span: { - start: 0, - end: 0 //previous length is 0 - }, - content: editor.document.getText() - }); - console.log("sent all the content", editor.document.getText()); - //Should i disconnect or stay attached to buffer??? } @@ -108,7 +112,8 @@ export async function createBuffer() { export async function attach() { let buffer_name : any = (await vscode.window.showInputBox({prompt: "buffer to attach to"}))!; - let buffer : codemp.JsBufferController = await codemp.attach(buffer_name); + if(workspace===null) throw "join a workspace first" + let buffer : codemp.JsBufferController = await workspace.attach(buffer_name); console.log("attached to buffer", buffer_name); console.log("buffer", buffer); let editor = vscode.window.activeTextEditor; @@ -132,7 +137,7 @@ export async function attach() { vscode.window.showInformationMessage(`Connected to codemp workspace buffer @[${buffer_name}]`); let file_uri : vscode.Uri = editor.document.uri; - BUFFERS.push([file_uri, buffer_name]); + BUFFERS.push(new BufferMapping(buffer_name, editor)); vscode.workspace.onDidChangeTextDocument((event:vscode.TextDocumentChangeEvent) => { //console.log(event.reason); @@ -167,24 +172,24 @@ export async function attach() { }); } -export async function disconnectBuffer() { +/*export async function disconnectBuffer() { TODO i should just set buffer=null let buffer : string = (await vscode.window.showInputBox({prompt: "buffer name for the file to disconnect from"}))!; codemp.disconnectBuffer(buffer); vscode.window.showInformationMessage(`Disconnected from codemp workspace buffer @[${buffer}]`); -} +}*/ export async function sync() { let editor = vscode.window.activeTextEditor; if (editor === undefined) { return } - for (let tuple of BUFFERS) { - console.log(tuple[0].toString()); + for (let mapping of BUFFERS) { + console.log(mapping.vscode.document.uri); //console.log(tuple[1]); console.log("\n"); console.log(editor?.document.uri.toString()); //console.log(BUFFERS[0]); - if (tuple[0].toString() === editor?.document.uri.toString()) { - - let buffer = await codemp.getBuffer(tuple[1]); + if (mapping.vscode.document.uri === editor?.document.uri) { + if(workspace===null) throw "join a workspace first" + let buffer = await workspace.bufferByName(mapping.codemp); if (buffer==null) { vscode.window.showErrorMessage("This buffer does not exist anymore"); return; @@ -195,7 +200,7 @@ export async function sync() { editor.document.positionAt(editor.document.getText().length) ); - CACHE.put(tuple[1],0,content,editor.document.getText().length); + CACHE.put(mapping.codemp, 0, content, editor.document.getText().length); editor.edit(editBuilder => editBuilder.replace(range, content)); return; } @@ -205,7 +210,11 @@ export async function sync() { } } - +export async function listBuffers(){ + if(workspace===null) throw "join a workspace first" + let buffers = workspace.filetree(); + console.log(buffers); // improve UX +} diff --git a/src/extension.ts b/src/extension.ts index afdd4c5..f07c465 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,41 +1,39 @@ -// 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 codemp from '../index'; // TODO why won't it work with a custom name??? import * as codemplogic from './codemp'; - -// This method is called when your extension is activated -// Your extension is activated the very first time the command is executed +// extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated console.log('Congratulations, your extension "codempvscode" is now active!'); - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - let disposable = vscode.commands.registerCommand('codempvscode.helloWorld', () => { - // The code you place here will be executed every time your command is executed - // Display a message box to the user - vscode.window.showInformationMessage(process.cwd()); - }); - let connectCommand = vscode.commands.registerCommand('codempvscode.connect', codemplogic.connect); - let joinCommand = vscode.commands.registerCommand('codempvscode.join', codemplogic.join); - let attachCommand = vscode.commands.registerCommand('codempvscode.attach', codemplogic.attach); - let createBufferCommand = vscode.commands.registerCommand('codempvscode.createBuffer', codemplogic.createBuffer); - let disconnectBufferCommand = vscode.commands.registerCommand('codempvscode.disconnectBuffer', codemplogic.disconnectBuffer); - let syncBufferCommand = vscode.commands.registerCommand('codempvscode.sync', codemplogic.sync); - context.subscriptions.push(connectCommand); - context.subscriptions.push(joinCommand); - context.subscriptions.push(attachCommand); - context.subscriptions.push(createBufferCommand); - context.subscriptions.push(disconnectBufferCommand); - context.subscriptions.push(syncBufferCommand); - context.subscriptions.push(disposable); + // start codemp log poller + let channel = vscode.window.createOutputChannel("codemp", {log: true}); + let logger = new codemp.JsLogger(false); + log_poller_task(logger, channel); // don't await it! run it in background forever + + // register commands: the commandId parameter must match the command field in package.json + for (let cmd of [ + vscode.commands.registerCommand('codempvscode.connect', codemplogic.connect), + vscode.commands.registerCommand('codempvscode.login', codemplogic.login), + vscode.commands.registerCommand('codempvscode.join', codemplogic.join), + vscode.commands.registerCommand('codempvscode.attach', codemplogic.attach), + vscode.commands.registerCommand('codempvscode.createBuffer', codemplogic.createBuffer), + vscode.commands.registerCommand('codempvscode.listBuffers', codemplogic.listBuffers), + // vscode.commands.registerCommand('codempvscode.disconnectBuffer', codemplogic.disconnectBuffer), + vscode.commands.registerCommand('codempvscode.sync', codemplogic.sync), + ]) { + context.subscriptions.push(cmd); + } } - - - +async function log_poller_task(logger: codemp.JsLogger, channel: vscode.LogOutputChannel) { + console.log("starting logger task"); + while (true) { + let message = await logger.message(); + if (message === null) break; + console.log(message); + channel.info(message); + } + console.log("stopping logger task"); +} \ No newline at end of file diff --git a/src/rust/lib.rs b/src/rust/lib.rs index 43aadf9..97875bf 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -13,4 +13,56 @@ 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