chore: adapting plugin to new glue + new glue

Former-commit-id: 1ed7e6f519295e7f46b4bce3b5d3143e787b26a3
This commit is contained in:
cschen 2024-08-09 09:17:38 +02:00
parent 6dc8d17e9d
commit 73e8e9c061
3 changed files with 215 additions and 89 deletions

View file

@ -1 +1 @@
8d0c5dec04835cf686c98fd9666f20b981450d6e def6ad401dc56ab672f6c77a3e0dada48efd8f9e

View file

@ -2,6 +2,7 @@
import sublime import sublime
import sublime_plugin import sublime_plugin
import random
# import os # import os
# import sys # import sys
@ -15,6 +16,7 @@ from .src.utils import safe_listener_detach
from .src.utils import safe_listener_attach from .src.utils import safe_listener_attach
from .src import globals as g from .src import globals as g
TEXT_LISTENER = None TEXT_LISTENER = None
# Initialisation and Deinitialisation # Initialisation and Deinitialisation
@ -29,6 +31,7 @@ def plugin_loaded():
tm.acquire(disconnect_client) tm.acquire(disconnect_client)
logger = CodempLogger() logger = CodempLogger()
tm.dispatch(logger.log(), "codemp-logger") tm.dispatch(logger.log(), "codemp-logger")
TEXT_LISTENER = CodempClientTextChangeListener() TEXT_LISTENER = CodempClientTextChangeListener()
@ -154,6 +157,8 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
# and buffer id # and buffer id
# codemp_join_workspace: joins a specific workspace, without joining also a buffer # 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_join_buffer: joins a specific buffer within the current active workspace
# codemp_share: ??? todo!() # codemp_share: ??? todo!()
# codemp_disconnect: manually call the disconnection, triggering the cleanup and dropping # codemp_disconnect: manually call the disconnection, triggering the cleanup and dropping
# the connection # the connection
@ -164,21 +169,48 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
# Connect Command # Connect Command
############################################################################# #############################################################################
class CodempConnectCommand(sublime_plugin.WindowCommand): class CodempConnectCommand(sublime_plugin.WindowCommand):
def run(self, server_host): def run(self, server_host, user_name, password="lmaodefaultpassword"):
tm.dispatch(client.connect(server_host)) client.connect(server_host, user_name, password)
def input(self, args): def input(self, args):
if "server_host" not in args: if "server_host" not in args:
return ServerHost() return ConnectServerHost()
def input_description(self): def input_description(self):
return "Server host:" 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()
class ConnectUserName(sublime_plugin.TextInputHandler):
def name(self):
return "user_name"
def initial_text(self):
return f"user-{random.random()}"
# Generic Join Command # Generic Join Command
############################################################################# #############################################################################
async def JoinCommand(client: VirtualClient, workspace_id: str, buffer_id: str): async def JoinCommand(client: VirtualClient, workspace_id: str, buffer_id: str):
if workspace_id is None:
return
vws = await client.join_workspace(workspace_id) vws = await client.join_workspace(workspace_id)
if buffer_id is None:
return
if vws is not None: if vws is not None:
await vws.attach(buffer_id) await vws.attach(buffer_id)
@ -195,6 +227,32 @@ class CodempJoinCommand(sublime_plugin.WindowCommand):
return WorkspaceIdAndFollowup() return WorkspaceIdAndFollowup()
class WorkspaceIdAndFollowup(sublime_plugin.ListInputHandler):
def name(self):
return "workspace_id"
def placeholder(self):
return "Workspace Id"
def list_items(self):
return client.active_workspaces()
def next_input(self, args):
if "buffer_id" not in args:
return ListBufferId()
class ListBufferId(sublime_plugin.ListInputHandler):
def name(self):
return "buffer_id"
def placeholder(self):
return "Buffer Id"
def list_items(self):
return client.active_workspace.handle.filetree()
# Join Workspace Command # Join Workspace Command
############################################################################# #############################################################################
class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand): class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
@ -239,7 +297,7 @@ class CodempJoinBufferCommand(sublime_plugin.WindowCommand):
if len(existing_buffers) == 0: if len(existing_buffers) == 0:
return RawBufferId() return RawBufferId()
else: else:
return ListBufferId() return ListBufferId2()
# Text Change Command # Text Change Command
@ -253,21 +311,15 @@ class CodempReplaceTextCommand(sublime_plugin.TextCommand):
# Input Handlers # Input Handlers
############################################################################## ##############################################################################
class ServerHost(sublime_plugin.TextInputHandler):
def name(self):
return "server_host"
def initial_text(self):
return "http://127.0.0.1:50051"
class ListBufferId(sublime_plugin.ListInputHandler): class ListBufferId2(sublime_plugin.ListInputHandler):
def name(self): def name(self):
return "buffer_id" return "buffer_id"
def list_items(self): def list_items(self):
assert client.active_workspace is not None assert client.active_workspace is not None
return client.active_workspace.handle.filetree() return client.active_workspace
def next_input(self, args): def next_input(self, args):
if "buffer_id" not in args: if "buffer_id" not in args:
@ -282,18 +334,6 @@ class RawWorkspaceId(sublime_plugin.TextInputHandler):
return "Workspace Id" return "Workspace Id"
class WorkspaceIdAndFollowup(sublime_plugin.TextInputHandler):
def name(self):
return "workspace_id"
def placeholder(self):
return "Workspace Id"
def next_input(self, args):
if "buffer_id" not in args:
return RawBufferId()
class RawBufferId(sublime_plugin.TextInputHandler): class RawBufferId(sublime_plugin.TextInputHandler):
def name(self): def name(self):
return "buffer_id" return "buffer_id"

