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
This commit is contained in:
Camillo Schenone 2024-02-23 17:49:26 +01:00
parent ff883c8e58
commit fa5f03bd6b
3 changed files with 48 additions and 22 deletions

View file

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

View file

@ -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,

View file

@ -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)",