diff --git a/src/commands/buffers.ts b/src/commands/buffers.ts index bf71e70..e073746 100644 --- a/src/commands/buffers.ts +++ b/src/commands/buffers.ts @@ -1,8 +1,7 @@ import * as vscode from 'vscode'; import * as codemp from 'codemp'; import * as mapping from "../mapping"; -import { workspaceState } from "./workspaces"; -import { LOGGER, provider } from '../extension'; +import { COC, LOGGER, provider } from '../extension'; let singles: Map<string, boolean> = new Map(); let locks: Map<string, string> = new Map(); @@ -16,7 +15,7 @@ export async function apply_changes_to_buffer(path: string, controller: codemp.B singles.set(path, true); while (true) { - if (workspaceState.workspace === null) { + if (!COC.has_workspace()) { LOGGER.info(`left workspace, unregistering buffer controller '${path}' callback`); controller.clearCallback(); return; @@ -34,12 +33,12 @@ export async function apply_changes_to_buffer(path: string, controller: codemp.B if (codemp.hash(editor.document.getText()) !== event.hash) { if (autoResync) { vscode.window.showWarningMessage("Out of Sync, resynching..."); - await resync(path, workspaceState.workspace, editor, 20); + await resync(path, COC.workspace(), editor, 20); } else { controller.clearCallback(); const selection = await vscode.window.showWarningMessage('Out of Sync', 'Resync'); - if (selection !== undefined && workspaceState.workspace) { - await resync(path, workspaceState.workspace, editor, 20); + if (selection !== undefined && COC.workspace()) { + await resync(path, COC.workspace(), editor, 20); controller.callback(async (controller: codemp.BufferController) => await apply_changes_to_buffer(controller.path(), controller) ); @@ -63,7 +62,7 @@ export async function apply_changes_to_buffer(path: string, controller: codemp.B } export async function attach_to_remote_buffer(buffer_name: string, set_content?: boolean): Promise<codemp.BufferController | undefined> { - if (workspaceState.workspace === null) { + if (!COC.has_workspace()) { vscode.window.showErrorMessage("join a Workspace first"); return; } @@ -87,7 +86,7 @@ export async function attach_to_remote_buffer(buffer_name: string, set_content?: let doc = await vscode.workspace.openTextDocument(path); let editor = await vscode.window.showTextDocument(doc, { preserveFocus: false }) await editor.edit((editor) => editor.setEndOfLine(vscode.EndOfLine.LF)); // set LF for EOL sequence - let buffer: codemp.BufferController = await workspaceState.workspace.attachBuffer(buffer_name); + let buffer: codemp.BufferController = await COC.workspace().attachBuffer(buffer_name); // wait for server changes // TODO poll never unblocks, so this dirty fix is necessary @@ -149,7 +148,7 @@ export async function attach_to_remote_buffer(buffer_name: string, set_content?: } export async function attach(selected: vscode.TreeItem | undefined) { - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Join a workspace first"); + if (!COC.has_workspace()) return vscode.window.showWarningMessage("Join a workspace first"); let buffer_name: string | undefined; if (selected !== undefined && selected.label !== undefined) { if (typeof (selected.label) === 'string') { @@ -158,14 +157,14 @@ export async function attach(selected: vscode.TreeItem | undefined) { buffer_name = selected.label.label; // TODO ughh what is this api? } } else { - buffer_name = await vscode.window.showQuickPick(workspaceState.workspace.searchBuffers(), { placeHolder: "buffer to attach to:" }, undefined); + buffer_name = await vscode.window.showQuickPick(COC.workspace().searchBuffers(), { placeHolder: "buffer to attach to:" }, undefined); } if (!buffer_name) return; await attach_to_remote_buffer(buffer_name); } export async function detach(selected: vscode.TreeItem | undefined) { - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Not in a workspace"); + if (!COC.has_workspace()) return vscode.window.showWarningMessage("Not in a workspace"); let buffer_name: string | undefined; if (selected !== undefined && selected.label !== undefined) { if (typeof (selected.label) === 'string') { @@ -174,12 +173,12 @@ export async function detach(selected: vscode.TreeItem | undefined) { buffer_name = selected.label.label; // TODO ughh what is this api? } } else { - buffer_name = await vscode.window.showQuickPick(workspaceState.workspace.activeBuffers(), { placeHolder: "buffer to detach from:" }, undefined); + buffer_name = await vscode.window.showQuickPick(COC.workspace().activeBuffers(), { placeHolder: "buffer to detach from:" }, undefined); } if (!buffer_name) return; - let controller = workspaceState.workspace.getBuffer(buffer_name); + let controller = COC.workspace().getBuffer(buffer_name); if (controller) controller.clearCallback(); - workspaceState.workspace.detachBuffer(buffer_name); + COC.workspace().detachBuffer(buffer_name); mapping.bufferMapper.remove(buffer_name); vscode.window.showInformationMessage(`Detached from buffer ${buffer_name}`) provider.refresh(); @@ -187,7 +186,7 @@ export async function detach(selected: vscode.TreeItem | undefined) { export async function share() { - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Join a workspace first"); + if (!COC.workspace()) return vscode.window.showWarningMessage("Join a workspace first"); let buffer_name: string | undefined; if (vscode.window.activeTextEditor !== null) { buffer_name = vscode.window.activeTextEditor?.document.uri.toString(); @@ -199,12 +198,12 @@ export async function share() { let workspacePath: string = vscode.workspace.workspaceFolders[0].uri.toString(); buffer_name = buffer_name.replace(workspacePath, "").substring(1); //vscode.workspace.asRelativePath doesn't work properly with other extensions like ssh, substring(1) to remove "/" console.log("After: " + buffer_name); - await workspaceState.workspace.createBuffer(buffer_name); + await COC.workspace().createBuffer(buffer_name); await attach_to_remote_buffer(buffer_name, true); } export async function sync(selected: vscode.TreeItem | undefined) { - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Join a workspace first"); + if (!COC.has_workspace()) return vscode.window.showWarningMessage("Join a workspace first"); let editor; let buffer_name; if (selected !== undefined && selected.label !== undefined) { @@ -222,7 +221,7 @@ export async function sync(selected: vscode.TreeItem | undefined) { if (buffer_name === undefined) return vscode.window.showWarningMessage("Buffer not synched with codemp"); } - resync(buffer_name, workspaceState.workspace, editor); + resync(buffer_name, COC.workspace(), editor); } export async function resync(buffer_name: string, workspace: codemp.Workspace, editor: vscode.TextEditor, tries?: number) { diff --git a/src/commands/client.ts b/src/commands/client.ts index 85ca7d2..af61824 100644 --- a/src/commands/client.ts +++ b/src/commands/client.ts @@ -2,11 +2,10 @@ import * as vscode from 'vscode'; import * as codemp from 'codemp'; import * as mapping from "../mapping"; import { executeJump, workspaceState } from "./workspaces"; -import { LOGGER, provider } from '../extension'; +import { COC, LOGGER, provider } from '../extension'; // TODO this "global state" should probably live elsewher but we need lo update it from these commands -export let client: codemp.Client | null = null; export let workspace_list: string[] = []; export let cursor_disposable: vscode.Disposable | null; @@ -24,13 +23,13 @@ export async function connect() { } try { - client = await codemp.connect({ + COC.set_client(await codemp.connect({ username: username, password: password, host: config.get<string>("server"), port: config.get<number>("port"), tls: config.get<boolean>("tls"), - }); + })); vscode.window.showInformationMessage("Connected to codemp"); provider.refresh(); listWorkspaces(); // dont await, run in background @@ -40,7 +39,7 @@ export async function connect() { } export async function join(selected: vscode.TreeItem | undefined) { - if (client === null) return vscode.window.showWarningMessage("Connect first"); + if (COC.has_client()) return vscode.window.showWarningMessage("Connect first"); let workspace_id: string | undefined; if (selected !== undefined && selected.label !== undefined) { if (typeof (selected.label) === 'string') { @@ -56,8 +55,8 @@ export async function join(selected: vscode.TreeItem | undefined) { let ws = await vscode.window.showWorkspaceFolderPick({ placeHolder: "directory to open workspace into:" }); if (ws === undefined) return vscode.window.showErrorMessage("Open a Workspace folder first"); } - workspaceState.workspace = await client.attachWorkspace(workspace_id); - let controller = workspaceState.workspace.cursor(); + COC.set_workspace(await COC.client().attachWorkspace(workspace_id)); + let controller = COC.workspace().cursor(); controller.callback(cursor_callback); let once = true; @@ -92,9 +91,9 @@ export async function join(selected: vscode.TreeItem | undefined) { } }); - workspaceState.workspace.callback(workspace_callback); + COC.workspace().callback(workspace_callback); - for (let user of workspaceState.workspace.userList()) { + for (let user of COC.workspace().userList()) { mapping.colors_cache.set(user.name, new mapping.UserDecoration(user.name)); } @@ -104,12 +103,12 @@ export async function join(selected: vscode.TreeItem | undefined) { async function workspace_callback(controller: codemp.Workspace) { while (true) { - if (workspaceState.workspace === null) { + if (!COC.has_workspace()) { controller.clearCallback(); LOGGER.info("left workspace, stopping receiving events"); return; } - let event = await workspaceState.workspace.tryRecv(); + let event = await COC.workspace().tryRecv(); if (event === null) break; if (event.type == "leave") { mapping.colors_cache.get(event.value)?.clear() @@ -125,7 +124,7 @@ async function workspace_callback(controller: codemp.Workspace) { async function cursor_callback(controller: codemp.CursorController) { while (true) { let event = await controller.tryRecv(); - if (workspaceState.workspace === null) { + if (!COC.has_workspace()) { controller.clearCallback(); LOGGER.info("left workspace, stopping cursor controller"); return; @@ -152,9 +151,9 @@ async function cursor_callback(controller: codemp.CursorController) { export async function listWorkspaces() { - if (client === null) return vscode.window.showWarningMessage("Connect first"); - let workspace_joined = await client.fetchJoinedWorkspaces(); - let workspace_owned = await client.fetchOwnedWorkspaces(); + if (!COC.has_client()) return vscode.window.showWarningMessage("Connect first"); + let workspace_joined = await COC.client().fetchJoinedWorkspaces(); + let workspace_owned = await COC.client().fetchOwnedWorkspaces(); workspace_list = workspace_owned.concat(workspace_joined); provider.refresh(); } @@ -162,49 +161,49 @@ export async function listWorkspaces() { export async function createWorkspace() { - if (client === null) return vscode.window.showWarningMessage("Connect first"); + if (!COC.has_client()) return vscode.window.showWarningMessage("Connect first"); let workspace_id = await vscode.window.showInputBox({ prompt: "Enter name for workspace" }); if (workspace_id === undefined) return; - await client.createWorkspace(workspace_id); + await COC.client().createWorkspace(workspace_id); vscode.window.showInformationMessage("Created new workspace " + workspace_id); listWorkspaces(); } export async function inviteToWorkspace() { - if (client === null) return vscode.window.showWarningMessage("Connect first"); + if (!COC.has_client()) return vscode.window.showWarningMessage("Connect first"); let workspace_id = await vscode.window.showQuickPick(workspace_list, { placeHolder: "workspace to invite to:" }); if (workspace_id === undefined) return; let user_id = await vscode.window.showInputBox({ prompt: "Name of user to invite" }); if (user_id === undefined) return; - await client.inviteToWorkspace(workspace_id, user_id); + await COC.client().inviteToWorkspace(workspace_id, user_id); vscode.window.showInformationMessage("Invited " + user_id + " into workspace " + workspace_id); } export async function leave() { - if (!client) throw "can't leave while disconnected"; - if (!workspaceState.workspace) throw "can't leave while not in a workspace"; - workspaceState.workspace.cursor().clearCallback() - client.leaveWorkspace(workspaceState.workspace.id()); + if (!COC.has_client()) throw "can't leave while disconnected"; + if (!COC.has_workspace()) throw "can't leave while not in a workspace"; + COC.workspace().cursor().clearCallback() + COC.client().leaveWorkspace(COC.workspace().id()); if (cursor_disposable !== null) cursor_disposable.dispose(); - let workspace_id = workspaceState.workspace.id(); - workspaceState.workspace = null; + let workspace_id = COC.workspace().id(); + COC.clear_workspace(); provider.refresh(); vscode.window.showInformationMessage("Left workspace " + workspace_id); } export async function deleteWorkspace() { - if (client === null) return vscode.window.showWarningMessage("Connect first"); + if (!COC.has_client()) return vscode.window.showWarningMessage("Connect first"); let workspace_id = await vscode.window.showInputBox({ prompt: "Enter workspace's name to delete" }); if (workspace_id === undefined) return; - await client.deleteWorkspace(workspace_id); + await COC.client().deleteWorkspace(workspace_id); vscode.window.showInformationMessage("Deleted workspace " + workspace_id); listWorkspaces(); } export async function refresh() { - if (client === null) return vscode.window.showWarningMessage("Connect first"); - await client.refresh(); + if (!COC.has_client()) return vscode.window.showWarningMessage("Connect first"); + await COC.client().refresh(); vscode.window.showInformationMessage("Refreshed Session token"); } diff --git a/src/commands/workspaces.ts b/src/commands/workspaces.ts index fd52411..e5f2d4b 100644 --- a/src/commands/workspaces.ts +++ b/src/commands/workspaces.ts @@ -1,23 +1,19 @@ import * as vscode from 'vscode'; -import * as codemp from 'codemp'; import * as mapping from "../mapping"; -import { client } from "./client" -import { LOGGER, provider } from '../extension'; +import { COC, provider } from '../extension'; export let workspaceState: { - workspace: codemp.Workspace | null, follow: string | null, justJumped: boolean, } = { - workspace: null, follow: null, justJumped: false, }; export async function jump(selected: vscode.TreeItem | undefined) { - if (client === null) return vscode.window.showWarningMessage("Connect first"); + if (!COC.has_client()) return vscode.window.showWarningMessage("Connect first"); let user; if (selected !== undefined && selected.label !== undefined) { if (typeof (selected.label) === 'string') { @@ -52,23 +48,23 @@ export async function executeJump(user: string) { export async function createBuffer() { let bufferName: any = (await vscode.window.showInputBox({ prompt: "path of the buffer to create" })); - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Join a workspace first"); - await workspaceState.workspace.createBuffer(bufferName); + if (!COC.has_workspace()) return vscode.window.showWarningMessage("Join a workspace first"); + await COC.workspace().createBuffer(bufferName); vscode.window.showInformationMessage(`new buffer created :${bufferName}`); provider.refresh(); } export async function listBuffers() { - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Join a workspace first"); - let buffers = workspaceState.workspace.searchBuffers(); + if (!COC.has_workspace()) return vscode.window.showWarningMessage("Join a workspace first"); + let buffers = COC.workspace().searchBuffers(); vscode.window.showInformationMessage(buffers.join("\n")); provider.refresh(); } export async function deleteBuffer() { let bufferName: any = (await vscode.window.showInputBox({ prompt: "path of the buffer to delete" })); - if (workspaceState.workspace === null) return vscode.window.showWarningMessage("Join a workspace first"); - await workspaceState.workspace.deleteBuffer(bufferName); + if (!COC.has_workspace()) return vscode.window.showWarningMessage("Join a workspace first"); + await COC.workspace().deleteBuffer(bufferName); vscode.window.showInformationMessage(`Deleted buffer :${bufferName}`); provider.refresh(); } diff --git a/src/extension.ts b/src/extension.ts index 4d4cdee..13efdd0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,15 +1,56 @@ import * as vscode from 'vscode'; import * as codemp from 'codemp'; -import { client, connect, join, refresh, createWorkspace, inviteToWorkspace, listWorkspaces, leave, deleteWorkspace, version } from './commands/client'; +import { connect, join, refresh, createWorkspace, inviteToWorkspace, listWorkspaces, leave, deleteWorkspace, version } from './commands/client'; import { CodempTreeProvider } from './tree'; import * as mapping from './mapping'; -import { workspaceState, jump, listBuffers, createBuffer, deleteBuffer } from './commands/workspaces' +import { jump, listBuffers, createBuffer, deleteBuffer } from './commands/workspaces' import { attach, share, sync, apply_changes_to_buffer, detach } from './commands/buffers' export let provider = new CodempTreeProvider(); export let LOGGER = vscode.window.createOutputChannel("codemp", { log: true }); +export class CodempObjectCache { + private _client: codemp.Client | null = null; + private _workspace: codemp.Workspace | null = null; + + public client(): codemp.Client { + if (this._client === null) throw "Must connect first!"; + return this._client; + } + + public has_client(): boolean { + return this._client !== null; + } + + public set_client(client: codemp.Client) { + this._client = client; + } + + public clear_client() { + this._client = null; + } + + public workspace(): codemp.Workspace { + if (this._workspace === null) throw "Must join a workspace first"; + return this._workspace; + } + + public has_workspace(): boolean { + return this._workspace !== null; + } + + public set_workspace(workspace: codemp.Workspace) { + this._workspace = workspace; + } + + public clear_workspace() { + this._workspace = null; + } +} + +export let COC = new CodempObjectCache(); + // extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { let config = vscode.workspace.getConfiguration('codemp'); @@ -22,11 +63,11 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(sub); vscode.window.onDidChangeVisibleTextEditors(async (editors: readonly vscode.TextEditor[]) => { - if (workspaceState.workspace === null) return; + if (!COC.has_workspace()) return; for (let editor of editors) { let path = mapping.bufferMapper.by_editor(editor.document.uri); if (!path) continue; - let controller = workspaceState.workspace.getBuffer(path); + let controller = COC.workspace().getBuffer(path); if (!controller) continue; await apply_changes_to_buffer(path, controller, true); } @@ -57,8 +98,8 @@ export function activate(context: vscode.ExtensionContext) { } export async function deactivate() { - if (client && workspaceState.workspace) { - await client.leaveWorkspace(workspaceState.workspace.id()); + if (COC.has_client() && COC.has_workspace()) { + await COC.client().leaveWorkspace(COC.workspace().id()); } } diff --git a/src/tree.ts b/src/tree.ts index 704b736..adf1119 100644 --- a/src/tree.ts +++ b/src/tree.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; -import { client, workspace_list } from './commands/client'; -import { workspaceState } from './commands/workspaces'; +import { workspace_list } from './commands/client'; import { bufferMapper, colors_cache } from './mapping'; +import { COC } from "./extension"; export class CodempTreeProvider implements vscode.TreeDataProvider<CodempTreeItem> { @@ -22,17 +22,17 @@ export class CodempTreeProvider implements vscode.TreeDataProvider<CodempTreeIte if (element) { switch (element.type) { case Type.CurrentWorkspace: - if (workspaceState.workspace === null) return []; // TODO ???? error maybe ??? - let items = workspaceState.workspace.searchBuffers().map((x) => + if (!COC.has_workspace()) return []; + let items = COC.workspace().searchBuffers().map((x) => new CodempTreeItem(x, Type.Buffer, { active: bufferMapper.bufferToEditorMapping.has(x) }) ); items.push(new CodempTreeItem("", Type.Placeholder, { expandable: false })); items.push(new CodempTreeItem("Users", Type.UserContainer, { expandable: true })); return items; case Type.WorkspaceContainer: - let active = workspaceState.workspace === null; + let active = !COC.has_workspace(); return workspace_list - .filter((x) => workspaceState.workspace == null || x != workspaceState.workspace.id()) + .filter((x) => x != COC.workspace().id()) .map((x) => new CodempTreeItem(x, Type.Workspace, { expandable: false, active: active })); case Type.UserContainer: @@ -44,9 +44,9 @@ export class CodempTreeProvider implements vscode.TreeDataProvider<CodempTreeIte case Type.ClientContainer: let info = []; - if (client === null) return []; - info.push(new CodempTreeItem("username", Type.ClientInfo, { description: client.currentUser().name })); - info.push(new CodempTreeItem("uuid", Type.ClientInfo, { description: client.currentUser().uuid })); + if (!COC.has_client()) return []; + info.push(new CodempTreeItem("username", Type.ClientInfo, { description: COC.client().currentUser().name })); + info.push(new CodempTreeItem("uuid", Type.ClientInfo, { description: COC.client().currentUser().uuid })); return info; case Type.Placeholder: @@ -58,14 +58,14 @@ export class CodempTreeProvider implements vscode.TreeDataProvider<CodempTreeIte return []; } } else { - if (client === null) { + if (!COC.has_client()) { return []; // empty screen with [connect] button } let items = []; - if (workspaceState.workspace !== null) { - items.push(new CodempTreeItem(workspaceState.workspace.id(), Type.CurrentWorkspace, { expandable: true })); + if (COC.has_workspace()) { + items.push(new CodempTreeItem(COC.workspace().id(), Type.CurrentWorkspace, { expandable: true })); items.push(new CodempTreeItem("", Type.Placeholder, {})); }