From fa5f03bd6b72bacfcc74bdee79c2474edb61129b Mon Sep 17 00:00:00 2001 From: Camillo Schenone Date: Fri, 23 Feb 2024 17:49:26 +0100 Subject: [PATCH] switched to more solid (but uglier) way to check for input focus. Changed how to get from a sublime view to a virtual buffer by means of tags. (maybe slow) added some syntactic sugar Former-commit-id: 2003fe0838af7e47258c6d10d84e2142dfa3da1b --- plugin.py | 27 +++++++++++++++------------ src/codemp_client.py | 37 ++++++++++++++++++++++++++++--------- src/globals.py | 6 +++++- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/plugin.py b/plugin.py index f5ca185..4050345 100644 --- a/plugin.py +++ b/plugin.py @@ -84,15 +84,17 @@ class EventListener(sublime_plugin.EventListener): class CodempClientViewEventListener(sublime_plugin.ViewEventListener): @classmethod def is_applicable(cls, settings): - return settings.get(g.CODEMP_BUFFER_VIEW_TAG, False) + return settings.get(g.CODEMP_BUFFER_TAG, False) @classmethod def applies_to_primary_view_only(cls): return False def on_selection_modified_async(self): + s = self.view.settings() + global CLIENT - vbuff = CLIENT.active_workspace.get_by_local(self.view.buffer_id()) + vbuff = CLIENT[s[g.CODEMP_WORKSPACE_ID]].get_by_local(self.view.buffer_id()) if vbuff is not None: CLIENT.send_cursor(vbuff) @@ -101,21 +103,26 @@ class CodempClientViewEventListener(sublime_plugin.ViewEventListener): # When we defocus, we detach it. def on_activated(self): global TEXT_LISTENER + + g.ACTIVE_CODEMP_VIEW = self.view.id() print("view {} activated".format(self.view.id())) TEXT_LISTENER.attach(self.view.buffer()) def on_deactivated(self): global TEXT_LISTENER + + g.ACTIVE_CODEMP_VIEW = None print("view {} deactivated".format(self.view.id())) safe_listener_detach(TEXT_LISTENER) def on_pre_close(self): global TEXT_LISTENER - if is_active(self.view): + if self.view.id() == g.ACTIVE_CODEMP_VIEW: safe_listener_detach(TEXT_LISTENER) global CLIENT - vbuff = CLIENT.active_workspace.get_by_local(self.view.buffer_id()) + wsid = self.view.settings().get(g.CODEMP_WORKSPACE_ID) + vbuff = CLIENT[wsid].get_by_local(self.view.buffer_id()) vbuff.cleanup() CLIENT.tm.stop_and_pop(f"{g.BUFFCTL_TASK_PREFIX}-{vbuff.codemp_id}") @@ -132,17 +139,14 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener): # blocking :D def on_text_changed(self, changes): - if ( - self.buffer.primary_view() - .settings() - .get(g.CODEMP_IGNORE_NEXT_TEXT_CHANGE, None) - ): + s = self.buffer.primary_view().settings() + if s.get(g.CODEMP_IGNORE_NEXT_TEXT_CHANGE, None): status_log("ignoring echoing back the change.") - self.view.settings()[g.CODEMP_IGNORE_NEXT_TEXT_CHANGE] = False + s[g.CODEMP_IGNORE_NEXT_TEXT_CHANGE] = False return global CLIENT - vbuff = CLIENT.active_workspace.get_by_local(self.buffer.id()) + vbuff = CLIENT[s[g.CODEMP_WORKSPACE_ID]].get_by_local(self.buffer.id()) CLIENT.send_buffer_change(changes, vbuff) @@ -250,7 +254,6 @@ class ListBufferIdInputHandler(sublime_plugin.ListInputHandler): 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 - print("running the replace command, launche manually.") region = self.view.transform_region_from(sublime.Region(start, end), change_id) self.view.replace(edit, region, content) diff --git a/src/codemp_client.py b/src/codemp_client.py index 2938f8f..f0fc03d 100644 --- a/src/codemp_client.py +++ b/src/codemp_client.py @@ -7,6 +7,7 @@ import asyncio # noqa: F401 import typing # noqa: F401 import tempfile import os +import shutil import Codemp.src.globals as g @@ -42,13 +43,19 @@ class VirtualBuffer: self.view.set_scratch(True) # mark the view as a codemp view + s = self.view.settings() self.view.set_status(g.SUBLIME_STATUS_ID, "[Codemp]") - self.view.settings()[g.CODEMP_BUFFER_VIEW_TAG] = True + s[g.CODEMP_BUFFER_TAG] = True + s[g.CODEMP_REMOTE_ID] = self.codemp_id + s[g.CODEMP_WORKSPACE_ID] = self.workspace.id def cleanup(self): os.remove(self.tmpfile) # cleanup views - del self.view.settings()[g.CODEMP_BUFFER_VIEW_TAG] + s = self.view.settings() + del s[g.CODEMP_BUFFER_TAG] + del s[g.CODEMP_REMOTE_ID] + del s[g.CODEMP_WORKSPACE_ID] self.view.erase_status(g.SUBLIME_STATUS_ID) # this does nothing for now. figure out a way later # self.view.erase_regions(g.SUBLIME_REGIONS_PREFIX) @@ -65,7 +72,7 @@ class VirtualWorkspace: self.handle = handle self.curctl = handle.cursor() - # mapping local buffer ids -> remote ids + # mapping remote ids -> local ids self.id_map: dict[str, str] = {} self.active_buffers: dict[str, VirtualBuffer] = {} @@ -85,8 +92,8 @@ class VirtualWorkspace: self.sublime_window.set_project_data(proj_data) def add_buffer(self, remote_id: str, vbuff: VirtualBuffer): - self.id_map[vbuff.view.buffer_id()] = remote_id - self.active_buffers[remote_id] = vbuff + self.id_map[remote_id] = vbuff.view.buffer_id() + self.active_buffers[vbuff.view.buffer_id()] = vbuff def cleanup(self): # the worskpace only cares about closing the various open views on its buffers. @@ -104,13 +111,13 @@ class VirtualWorkspace: d["folders"] = newf self.sublime_window.set_project_data(d) status_log(f"cleaning up virtual workspace '{self.id}'") - os.removedirs(self.rootdir) + shutil.rmtree(self.rootdir, ignore_errors=True) def get_by_local(self, local_id: str) -> Optional[VirtualBuffer]: - return self.active_buffers.get(self.id_map.get(local_id)) + return self.active_buffers.get(local_id) def get_by_remote(self, remote_id: str) -> Optional[VirtualBuffer]: - return self.active_buffers.get(remote_id) + return self.active_buffers.get(self.id_map.get(remote_id)) async def attach(self, id: str): if id is None: @@ -149,6 +156,9 @@ class VirtualClient: self.active_workspace: VirtualWorkspace = None self.tm = TaskManager(on_exit) + def __getitem__(self, key: str): + return self.workspaces.get(key) + def make_active(self, ws: VirtualWorkspace): # TODO: Logic to deal with swapping to and from workspaces, # what happens to the cursor tasks etc.. @@ -157,6 +167,12 @@ class VirtualClient: self.active_workspace = ws self.spawn_cursor_manager(ws) + def get_by_local(self, id): + for vws in self.workspaces.values(): + vbuff = vws.get_by_local(id) + if vbuff is not None: + return + async def connect(self, server_host: str): status_log(f"Connecting to {server_host}") try: @@ -248,7 +264,10 @@ class VirtualClient: # In case a change arrives to a background buffer, just apply it. # We are not listening on it. Otherwise, interrupt the listening # to avoid echoing back the change just received. - if is_active(vb.view): + if vb.view.id() == g.ACTIVE_CODEMP_VIEW: + status_log( + "received a text change with view active, stopping the echo." + ) vb.view.settings()[g.CODEMP_IGNORE_NEXT_TEXT_CHANGE] = True # we need to go through a sublime text command, since the method, diff --git a/src/globals.py b/src/globals.py index b258d63..dd9fb74 100644 --- a/src/globals.py +++ b/src/globals.py @@ -1,10 +1,14 @@ BUFFCTL_TASK_PREFIX = "buffer-ctl" CURCTL_TASK_PREFIX = "cursor-ctl" +CODEMP_BUFFER_TAG = "codemp-buffer" +CODEMP_REMOTE_ID = "codemp-buffer-id" +CODEMP_WORKSPACE_ID = "codemp-workspace-id" + WORKSPACE_FOLDER_PREFIX = "CODEMP::" SUBLIME_REGIONS_PREFIX = "codemp-cursors" -CODEMP_BUFFER_VIEW_TAG = "codemp-buffer" SUBLIME_STATUS_ID = "z_codemp_buffer" CODEMP_IGNORE_NEXT_TEXT_CHANGE = "codemp-skip-change-event" +ACTIVE_CODEMP_VIEW = None PALETTE = [ "var(--redish)",