From abc976e3e581864f78b32a5331bac717252d41c7 Mon Sep 17 00:00:00 2001 From: cschen Date: Sat, 24 Aug 2024 18:45:42 +0200 Subject: [PATCH] feat: added leave buffer command. feat: new generic input handler for sequence of text inputs with defaults. chore: minor fixes and improvements Former-commit-id: 73ea017903fd717d894092871b7b62f827df4ff2 --- Codemp.sublime-commands | 32 +++--- plugin.py | 238 +++++++++++++++++++++------------------- src/client.py | 6 +- src/workspace.py | 2 +- 4 files changed, 148 insertions(+), 130 deletions(-) diff --git a/Codemp.sublime-commands b/Codemp.sublime-commands index 5472fad..21bd39a 100644 --- a/Codemp.sublime-commands +++ b/Codemp.sublime-commands @@ -28,6 +28,11 @@ // "server_host": "http://[::1]:50051" } }, + { + "caption": "Codemp: Disconnect Client", + "command": "codemp_disconnect", + "arg": {} + }, { "caption": "Codemp: Join Workspace", "command": "codemp_join_workspace", @@ -36,6 +41,13 @@ // 'buffer_id': 'test' }, }, + { + "caption": "Codemp: Leave Workspace", + "command": "codemp_leave_workspace", + "arg": { + // "id": 'lmaaaao' + } + }, { "caption": "Codemp: Join Buffer", "command": "codemp_join_buffer", @@ -45,23 +57,11 @@ }, }, { - "caption": "Codemp: Share", - "command": "codemp_share", + "caption": "Codemp: Leave Buffer", + "command": "codemp_leave_buffer", "arg": { - // 'sublime_buffer' : /path/to/buffer/to/share - // 'server_id' : 'how to call the buffer on the server' + // 'workspace_id': 'asd' + // 'buffer_id': 'test' } }, - { - "caption": "Codemp: Leave Workspace", - "command": "codemp_leave_workspace", - "arg": { - // "id": 'lmaaaao' - } - }, - { - "caption": "Codemp: Disconnect Client", - "command": "codemp_disconnect", - "arg": {} - }, ] \ No newline at end of file diff --git a/plugin.py b/plugin.py index e6acbde..c20b8ea 100644 --- a/plugin.py +++ b/plugin.py @@ -3,6 +3,7 @@ import sublime import sublime_plugin import logging import random +from typing import List, Tuple import codemp from Codemp.src.client import client @@ -114,7 +115,7 @@ class CodempClientViewEventListener(sublime_plugin.ViewEventListener): if vws is None or vbuff is None: raise - vws.uninstall_buffer(vbuff.id) + vws.uninstall_buffer(vbuff) def on_text_command(self, command_name, args): if command_name == "codemp_replace_text": @@ -146,23 +147,26 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener): sublime.set_timeout(lambda: vbuff.send_buffer_change(changes)) -# Commands: +# Client Commands: # codemp_connect: connect to a server. -# codemp_join: shortcut command if you already know both workspace id -# and buffer id -# codemp_join_workspace: joins a specific workspace, without joining also a buffer -# codemp_join_buffer: joins a specific buffer within the current active workspace - - -# codemp_share: ??? todo!() # codemp_disconnect: manually call the disconnection, triggering the cleanup and dropping # the connection -# +# codemp_join_workspace: joins a specific workspace, without joining also a buffer +# codemp_leave_workspace: + +# Workspace Commands: +# codemp_join_buffer: joins a specific buffer within the current active workspace +# codemp_leave_buffer: +# codemp_create_buffer: +# codemp_delete_buffer: + # Internal commands: # replace_text: swaps the content of a view with the given text. -# -# Connect Command + + +# Client Commands ############################################################################# +# Connect Command class CodempConnectCommand(sublime_plugin.WindowCommand): def is_enabled(self) -> bool: return client.codemp is None @@ -182,42 +186,27 @@ class CodempConnectCommand(sublime_plugin.WindowCommand): sublime.set_timeout_async(try_connect) - def input(self, args): - if "server_host" not in args: - return ConnectServerHost() - def input_description(self): return "Server host:" - -class ConnectServerHost(sublime_plugin.TextInputHandler): - def name(self): - return "server_host" - - def initial_text(self): - return "http://127.0.0.1:50051" - - def next_input(self, args): - if "user_name" not in args: - return ConnectUserName(args) + def input(self, args): + if "server_host" not in args: + return SimpleTextInput( + ("server_host", "http://127.0.0.1:50051"), + ("user_name", f"user-{random.random()}"), + ) -class ConnectUserName(sublime_plugin.TextInputHandler): - def __init__(self, args): - self.host = args["server_host"] +# Disconnect Command +class CodempDisconnectCommand(sublime_plugin.WindowCommand): + def is_enabled(self): + return client.codemp is not None - def name(self): - return "user_name" - - def initial_text(self): - return f"user-{random.random()}" + def run(self): + client.disconnect() -# Separate the join command into two join workspace and join buffer commands that get called back to back - - -# Generic Join Workspace Command -############################################################################# +# Join Workspace Command class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand): def is_enabled(self) -> bool: return client.codemp is not None @@ -252,11 +241,6 @@ class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand): return WorkspaceIdText() -class WorkspaceIdText(sublime_plugin.TextInputHandler): - def name(self): - return "workspace_id" - - # To allow for having a selection and choosing non existing workspaces # we do a little dance: We pass this list input handler to a TextInputHandler # when we select "Create New..." which adds his result to the list of possible @@ -291,6 +275,25 @@ class WorkspaceIdText(sublime_plugin.TextInputHandler): # return AddListEntry(self) +# Leave Workspace Command +class CodempLeaveWorkspaceCommand(sublime_plugin.WindowCommand): + def is_enabled(self): + return client.codemp is not None and len(client.all_workspaces(self.window)) > 0 + + def run(self, workspace_id: str): + # client.leave_workspace(id) + pass + + def input(self, args): + if "id" not in args: + return ActiveWorkspacesIdList() + + +# WORKSPACE COMMANDS +############################################################################# + + +# Join Buffer Command class CodempJoinBufferCommand(sublime_plugin.WindowCommand): def is_enabled(self): available_workspaces = client.all_workspaces(self.window) @@ -343,8 +346,6 @@ class CodempJoinBufferCommand(sublime_plugin.WindowCommand): return "Attach: " def input(self, args): - # if we have only a workspace in the window, then - # skip to the buffer choice if "workspace_id" not in args: return ActiveWorkspacesIdList(self.window, get_buffer=True) @@ -352,6 +353,86 @@ class CodempJoinBufferCommand(sublime_plugin.WindowCommand): return BufferIdList(args["workspace_id"]) +# Leave Buffer Comand +class CodempLeaveBufferCommand(sublime_plugin.WindowCommand): + def is_enabled(self): + return len(client.all_buffers()) > 0 + + def run(self, workspace_id, buffer_id): + vbuff = client.buffer_from_id(buffer_id) + vws = client.workspace_from_id(workspace_id) + + if vbuff is None or vws is None: + return + + def defer_detach(): + if vws.codemp.detach(buffer_id): + vws.uninstall_buffer(vbuff) + + sublime.set_timeout_async(defer_detach) + + def input_description(self) -> str: + return "Leave: " + + def input(self, args): + if "workspace_id" not in args: + return ActiveWorkspacesIdList(self.window, get_buffer=True) + + if "buffer_id" not in args: + return BufferIdList(args["workspace_id"]) + + +# Text Change Command +############################################################################# +class CodempReplaceTextCommand(sublime_plugin.TextCommand): + def run(self, edit, start, end, content, change_id): + # we modify the region to account for any change that happened in the mean time + region = self.view.transform_region_from(sublime.Region(start, end), change_id) + self.view.replace(edit, region, content) + + +# Input handlers +############################################################ +class SimpleTextInput(sublime_plugin.TextInputHandler): + def __init__(self, *args: Tuple[str, str]): + assert len(args) > 0 + self.argname = args[0][0] + self.default = args[0][1] + self.next_inputs = args[1:] + + def initial_text(self): + return self.default + + def name(self): + return self.argname + + def next_input(self, args): + if len(self.next_inputs) > 0: + if self.next_inputs[0][0] not in args: + return SimpleTextInput(*self.next_inputs) + + +class WorkspaceIdText(sublime_plugin.TextInputHandler): + def name(self): + return "workspace_id" + + +class ActiveWorkspacesIdList(sublime_plugin.ListInputHandler): + def __init__(self, window=None, get_buffer=False): + self.window = window + self.get_buffer = get_buffer + + def name(self): + return "workspace_id" + + def list_items(self): + return [vws.id for vws in client.all_workspaces(self.window)] + + def next_input(self, args): + if self.get_buffer: + return BufferIdList(args["workspace_id"]) + + class BufferIdList(sublime_plugin.ListInputHandler): def __init__(self, workspace_id): self.add_entry_text = "* create new..." @@ -377,69 +458,6 @@ class BufferIdList(sublime_plugin.ListInputHandler): return AddListEntry(self) -# Text Change Command -############################################################################# -class CodempReplaceTextCommand(sublime_plugin.TextCommand): - def run(self, edit, start, end, content, change_id): - # we modify the region to account for any change that happened in the mean time - region = self.view.transform_region_from(sublime.Region(start, end), change_id) - self.view.replace(edit, region, content) - - -# Share Command -# ############################################################################# -# class CodempShareCommand(sublime_plugin.WindowCommand): -# def run(self, sublime_buffer_path, server_id): -# sublime_asyncio.dispatch(share_buffer_command(sublime_buffer_path, server_id)) - -# def input(self, args): -# if "sublime_buffer" not in args: -# return SublimeBufferPathInputHandler() - -# def input_description(self): -# return "Share Buffer:" - - -# Disconnect Command -############################################################################# -class CodempDisconnectCommand(sublime_plugin.WindowCommand): - def is_enabled(self): - return client.codemp is not None - - def run(self): - client.disconnect() - - -# Leave Workspace Command -class CodempLeaveWorkspaceCommand(sublime_plugin.WindowCommand): - def is_enabled(self): - return client.codemp is not None and len(client.all_workspaces(self.window)) > 0 - - def run(self, workspace_id: str): - # client.leave_workspace(id) - pass - - def input(self, args): - if "id" not in args: - return ActiveWorkspacesIdList() - - -class ActiveWorkspacesIdList(sublime_plugin.ListInputHandler): - def __init__(self, window=None, get_buffer=False): - self.window = window - self.get_buffer = get_buffer - - def name(self): - return "workspace_id" - - def list_items(self): - return [vws.id for vws in client.all_workspaces(self.window)] - - def next_input(self, args): - if self.get_buffer: - return BufferIdList(args["workspace_id"]) - - class AddListEntry(sublime_plugin.TextInputHandler): # this class works when the list input handler # added appended a new element to it's list that will need to be diff --git a/src/client.py b/src/client.py index 81c5457..2d0cecb 100644 --- a/src/client.py +++ b/src/client.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Optional, Dict +from typing import Optional import sublime @@ -50,7 +50,7 @@ class VirtualClient: if window is None: return list(self.__workspace2window.keys()) else: - return self.__workspace2window.inverse[window] + return self.__workspace2window.inverse.get(window, []) def workspace_from_view(self, view: sublime.View) -> Optional[VirtualWorkspace]: buff = self.__view2buff.get(view, None) @@ -70,7 +70,7 @@ class VirtualClient: else: if isinstance(workspace, str): workspace = client.__id2workspace[workspace] - return self.__buff2workspace.inverse[workspace] + return self.__buff2workspace.inverse.get(workspace, []) def buffer_from_view(self, view: sublime.View) -> Optional[VirtualBuffer]: return self.__view2buff.get(view) diff --git a/src/workspace.py b/src/workspace.py index 8944948..b793717 100644 --- a/src/workspace.py +++ b/src/workspace.py @@ -47,7 +47,7 @@ class VirtualWorkspace: all(id in self.__id2buff for id in attached_buffers) # TODO! - def valid_bufffer(self, buff: VirtualBuffer | str): + def valid_buffer(self, buff: VirtualBuffer | str): if isinstance(buff, str): return self.buff_by_id(buff) is not None