View file

@ -9,12 +9,12 @@ import os
import shutil import shutil
from codemp import ( from codemp import (
init_logger, BufferController,
codemp_init, Workspace,
CodempBufferController,
CodempWorkspace,
Client, Client,
PyLogger,
) )
from sublime_plugin import attach_buffer
from ..src import globals as g from ..src import globals as g
from ..src.TaskManager import tm from ..src.TaskManager import tm
from ..src.utils import status_log, rowcol_to_region from ..src.utils import status_log, rowcol_to_region
@ -22,12 +22,15 @@ from ..src.utils import status_log, rowcol_to_region
class CodempLogger: class CodempLogger:
def __init__(self, debug: bool = False): def __init__(self, debug: bool = False):
self.handle = init_logger(debug) try:
self.handle = PyLogger(debug)
except Exception:
pass
async def log(self): async def log(self):
status_log("spinning up the logger...") status_log("spinning up the logger...")
try: try:
while msg := await self.handle.message(): while msg := await self.handle.listen():
print(msg) print(msg)
except asyncio.CancelledError: except asyncio.CancelledError:
status_log("stopping logger") status_log("stopping logger")
@ -46,7 +49,7 @@ class VirtualBuffer:
self, self,
workspace: VirtualWorkspace, workspace: VirtualWorkspace,
remote_id: str, remote_id: str,
buffctl: CodempBufferController, buffctl: BufferController,
): ):
self.view = sublime.active_window().new_file() self.view = sublime.active_window().new_file()
self.codemp_id = remote_id self.codemp_id = remote_id
@ -90,6 +93,7 @@ class VirtualBuffer:
status_log(f"spinning up '{self.codemp_id}' buffer worker...") status_log(f"spinning up '{self.codemp_id}' buffer worker...")
try: try:
while text_change := await self.buffctl.recv(): while text_change := await self.buffctl.recv():
change_id = self.view.change_id()
if text_change.is_empty(): if text_change.is_empty():
status_log("change is empty. skipping.") status_log("change is empty. skipping.")
continue continue
@ -105,10 +109,10 @@ class VirtualBuffer:
self.view.run_command( self.view.run_command(
"codemp_replace_text", "codemp_replace_text",
{ {
"start": text_change.start_incl, "start": text_change.start,
"end": text_change.end_excl, "end": text_change.end,
"content": text_change.content, "content": text_change.content,
"change_id": self.view.change_id(), "change_id": change_id,
}, # pyright: ignore }, # pyright: ignore
) )
@ -129,9 +133,7 @@ class VirtualBuffer:
region.begin(), region.end(), change.str region.begin(), region.end(), change.str
) )
) )
self.buffctl.send( self.buffctl.send(region.begin(), region.end(), change.str)
region.begin(), region.end() + len(change.str) - 1, change.str
)
def send_cursor(self, vws: VirtualWorkspace): def send_cursor(self, vws: VirtualWorkspace):
# TODO: only the last placed cursor/selection. # TODO: only the last placed cursor/selection.
@ -146,10 +148,10 @@ class VirtualBuffer:
# A virtual workspace is a bridge class that aims to translate # A virtual workspace is a bridge class that aims to translate
# events that happen to the codemp workspaces into sublime actions # events that happen to the codemp workspaces into sublime actions
class VirtualWorkspace: class VirtualWorkspace:
def __init__(self, workspace_id: str, handle: CodempWorkspace): def __init__(self, handle: Workspace):
self.id = workspace_id
self.sublime_window = sublime.active_window()
self.handle = handle self.handle = handle
self.id = self.handle.id()
self.sublime_window = sublime.active_window()
self.curctl = handle.cursor() self.curctl = handle.cursor()
self.isactive = False self.isactive = False
@ -201,6 +203,8 @@ 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)
self.curctl.stop()
s = self.sublime_window.settings() s = self.sublime_window.settings()
del s[g.CODEMP_WINDOW_TAG] del s[g.CODEMP_WINDOW_TAG]
del s[g.CODEMP_WINDOW_WORKSPACES] del s[g.CODEMP_WINDOW_WORKSPACES]
@ -232,29 +236,49 @@ class VirtualWorkspace:
vbuff = self.active_buffers.get(local_id) vbuff = self.active_buffers.get(local_id)
if vbuff is None: if vbuff is None:
status_log( status_log(
"[WARN] a local-remote buffer id pair was found but not the matching virtual buffer." "[WARN] a local-remote buffer id pair was found but \
not the matching virtual buffer."
) )
return return
return vbuff return vbuff
# A workspace has some buffers inside of it (filetree)
# some of those you are already attached to (buffers_by_name)
# If already attached to it return the same alredy existing bufferctl
# if existing but not attached (attach)
# if not existing ask for creation (create + attach)
async def attach(self, id: str): async def attach(self, id: str):
if id is None: if id is None:
return return
attached_buffers = self.handle.buffer_by_name(id)
if attached_buffers is not None:
return self.get_by_remote(id)
await self.handle.fetch_buffers() await self.handle.fetch_buffers()
existing_buffers = self.handle.filetree() existing_buffers = self.handle.filetree()
if id not in existing_buffers: if id not in existing_buffers:
try: create = sublime.ok_cancel_dialog(
await self.handle.create(id) "There is no buffer named '{id}' in the workspace.\n\
except Exception as e: Do you want to create it?",
status_log(f"could not create buffer: {e}") ok_title="yes",
title="Create Buffer?",
)
if create:
try:
await self.handle.create(id)
except Exception as e:
status_log(f"could not create buffer:\n\n {e}", True)
return
else:
return return
# now either we created it or it exists already
try: try:
buff_ctl = await self.handle.attach(id) buff_ctl = await self.handle.attach(id)
except Exception as e: except Exception as e:
status_log(f"error when attaching to buffer '{id}': {e}") status_log(f"error when attaching to buffer '{id}':\n\n {e}", True)
return return
vbuff = VirtualBuffer(self, id, buff_ctl) vbuff = VirtualBuffer(self, id, buff_ctl)
@ -263,6 +287,59 @@ class VirtualWorkspace:
# TODO! if the view is already active calling focus_view() will not trigger the on_activate # TODO! if the view is already active calling focus_view() will not trigger the on_activate
self.sublime_window.focus_view(vbuff.view) self.sublime_window.focus_view(vbuff.view)
def detach(self, id: str):
if id is None:
return
attached_buffers = self.handle.buffer_by_name(id)
if attached_buffers is None:
status_log(f"You are not attached to the buffer '{id}'", True)
return
self.handle.detach(id)
async def delete(self, id: str):
if id is None:
return
# delete a non existent buffer
await self.handle.fetch_buffers()
existing_buffers = self.handle.filetree()
if id not in existing_buffers:
status_log(f"The buffer '{id}' does not exists.", True)
return
# delete a buffer that exists but you are not attached to
attached_buffers = self.handle.buffer_by_name(id)
if attached_buffers is None:
delete = sublime.ok_cancel_dialog(
"Confirm you want to delete the buffer '{id}'",
ok_title="delete",
title="Delete Buffer?",
)
if delete:
try:
await self.handle.delete(id)
except Exception as e:
status_log(f"error when deleting the buffer '{id}':\n\n {e}", True)
return
else:
return
# delete buffer that you are attached to
delete = sublime.ok_cancel_dialog(
"Confirm you want to delete the buffer '{id}'.\n\
You will be disconnected from it.",
ok_title="delete",
title="Delete Buffer?",
)
if delete:
self.detach(id)
try:
await self.handle.delete(id)
except Exception as e:
status_log(f"error when deleting the buffer '{id}':\n\n {e}", True)
return
async def move_cursor_task(self): async def move_cursor_task(self):
status_log(f"spinning up cursor worker for workspace '{self.id}'...") status_log(f"spinning up cursor worker for workspace '{self.id}'...")
try: try:
@ -295,13 +372,57 @@ class VirtualWorkspace:
class VirtualClient: class VirtualClient:
def __init__(self): def __init__(self):
self.handle: Client = codemp_init() self.handle = None
self.workspaces: dict[str, VirtualWorkspace] = {} self.workspaces: dict[str, VirtualWorkspace] = {}
self.active_workspace: Optional[VirtualWorkspace] = None self.active_workspace: Optional[VirtualWorkspace] = None
def __getitem__(self, key: str): def __getitem__(self, key: str):
return self.workspaces.get(key) return self.workspaces.get(key)
def connect(self, host: str, user: str, password: str):
status_log(f"Connecting to {host} with user {user}")
try:
self.handle = Client(host, user, password)
except Exception as e:
sublime.error_message(
f"Could not connect:\n Make sure the server is up.\n\
or your credentials are correct\n\nerror: {e}"
)
return
id = self.handle.user_id()
status_log(f"Connected to '{host}' with user {user} and id: {id}")
async def join_workspace(
self,
workspace_id: str,
) -> Optional[VirtualWorkspace]:
if self.handle is None:
status_log("Connect to a server first!", True)
return
status_log(f"Joining workspace: '{workspace_id}'")
try:
workspace = await self.handle.join_workspace(workspace_id)
except Exception as e:
status_log(
f"Could not join workspace '{workspace_id}'.\n\nerror: {e}", True
)
return
vws = VirtualWorkspace(workspace)
self.workspaces[workspace_id] = vws
self.make_active(vws)
return vws
def leave_workspace(self, id: str):
if self.handle is None:
status_log("Connect to a server first!", True)
return False
status_log(f"Leaving workspace: '{id}'")
return self.handle.leave_workspace(id)
def get_workspace(self, view): def get_workspace(self, view):
tag_id = view.settings().get(g.CODEMP_WORKSPACE_ID) tag_id = view.settings().get(g.CODEMP_WORKSPACE_ID)
if tag_id is None: if tag_id is None:
@ -316,6 +437,12 @@ class VirtualClient:
return ws return ws
def active_workspaces(self):
return self.handle.active_workspaces() if self.handle else []
def user_id(self):
return self.handle.user_id() if self.handle else None
def get_buffer(self, view): def get_buffer(self, view):
ws = self.get_workspace(view) ws = self.get_workspace(view)
return None if ws is None else ws.get_by_local(view.buffer_id()) return None if ws is None else ws.get_by_local(view.buffer_id())
@ -332,46 +459,5 @@ class VirtualClient:
self.active_workspace = ws self.active_workspace = ws
async def connect(self, server_host: str):
status_log(f"Connecting to {server_host}")
try:
await self.handle.connect(server_host)
except Exception as e:
sublime.error_message(
f"Could not connect:\n Make sure the server is up.\nerror: {e}"
)
return
id = await self.handle.user_id()
status_log(f"Connected to '{server_host}' with user id: {id}")
async def join_workspace(
self,
workspace_id: str,
user=f"user-{random.random()}",
password="lmaodefaultpassword",
) -> Optional[VirtualWorkspace]:
try:
status_log(f"Logging into workspace: '{workspace_id}' with user: {user}")
await self.handle.login(user, password, workspace_id)
except Exception as e:
status_log(
f"Failed to login to workspace '{workspace_id}'.\nerror: {e}", True
)
return
try:
status_log(f"Joining workspace: '{workspace_id}'")
workspace_handle = await self.handle.join_workspace(workspace_id)
except Exception as e:
status_log(f"Could not join workspace '{workspace_id}'.\nerror: {e}", True)
return
vws = VirtualWorkspace(workspace_id, workspace_handle)
self.workspaces[workspace_id] = vws
self.make_active(vws)
return vws
client = VirtualClient() client = VirtualClient()