mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-11-21 22:34:48 +01:00
fix: properly handle the cleanup of workspaces when closing a window containing them. as well as tagging a window similarly to how we tag views that contain a buffer managed by codemp.
fix: TaskManager, properly implement the stopping logic: Tasks in asyncio when .cancel()'ed are not immediatelly stopped. but only a request of cancellation is made. subsequently awaiting the task allows it to raise a cancelledError exception, do any necessary cleanup (finally block). chore: removed unused import in lib. Former-commit-id: 04f294c50b180e3676fd026d9a47732cdf6511a6
This commit is contained in:
parent
8a67c7ce93
commit
a26a51cecf
5 changed files with 76 additions and 29 deletions
23
plugin.py
23
plugin.py
|
@ -38,8 +38,6 @@ async def disconnect_client():
|
||||||
for vws in CLIENT.workspaces.values():
|
for vws in CLIENT.workspaces.values():
|
||||||
vws.cleanup()
|
vws.cleanup()
|
||||||
|
|
||||||
CLIENT = None
|
|
||||||
|
|
||||||
|
|
||||||
def plugin_unloaded():
|
def plugin_unloaded():
|
||||||
global CLIENT
|
global CLIENT
|
||||||
|
@ -51,10 +49,26 @@ def plugin_unloaded():
|
||||||
# Listeners
|
# Listeners
|
||||||
##############################################################################
|
##############################################################################
|
||||||
class EventListener(sublime_plugin.EventListener):
|
class EventListener(sublime_plugin.EventListener):
|
||||||
def on_exit(self) -> None:
|
def on_exit(self):
|
||||||
global CLIENT
|
global CLIENT
|
||||||
CLIENT.tm.release(True)
|
CLIENT.tm.release(True)
|
||||||
|
|
||||||
|
def on_pre_close_window(self, window):
|
||||||
|
global CLIENT
|
||||||
|
s = window.settings()
|
||||||
|
if s.get(g.CODEMP_WINDOW_TAG, False):
|
||||||
|
for wsid in s[g.CODEMP_WINDOW_WORKSPACES]:
|
||||||
|
ws = CLIENT[wsid]
|
||||||
|
if ws is not None:
|
||||||
|
status_log(
|
||||||
|
f"current active: {CLIENT.active_workspace.id}, ws = {ws.id}"
|
||||||
|
)
|
||||||
|
if ws.id == CLIENT.active_workspace.id:
|
||||||
|
CLIENT.active_workspace = None
|
||||||
|
CLIENT.tm.stop(f"{g.CURCTL_TASK_PREFIX}-{ws.id}")
|
||||||
|
ws.cleanup()
|
||||||
|
del CLIENT.workspaces[wsid]
|
||||||
|
|
||||||
|
|
||||||
class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
|
class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -102,7 +116,7 @@ class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
|
||||||
vbuff = CLIENT[wsid].get_by_local(self.view.buffer_id())
|
vbuff = CLIENT[wsid].get_by_local(self.view.buffer_id())
|
||||||
vbuff.cleanup()
|
vbuff.cleanup()
|
||||||
|
|
||||||
CLIENT.tm.stop_and_pop(f"{g.BUFFCTL_TASK_PREFIX}-{vbuff.codemp_id}")
|
CLIENT.tm.stop(f"{g.BUFFCTL_TASK_PREFIX}-{vbuff.codemp_id}")
|
||||||
|
|
||||||
|
|
||||||
class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
||||||
|
@ -283,6 +297,7 @@ class RawBufferId(sublime_plugin.TextInputHandler):
|
||||||
def placeholder(self):
|
def placeholder(self):
|
||||||
return "Buffer Id"
|
return "Buffer Id"
|
||||||
|
|
||||||
|
|
||||||
# Share Command
|
# Share Command
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# class CodempShareCommand(sublime_plugin.WindowCommand):
|
# class CodempShareCommand(sublime_plugin.WindowCommand):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
import asyncio
|
||||||
import Codemp.ext.sublime_asyncio as rt
|
import Codemp.ext.sublime_asyncio as rt
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,17 +17,18 @@ class TaskManager:
|
||||||
def sync(self, coro):
|
def sync(self, coro):
|
||||||
rt.sync(coro)
|
rt.sync(coro)
|
||||||
|
|
||||||
def store(self, task):
|
def remove_stopped(self):
|
||||||
self.tasks.append(task)
|
self.tasks = list(filter(lambda T: not T.cancelled(), self.tasks))
|
||||||
|
|
||||||
def store_named(self, task, name=None):
|
def store(self, task, name=None):
|
||||||
|
if name is not None:
|
||||||
task.set_name(name)
|
task.set_name(name)
|
||||||
self.store(task)
|
self.tasks.append(task)
|
||||||
|
self.remove_stopped()
|
||||||
|
|
||||||
def store_named_lambda(self, name):
|
def store_named_lambda(self, name):
|
||||||
def _store(task):
|
def _store(task):
|
||||||
task.set_name(name)
|
self.store(task, name)
|
||||||
self.store(task)
|
|
||||||
|
|
||||||
return _store
|
return _store
|
||||||
|
|
||||||
|
@ -44,20 +46,20 @@ class TaskManager:
|
||||||
return self.task.pop(idx)
|
return self.task.pop(idx)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
async def _stop(self, task):
|
||||||
|
task.cancel() # cancelling a task, merely requests a cancellation.
|
||||||
|
try:
|
||||||
|
await task
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
return
|
||||||
|
|
||||||
def stop(self, name):
|
def stop(self, name):
|
||||||
t = self.get_task(name)
|
t = self.get_task(name)
|
||||||
if t is not None:
|
if t is not None:
|
||||||
t.cancel()
|
rt.sync(
|
||||||
|
self._stop(t)
|
||||||
def stop_and_pop(self, name) -> Optional:
|
) # awaiting the task blocks until it actually is finished.
|
||||||
idx, task = next(
|
|
||||||
((i, t) for (i, t) in enumerate(self.tasks) if t.get_name() == name),
|
|
||||||
(None, None),
|
|
||||||
)
|
|
||||||
if idx is not None:
|
|
||||||
task.cancel()
|
|
||||||
return self.tasks.pop(idx)
|
|
||||||
|
|
||||||
def stop_all(self):
|
def stop_all(self):
|
||||||
for task in self.tasks:
|
for task in self.tasks:
|
||||||
task.cancel()
|
rt.sync(self._stop(task))
|
||||||
|
|
|
@ -73,7 +73,7 @@ class VirtualWorkspace:
|
||||||
|
|
||||||
# mapping remote ids -> local ids
|
# mapping remote ids -> local ids
|
||||||
self.id_map: dict[str, str] = {}
|
self.id_map: dict[str, str] = {}
|
||||||
self.active_buffers: dict[str, VirtualBuffer] = {}
|
self.active_buffers: dict[str, VirtualBuffer] = {} # local_id -> VBuff
|
||||||
|
|
||||||
# initialise the virtual filesystem
|
# initialise the virtual filesystem
|
||||||
tmpdir = tempfile.mkdtemp(prefix="codemp_")
|
tmpdir = tempfile.mkdtemp(prefix="codemp_")
|
||||||
|
@ -90,6 +90,13 @@ class VirtualWorkspace:
|
||||||
)
|
)
|
||||||
self.sublime_window.set_project_data(proj_data)
|
self.sublime_window.set_project_data(proj_data)
|
||||||
|
|
||||||
|
s: dict = self.sublime_window.settings()
|
||||||
|
if s.get(g.CODEMP_WINDOW_TAG, False):
|
||||||
|
s[g.CODEMP_WINDOW_WORKSPACES].append(self.id)
|
||||||
|
else:
|
||||||
|
s[g.CODEMP_WINDOW_TAG] = True
|
||||||
|
s[g.CODEMP_WINDOW_WORKSPACES] = [self.id]
|
||||||
|
|
||||||
def add_buffer(self, remote_id: str, vbuff: VirtualBuffer):
|
def add_buffer(self, remote_id: str, vbuff: VirtualBuffer):
|
||||||
self.id_map[remote_id] = vbuff.view.buffer_id()
|
self.id_map[remote_id] = vbuff.view.buffer_id()
|
||||||
self.active_buffers[vbuff.view.buffer_id()] = vbuff
|
self.active_buffers[vbuff.view.buffer_id()] = vbuff
|
||||||
|
@ -100,6 +107,8 @@ class VirtualWorkspace:
|
||||||
for vbuff in self.active_buffers.values():
|
for vbuff in self.active_buffers.values():
|
||||||
vbuff.view.close()
|
vbuff.view.close()
|
||||||
|
|
||||||
|
self.active_buffers = {} # drop all buffers, let them be garbace collected (hopefully)
|
||||||
|
|
||||||
d = self.sublime_window.project_data()
|
d = self.sublime_window.project_data()
|
||||||
newf = list(
|
newf = list(
|
||||||
filter(
|
filter(
|
||||||
|
@ -112,6 +121,10 @@ class VirtualWorkspace:
|
||||||
status_log(f"cleaning up virtual workspace '{self.id}'")
|
status_log(f"cleaning up virtual workspace '{self.id}'")
|
||||||
shutil.rmtree(self.rootdir, ignore_errors=True)
|
shutil.rmtree(self.rootdir, ignore_errors=True)
|
||||||
|
|
||||||
|
s = self.sublime_window.settings()
|
||||||
|
del s[g.CODEMP_WINDOW_TAG]
|
||||||
|
del s[g.CODEMP_WINDOW_WORKSPACES]
|
||||||
|
|
||||||
def get_by_local(self, local_id: str) -> Optional[VirtualBuffer]:
|
def get_by_local(self, local_id: str) -> Optional[VirtualBuffer]:
|
||||||
return self.active_buffers.get(local_id)
|
return self.active_buffers.get(local_id)
|
||||||
|
|
||||||
|
@ -171,7 +184,9 @@ class VirtualClient:
|
||||||
try:
|
try:
|
||||||
await self.handle.connect(server_host)
|
await self.handle.connect(server_host)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sublime.error_message(f"Could not connect:\n Make sure the server is up.\nerror: {e}")
|
sublime.error_message(
|
||||||
|
f"Could not connect:\n Make sure the server is up.\nerror: {e}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
id = await self.handle.user_id()
|
id = await self.handle.user_id()
|
||||||
|
@ -185,7 +200,9 @@ class VirtualClient:
|
||||||
await self.handle.login(user, password, workspace_id)
|
await self.handle.login(user, password, workspace_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
status_log(f"Failed to login to workspace '{workspace_id}'.\nerror: {e}")
|
status_log(f"Failed to login to workspace '{workspace_id}'.\nerror: {e}")
|
||||||
sublime.error_message(f"Failed to login to workspace '{workspace_id}'.\nerror: {e}")
|
sublime.error_message(
|
||||||
|
f"Failed to login to workspace '{workspace_id}'.\nerror: {e}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -193,7 +210,9 @@ class VirtualClient:
|
||||||
workspace_handle = await self.handle.join_workspace(workspace_id)
|
workspace_handle = await self.handle.join_workspace(workspace_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
status_log(f"Could not join workspace '{workspace_id}'.\nerror: {e}")
|
status_log(f"Could not join workspace '{workspace_id}'.\nerror: {e}")
|
||||||
sublime.error_message(f"Could not join workspace '{workspace_id}'.\nerror: {e}")
|
sublime.error_message(
|
||||||
|
f"Could not join workspace '{workspace_id}'.\nerror: {e}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
vws = VirtualWorkspace(self, workspace_id, workspace_handle)
|
vws = VirtualWorkspace(self, workspace_id, workspace_handle)
|
||||||
|
@ -233,7 +252,10 @@ class VirtualClient:
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
status_log(f"cursor worker for '{vws.id}' stopped...")
|
status_log(f"cursor worker for '{vws.id}' stopped...")
|
||||||
return
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
status_log(f"cursor worker '{vws.id}' crashed:\n{e}")
|
||||||
|
raise
|
||||||
|
|
||||||
self.tm.dispatch(
|
self.tm.dispatch(
|
||||||
move_cursor_task(virtual_workspace),
|
move_cursor_task(virtual_workspace),
|
||||||
|
@ -280,7 +302,11 @@ class VirtualClient:
|
||||||
)
|
)
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
status_log("'{}' buffer worker stopped...".format(vb.codemp_id))
|
status_log(f"'{vb.codemp_id}' buffer worker stopped...")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
status_log(f"buffer worker '{vb.codemp_id}' crashed:\n{e}")
|
||||||
|
raise
|
||||||
|
|
||||||
self.tm.dispatch(
|
self.tm.dispatch(
|
||||||
apply_buffer_change_task(vbuff),
|
apply_buffer_change_task(vbuff),
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
BUFFCTL_TASK_PREFIX = "buffer-ctl"
|
BUFFCTL_TASK_PREFIX = "buffer-ctl"
|
||||||
CURCTL_TASK_PREFIX = "cursor-ctl"
|
CURCTL_TASK_PREFIX = "cursor-ctl"
|
||||||
|
|
||||||
CODEMP_BUFFER_TAG = "codemp-buffer"
|
CODEMP_BUFFER_TAG = "codemp-buffer"
|
||||||
CODEMP_REMOTE_ID = "codemp-buffer-id"
|
CODEMP_REMOTE_ID = "codemp-buffer-id"
|
||||||
CODEMP_WORKSPACE_ID = "codemp-workspace-id"
|
CODEMP_WORKSPACE_ID = "codemp-workspace-id"
|
||||||
|
|
||||||
|
CODEMP_WINDOW_TAG = "codemp-window"
|
||||||
|
CODEMP_WINDOW_WORKSPACES = "codemp-workspaces"
|
||||||
|
|
||||||
WORKSPACE_FOLDER_PREFIX = "CODEMP::"
|
WORKSPACE_FOLDER_PREFIX = "CODEMP::"
|
||||||
SUBLIME_REGIONS_PREFIX = "codemp-cursors"
|
SUBLIME_REGIONS_PREFIX = "codemp-cursors"
|
||||||
SUBLIME_STATUS_ID = "z_codemp_buffer"
|
SUBLIME_STATUS_ID = "z_codemp_buffer"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use codemp::proto::common::Identity;
|
use codemp::proto::common::Identity;
|
||||||
use pyo3::types::PyList;
|
use pyo3::types::PyList;
|
||||||
use std::{format, ops::Deref, sync::Arc};
|
use std::{format, sync::Arc};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use codemp::prelude::*;
|
use codemp::prelude::*;
|
||||||
|
|
Loading…
Reference in a new issue