diff --git a/src/buffers.py b/src/buffers.py index f863881..85b78f1 100644 --- a/src/buffers.py +++ b/src/buffers.py @@ -6,7 +6,7 @@ import logging import codemp from Codemp.src import globals as g -from Codemp.src.utils import populate_view +from Codemp.src.utils import populate_view, safe_listener_attach, safe_listener_detach logger = logging.getLogger(__name__) @@ -66,7 +66,7 @@ class VirtualBuffer: self.tmpfile = os.path.join(rootdir, self.id) open(self.tmpfile, "a").close() - # self.view.set_scratch(True) + self.view.set_scratch(True) self.view.set_name(self.id) self.view.retarget(self.tmpfile) @@ -74,13 +74,17 @@ class VirtualBuffer: self.view.set_status(g.SUBLIME_STATUS_ID, "[Codemp]") s[g.CODEMP_BUFFER_TAG] = True - self.sync() - logger.info(f"registering a callback for buffer: {self.id}") self.buffctl.callback(make_bufferchange_cb(self)) self.isactive = True def __del__(self): + logger.debug("__del__ buffer called.") + + def __hash__(self) -> int: + return hash(self.id) + + def uninstall(self): logger.info(f"clearing a callback for buffer: {self.id}") self.buffctl.clear_callback() self.buffctl.stop() @@ -96,15 +100,14 @@ class VirtualBuffer: self.view.close(onclose) - def __hash__(self) -> int: - return hash(self.id) - - def sync(self): + def sync(self, text_listener): promise = self.buffctl.content() def defer_sync(promise): content = promise.wait() + safe_listener_detach(text_listener) populate_view(self.view, content) + safe_listener_attach(text_listener, self.view.buffer()) sublime.set_timeout_async(lambda: defer_sync(promise)) diff --git a/src/client.py b/src/client.py index 5cd94b3..3a2bbe6 100644 --- a/src/client.py +++ b/src/client.py @@ -21,8 +21,8 @@ logger = logging.getLogger(__name__) # bidir: VirtualBuffer <-> VirtualWorkspace # bidir: VirtualBuffer <-> Sublime.View # bidir: VirtualWorkspace <-> Sublime.Window -def log_async(msg): - sublime.set_timeout_async(lambda: logger.log(logger.level, msg)) +# def log_async(msg): +# sublime.set_timeout_async(lambda: logger.log(logger.level, msg)) class VirtualClient: @@ -36,11 +36,12 @@ class VirtualClient: self._view2buff: dict[sublime.View, VirtualBuffer] = {} self._buff2workspace: bidict[VirtualBuffer, VirtualWorkspace] = bidict() - # self._workspace2window: bidict[VirtualWorkspace, sublime.Window] = bidict() self._workspace2window: dict[VirtualWorkspace, sublime.Window] = bidict() def dump(self): logger.debug("CLIENT STATUS:") + logger.debug(f"codemp: {self.codemp is not None}") + logger.debug(f"drived: {self.driver is not None}") logger.debug("WORKSPACES:") logger.debug(f"{self._id2workspace}") logger.debug(f"{self._workspace2window}") @@ -103,6 +104,7 @@ class VirtualClient: logger.info("disconnecting from the current client") # for each workspace tell it to clean up after itself. for vws in self.all_workspaces(): + self.uninstall_workspace(vws) self.codemp.leave_workspace(vws.id) self._id2workspace.clear() @@ -110,6 +112,9 @@ class VirtualClient: self._buff2workspace.clear() self._view2buff.clear() self._workspace2window.clear() + + self.driver.stop() + self.driver = None self.codemp = None def connect(self, host: str, user: str, password: str): @@ -119,7 +124,11 @@ class VirtualClient: if self.driver is None: self.driver = codemp.init() - codemp.set_logger(log_async, False) + logger.debug("registering logger callback...") + if not codemp.set_logger(lambda msg: logger.debug(msg), False): + logger.debug( + "could not register the logger... If reconnecting it's ok, the previous logger is still registered" + ) self.codemp = codemp.connect(host, user, password).wait() id = self.codemp.user_id() @@ -140,7 +149,8 @@ class VirtualClient: del self._id2workspace[vws.id] for vbuff in self.all_buffers(vws): self.unregister_buffer(vbuff) - del vws + + vws.uninstall() # self._buff2workspace.inverse_del(vws) - if we delete all straight # keys the last delete will remove also the empty key. diff --git a/src/workspace.py b/src/workspace.py index bf446af..e16c19d 100644 --- a/src/workspace.py +++ b/src/workspace.py @@ -9,7 +9,7 @@ import logging import codemp from Codemp.src import globals as g from Codemp.src.buffers import VirtualBuffer -from Codemp.src.utils import draw_cursor_region +from Codemp.src.utils import draw_cursor_region, safe_listener_attach, sublime_plugin from Codemp.src.utils import bidict @@ -51,7 +51,6 @@ class VirtualWorkspace: self.codemp.fetch_buffers() self.codemp.fetch_users() - self._buff2view: bidict[VirtualBuffer, sublime.View] = bidict() self._id2buff: dict[str, VirtualBuffer] = {} tmpdir = tempfile.mkdtemp(prefix="codemp_") @@ -71,10 +70,25 @@ class VirtualWorkspace: self.isactive = True def __del__(self): + logger.debug("workspace destroyed!") + + def __hash__(self) -> int: + # so we can use these as dict keys! + return hash(self.id) + + def uninstall(self): self.curctl.clear_callback() self.isactive = False self.curctl.stop() + for vbuff in self._id2buff.values(): + vbuff.uninstall() + if not self.codemp.detach(vbuff.id): + logger.warning( + f"could not detach from '{vbuff.id}' for workspace '{self.id}'." + ) + self._id2buff.clear() + proj: dict = self.window.project_data() # type:ignore if proj is None: raise @@ -91,34 +105,29 @@ class VirtualWorkspace: logger.info(f"cleaning up virtual workspace '{self.id}'") shutil.rmtree(self.rootdir, ignore_errors=True) - if not all(self.codemp.detach(buff) for buff in self._id2buff.keys()): - logger.warning( - f"could not detach from all buffers for workspace '{self.id}'." - ) - self._id2buff.clear() - - def __hash__(self) -> int: - # so we can use these as dict keys! - return hash(self.id) - def all_buffers(self) -> list[VirtualBuffer]: return list(self._id2buff.values()) def buff_by_id(self, id: str) -> Optional[VirtualBuffer]: return self._id2buff.get(id) - def install_buffer(self, buff: codemp.BufferController) -> VirtualBuffer: + def install_buffer( + self, buff: codemp.BufferController, listener: sublime_plugin.TextChangeListener + ) -> VirtualBuffer: logger.debug(f"installing buffer {buff.name()}") view = self.window.new_file() vbuff = VirtualBuffer(buff, view, self.rootdir) self._id2buff[vbuff.id] = vbuff + vbuff.sync(listener) + return vbuff def uninstall_buffer(self, vbuff: VirtualBuffer): del self._id2buff[vbuff.id] self.codemp.detach(vbuff.id) + vbuff.uninstall() def send_cursor(self, id: str, start: Tuple[int, int], end: Tuple[int, int]): # we can safely ignore the promise, we don't really care if everything