fix: codemp object cache to store client and ws

vscode shared state must be json serializable, ouch...
This commit is contained in:
əlemi 2025-02-15 18:42:20 +01:00
parent 9947a289de
commit c06ebaa57c
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 112 additions and 77 deletions

View file

@ -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) {

View file

@ -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");
}

View file

@ -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();
}

View file

@ -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());
}
}

View file

@ -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, {}));
}