mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-12-23 13:14:52 +01:00
chore: adapting plugin to new glue + new glue
Former-commit-id: 1ed7e6f519295e7f46b4bce3b5d3143e787b26a3
This commit is contained in:
parent
6dc8d17e9d
commit
73e8e9c061
3 changed files with 215 additions and 89 deletions
|
@ -1 +1 @@
|
|||
8d0c5dec04835cf686c98fd9666f20b981450d6e
|
||||
def6ad401dc56ab672f6c77a3e0dada48efd8f9e
|
88
plugin.py
88
plugin.py
|
@ -2,6 +2,7 @@
|
|||
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
import random
|
||||
|
||||
# import os
|
||||
# import sys
|
||||
|
@ -15,6 +16,7 @@ from .src.utils import safe_listener_detach
|
|||
from .src.utils import safe_listener_attach
|
||||
from .src import globals as g
|
||||
|
||||
|
||||
TEXT_LISTENER = None
|
||||
|
||||
# Initialisation and Deinitialisation
|
||||
|
@ -29,6 +31,7 @@ def plugin_loaded():
|
|||
tm.acquire(disconnect_client)
|
||||
|
||||
logger = CodempLogger()
|
||||
|
||||
tm.dispatch(logger.log(), "codemp-logger")
|
||||
|
||||
TEXT_LISTENER = CodempClientTextChangeListener()
|
||||
|
@ -154,6 +157,8 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
|||
# and buffer id
|
||||
# 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_share: ??? todo!()
|
||||
# codemp_disconnect: manually call the disconnection, triggering the cleanup and dropping
|
||||
# the connection
|
||||
|
@ -164,21 +169,48 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
|||
# Connect Command
|
||||
#############################################################################
|
||||
class CodempConnectCommand(sublime_plugin.WindowCommand):
|
||||
def run(self, server_host):
|
||||
tm.dispatch(client.connect(server_host))
|
||||
def run(self, server_host, user_name, password="lmaodefaultpassword"):
|
||||
client.connect(server_host, user_name, password)
|
||||
|
||||
def input(self, args):
|
||||
if "server_host" not in args:
|
||||
return ServerHost()
|
||||
return ConnectServerHost()
|
||||
|
||||
def input_description(self):
|
||||
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
|
||||
#############################################################################
|
||||
async def JoinCommand(client: VirtualClient, workspace_id: str, buffer_id: str):
|
||||
if workspace_id is None:
|
||||
return
|
||||
|
||||
vws = await client.join_workspace(workspace_id)
|
||||
|
||||
if buffer_id is None:
|
||||
return
|
||||
|
||||
if vws is not None:
|
||||
await vws.attach(buffer_id)
|
||||
|
||||
|
@ -195,6 +227,32 @@ class CodempJoinCommand(sublime_plugin.WindowCommand):
|
|||
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
|
||||
#############################################################################
|
||||
class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
|
||||
|
@ -239,7 +297,7 @@ class CodempJoinBufferCommand(sublime_plugin.WindowCommand):
|
|||
if len(existing_buffers) == 0:
|
||||
return RawBufferId()
|
||||
else:
|
||||
return ListBufferId()
|
||||
return ListBufferId2()
|
||||
|
||||
|
||||
# Text Change Command
|
||||
|
@ -253,21 +311,15 @@ class CodempReplaceTextCommand(sublime_plugin.TextCommand):
|
|||
|
||||
# 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):
|
||||
return "buffer_id"
|
||||
|
||||
def list_items(self):
|
||||
assert client.active_workspace is not None
|
||||
return client.active_workspace.handle.filetree()
|
||||
return client.active_workspace
|
||||
|
||||
def next_input(self, args):
|
||||
if "buffer_id" not in args:
|
||||
|
@ -282,18 +334,6 @@ class RawWorkspaceId(sublime_plugin.TextInputHandler):
|
|||
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):
|
||||
def name(self):
|
||||
return "buffer_id"
|
||||
|
|
214
src/client.py
214
src/client.py
|
@ -9,12 +9,12 @@ import os
|
|||
import shutil
|
||||
|
||||
from codemp import (
|
||||
init_logger,
|
||||
codemp_init,
|
||||
CodempBufferController,
|
||||
CodempWorkspace,
|
||||
BufferController,
|
||||
Workspace,
|
||||
Client,
|
||||
PyLogger,
|
||||
)
|
||||
from sublime_plugin import attach_buffer
|
||||
from ..src import globals as g
|
||||
from ..src.TaskManager import tm
|
||||
from ..src.utils import status_log, rowcol_to_region
|
||||
|
@ -22,12 +22,15 @@ from ..src.utils import status_log, rowcol_to_region
|
|||
|
||||
class CodempLogger:
|
||||
def __init__(self, debug: bool = False):
|
||||
self.handle = init_logger(debug)
|
||||
try:
|
||||
self.handle = PyLogger(debug)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def log(self):
|
||||
status_log("spinning up the logger...")
|
||||
try:
|
||||
while msg := await self.handle.message():
|
||||
while msg := await self.handle.listen():
|
||||
print(msg)
|
||||
except asyncio.CancelledError:
|
||||
status_log("stopping logger")
|
||||
|
@ -46,7 +49,7 @@ class VirtualBuffer:
|
|||
self,
|
||||
workspace: VirtualWorkspace,
|
||||
remote_id: str,
|
||||
buffctl: CodempBufferController,
|
||||
buffctl: BufferController,
|
||||
):
|
||||
self.view = sublime.active_window().new_file()
|
||||
self.codemp_id = remote_id
|
||||
|
@ -90,6 +93,7 @@ class VirtualBuffer:
|
|||
status_log(f"spinning up '{self.codemp_id}' buffer worker...")
|
||||
try:
|
||||
while text_change := await self.buffctl.recv():
|
||||
change_id = self.view.change_id()
|
||||
if text_change.is_empty():
|
||||
status_log("change is empty. skipping.")
|
||||
continue
|
||||
|
@ -105,10 +109,10 @@ class VirtualBuffer:
|
|||
self.view.run_command(
|
||||
"codemp_replace_text",
|
||||
{
|
||||
"start": text_change.start_incl,
|
||||
"end": text_change.end_excl,
|
||||
"start": text_change.start,
|
||||
"end": text_change.end,
|
||||
"content": text_change.content,
|
||||
"change_id": self.view.change_id(),
|
||||
"change_id": change_id,
|
||||
}, # pyright: ignore
|
||||
)
|
||||
|
||||
|
@ -129,9 +133,7 @@ class VirtualBuffer:
|
|||
region.begin(), region.end(), change.str
|
||||
)
|
||||
)
|
||||
self.buffctl.send(
|
||||
region.begin(), region.end() + len(change.str) - 1, change.str
|
||||
)
|
||||
self.buffctl.send(region.begin(), region.end(), change.str)
|
||||
|
||||
def send_cursor(self, vws: VirtualWorkspace):
|
||||
# TODO: only the last placed cursor/selection.
|
||||
|
@ -146,10 +148,10 @@ class VirtualBuffer:
|
|||
# A virtual workspace is a bridge class that aims to translate
|
||||
# events that happen to the codemp workspaces into sublime actions
|
||||
class VirtualWorkspace:
|
||||
def __init__(self, workspace_id: str, handle: CodempWorkspace):
|
||||
self.id = workspace_id
|
||||
self.sublime_window = sublime.active_window()
|
||||
def __init__(self, handle: Workspace):
|
||||
self.handle = handle
|
||||
self.id = self.handle.id()
|
||||
self.sublime_window = sublime.active_window()
|
||||
self.curctl = handle.cursor()
|
||||
self.isactive = False
|
||||
|
||||
|
@ -201,6 +203,8 @@ class VirtualWorkspace:
|
|||
status_log(f"cleaning up virtual workspace '{self.id}'")
|
||||
shutil.rmtree(self.rootdir, ignore_errors=True)
|
||||
|
||||
self.curctl.stop()
|
||||
|
||||
s = self.sublime_window.settings()
|
||||
del s[g.CODEMP_WINDOW_TAG]
|
||||
del s[g.CODEMP_WINDOW_WORKSPACES]
|
||||
|
@ -232,29 +236,49 @@ class VirtualWorkspace:
|
|||
vbuff = self.active_buffers.get(local_id)
|
||||
if vbuff is None:
|
||||
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 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):
|
||||
if id is None:
|
||||
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()
|
||||
existing_buffers = self.handle.filetree()
|
||||
if id not in existing_buffers:
|
||||
try:
|
||||
await self.handle.create(id)
|
||||
except Exception as e:
|
||||
status_log(f"could not create buffer: {e}")
|
||||
create = sublime.ok_cancel_dialog(
|
||||
"There is no buffer named '{id}' in the workspace.\n\
|
||||
Do you want to create it?",
|
||||
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
|
||||
|
||||
# now either we created it or it exists already
|
||||
try:
|
||||
buff_ctl = await self.handle.attach(id)
|
||||
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
|
||||
|
||||
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
|
||||
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):
|
||||
status_log(f"spinning up cursor worker for workspace '{self.id}'...")
|
||||
try:
|
||||
|
@ -295,13 +372,57 @@ class VirtualWorkspace:
|
|||
|
||||
class VirtualClient:
|
||||
def __init__(self):
|
||||
self.handle: Client = codemp_init()
|
||||
self.handle = None
|
||||
self.workspaces: dict[str, VirtualWorkspace] = {}
|
||||
self.active_workspace: Optional[VirtualWorkspace] = None
|
||||
|
||||
def __getitem__(self, key: str):
|
||||
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):
|
||||
tag_id = view.settings().get(g.CODEMP_WORKSPACE_ID)
|
||||
if tag_id is None:
|
||||
|
@ -316,6 +437,12 @@ class VirtualClient:
|
|||
|
||||
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):
|
||||
ws = self.get_workspace(view)
|
||||
return None if ws is None else ws.get_by_local(view.buffer_id())
|
||||
|
@ -332,46 +459,5 @@ class VirtualClient:
|
|||
|
||||
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()
|
||||
|
|
Loading…
Reference in a new issue