mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-11-21 22:34:48 +01:00
feat: Stacco nuova version.
feat: Added single Join command chore: separated input handlers and commands, minor cleanup. Former-commit-id: 29a49bd8dbdeaf24f988e0a382e74d7e14d957a8
This commit is contained in:
parent
fa5f03bd6b
commit
8a67c7ce93
5 changed files with 149 additions and 156 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "CodempClient-Sublime"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
@ -26,9 +26,16 @@
|
|||
"command": "codemp_connect",
|
||||
"args": {
|
||||
// "server_host": "http://[::1]:50051"
|
||||
// 'session' : 'default' (the name of the workspace to join)
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Codemp: Join",
|
||||
"command": "codemp_join",
|
||||
"arg": {
|
||||
// 'workspace_id': 'asd'
|
||||
// 'buffer_id': 'test'
|
||||
},
|
||||
},
|
||||
{
|
||||
"caption": "Codemp: Share",
|
||||
"command": "codemp_share",
|
||||
|
@ -39,29 +46,21 @@
|
|||
},
|
||||
{
|
||||
"caption": "Codemp: Join Workspace",
|
||||
"command": "codemp_join",
|
||||
"command": "codemp_join_workspace",
|
||||
"arg": {
|
||||
// 'server_buffer' : 'test'
|
||||
// 'workspace_id' : 'asd'
|
||||
},
|
||||
},
|
||||
{
|
||||
"caption": "Codemp: Join buffer",
|
||||
"command": "codemp_attach",
|
||||
"command": "codemp_join_buffer",
|
||||
"arg": {
|
||||
// 'server_buffer' : 'test'
|
||||
// 'buffer_id' : 'test'
|
||||
},
|
||||
},
|
||||
{
|
||||
"caption": "Codemp: Disconnect Buffer",
|
||||
"command": "codemp_disconnect_buffer",
|
||||
"arg": {
|
||||
// 'remote_name' : 'name of buffer to disconnect'
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Codemp: Disconnect Client",
|
||||
"command": "codemp_disconnect",
|
||||
"arg": {
|
||||
}
|
||||
"arg": {}
|
||||
},
|
||||
]
|
216
plugin.py
216
plugin.py
|
@ -3,7 +3,11 @@ import sublime_plugin
|
|||
|
||||
from Codemp.src.codemp_client import VirtualClient
|
||||
from Codemp.src.TaskManager import rt
|
||||
from Codemp.src.utils import status_log, is_active, safe_listener_detach
|
||||
from Codemp.src.utils import status_log
|
||||
from Codemp.src.utils import safe_listener_detach
|
||||
from Codemp.src.utils import get_contents
|
||||
from Codemp.src.utils import populate_view
|
||||
from Codemp.src.utils import get_view_from_local_path
|
||||
import Codemp.src.globals as g
|
||||
|
||||
CLIENT = None
|
||||
|
@ -34,7 +38,6 @@ async def disconnect_client():
|
|||
for vws in CLIENT.workspaces.values():
|
||||
vws.cleanup()
|
||||
|
||||
# fix me: allow riconnections
|
||||
CLIENT = None
|
||||
|
||||
|
||||
|
@ -45,34 +48,6 @@ def plugin_unloaded():
|
|||
status_log("plugin unloaded")
|
||||
|
||||
|
||||
# Utils
|
||||
##############################################################################
|
||||
|
||||
|
||||
def get_contents(view):
|
||||
r = sublime.Region(0, view.size())
|
||||
return view.substr(r)
|
||||
|
||||
|
||||
def populate_view(view, content):
|
||||
view.run_command(
|
||||
"codemp_replace_text",
|
||||
{
|
||||
"start": 0,
|
||||
"end": view.size(),
|
||||
"content": content,
|
||||
"change_id": view.change_id(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_view_from_local_path(path):
|
||||
for window in sublime.windows():
|
||||
for view in window.views():
|
||||
if view.file_name() == path:
|
||||
return view
|
||||
|
||||
|
||||
# Listeners
|
||||
##############################################################################
|
||||
class EventListener(sublime_plugin.EventListener):
|
||||
|
@ -104,6 +79,8 @@ class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
|
|||
def on_activated(self):
|
||||
global TEXT_LISTENER
|
||||
|
||||
# sublime has no proper way to check if a view gained or lost input focus outside of this
|
||||
# callback (i know right?), so we have to manually keep track of which view has the focus
|
||||
g.ACTIVE_CODEMP_VIEW = self.view.id()
|
||||
print("view {} activated".format(self.view.id()))
|
||||
TEXT_LISTENER.attach(self.view.buffer())
|
||||
|
@ -126,8 +103,6 @@ class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
|
|||
vbuff.cleanup()
|
||||
|
||||
CLIENT.tm.stop_and_pop(f"{g.BUFFCTL_TASK_PREFIX}-{vbuff.codemp_id}")
|
||||
# have to run the detach logic in sync, to keep a valid reference to the view.
|
||||
# sublime_asyncio.sync(buffer.detach(_client))
|
||||
|
||||
|
||||
class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
||||
|
@ -152,8 +127,13 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
|||
|
||||
# Commands:
|
||||
# codemp_connect: connect to a server.
|
||||
# codemp_join: join a workspace with a given name within the server.
|
||||
# codemp_share: shares a buffer with a given name in the workspace.
|
||||
# codemp_join: shortcut command if you already know both workspace id
|
||||
# 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
|
||||
#
|
||||
# Internal commands:
|
||||
# replace_text: swaps the content of a view with the given text.
|
||||
|
@ -167,75 +147,103 @@ class CodempConnectCommand(sublime_plugin.WindowCommand):
|
|||
|
||||
def input(self, args):
|
||||
if "server_host" not in args:
|
||||
return ServerHostInputHandler()
|
||||
return ServerHost()
|
||||
|
||||
def input_description(self):
|
||||
return "Server host:"
|
||||
|
||||
|
||||
class ServerHostInputHandler(sublime_plugin.TextInputHandler):
|
||||
def initial_text(self):
|
||||
return "http://127.0.0.1:50051"
|
||||
# Generic Join Command
|
||||
#############################################################################
|
||||
async def JoinCommand(client: VirtualClient, workspace_id: str, buffer_id: str):
|
||||
vws = await client.join_workspace(workspace_id)
|
||||
if vws is not None:
|
||||
await vws.attach(buffer_id)
|
||||
|
||||
|
||||
class CodempJoinCommand(sublime_plugin.WindowCommand):
|
||||
def run(self, workspace_id, buffer_id):
|
||||
global CLIENT
|
||||
rt.dispatch(JoinCommand(CLIENT, workspace_id, buffer_id))
|
||||
|
||||
def input_description(self):
|
||||
return "Join:"
|
||||
|
||||
def input(self, args):
|
||||
if "workspace_id" not in args:
|
||||
return WorkspaceIdAndFollowup()
|
||||
|
||||
|
||||
# Join Workspace Command
|
||||
#############################################################################
|
||||
class CodempJoinCommand(sublime_plugin.WindowCommand):
|
||||
class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
|
||||
def run(self, workspace_id):
|
||||
global CLIENT
|
||||
rt.dispatch(CLIENT.join_workspace(workspace_id))
|
||||
|
||||
def input_description(self):
|
||||
return "Join Workspace:"
|
||||
return "Join specific workspace"
|
||||
|
||||
def input(self, args):
|
||||
if "workspace_id" not in args:
|
||||
return WorkspaceIdInputHandler()
|
||||
|
||||
|
||||
class WorkspaceIdInputHandler(sublime_plugin.TextInputHandler):
|
||||
def initial_text(self):
|
||||
return "What workspace should I join?"
|
||||
return RawWorkspaceId()
|
||||
|
||||
|
||||
# Join Buffer Command
|
||||
#############################################################################
|
||||
class CodempAttachCommand(sublime_plugin.WindowCommand):
|
||||
class CodempJoinBufferCommand(sublime_plugin.WindowCommand):
|
||||
def run(self, buffer_id):
|
||||
global CLIENT
|
||||
if CLIENT.active_workspace is not None:
|
||||
rt.dispatch(CLIENT.active_workspace.attach(buffer_id))
|
||||
else:
|
||||
sublime.error_message(
|
||||
"You haven't joined any worksapce yet. use `Codemp: Join Workspace`"
|
||||
"You haven't joined any worksapce yet. \
|
||||
use `Codemp: Join Workspace` or `Codemp: Join`"
|
||||
)
|
||||
return
|
||||
|
||||
rt.dispatch(CLIENT.active_workspace.attach(buffer_id))
|
||||
|
||||
def input_description(self):
|
||||
return "Join Buffer in workspace:"
|
||||
return "Join buffer in the active workspace"
|
||||
|
||||
# This is awful, fix it
|
||||
def input(self, args):
|
||||
global CLIENT
|
||||
if CLIENT.active_workspace is not None:
|
||||
if "buffer_id" not in args:
|
||||
existing_buffers = CLIENT.active_workspace.handle.filetree()
|
||||
if len(existing_buffers) == 0:
|
||||
return BufferIdInputHandler()
|
||||
else:
|
||||
return ListBufferIdInputHandler()
|
||||
else:
|
||||
if CLIENT.active_workspace is None:
|
||||
sublime.error_message(
|
||||
"You haven't joined any worksapce yet. use `Codemp: Join Workspace`"
|
||||
"You haven't joined any worksapce yet. \
|
||||
use `Codemp: Join Workspace` or `Codemp: Join`"
|
||||
)
|
||||
return
|
||||
|
||||
if "buffer_id" not in args:
|
||||
existing_buffers = CLIENT.active_workspace.handle.filetree()
|
||||
if len(existing_buffers) == 0:
|
||||
return RawBufferId()
|
||||
else:
|
||||
return ListBufferId()
|
||||
|
||||
|
||||
# Text Change Command
|
||||
#############################################################################
|
||||
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
|
||||
region = self.view.transform_region_from(sublime.Region(start, end), change_id)
|
||||
self.view.replace(edit, region, content)
|
||||
|
||||
|
||||
# Input Handlers
|
||||
##############################################################################
|
||||
class ServerHost(sublime_plugin.TextInputHandler):
|
||||
def name(self):
|
||||
return "server_host"
|
||||
|
||||
class BufferIdInputHandler(sublime_plugin.TextInputHandler):
|
||||
def initial_text(self):
|
||||
return "No buffers found in the workspace. Create new: "
|
||||
return "http://127.0.0.1:50051"
|
||||
|
||||
|
||||
class ListBufferIdInputHandler(sublime_plugin.ListInputHandler):
|
||||
class ListBufferId(sublime_plugin.ListInputHandler):
|
||||
def name(self):
|
||||
return "buffer_id"
|
||||
|
||||
|
@ -245,18 +253,35 @@ class ListBufferIdInputHandler(sublime_plugin.ListInputHandler):
|
|||
|
||||
def next_input(self, args):
|
||||
if "buffer_id" not in args:
|
||||
return BufferIdInputHandler()
|
||||
return RawBufferId()
|
||||
|
||||
|
||||
# Text Change Command
|
||||
#############################################################################
|
||||
# we call this command manually to have access to the edit token.
|
||||
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
|
||||
region = self.view.transform_region_from(sublime.Region(start, end), change_id)
|
||||
self.view.replace(edit, region, content)
|
||||
class RawWorkspaceId(sublime_plugin.TextInputHandler):
|
||||
def name(self):
|
||||
return "workspace_id"
|
||||
|
||||
def placeholder(self):
|
||||
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"
|
||||
|
||||
def placeholder(self):
|
||||
return "Buffer Id"
|
||||
|
||||
# Share Command
|
||||
# #############################################################################
|
||||
|
@ -272,27 +297,6 @@ class CodempReplaceTextCommand(sublime_plugin.TextCommand):
|
|||
# return "Share Buffer:"
|
||||
|
||||
|
||||
# class SublimeBufferPathInputHandler(sublime_plugin.ListInputHandler):
|
||||
# def list_items(self):
|
||||
# ret_list = []
|
||||
|
||||
# for window in sublime.windows():
|
||||
# for view in window.views():
|
||||
# if view.file_name():
|
||||
# ret_list.append(view.file_name())
|
||||
|
||||
# return ret_list
|
||||
|
||||
# def next_input(self, args):
|
||||
# if "server_id" not in args:
|
||||
# return ServerIdInputHandler()
|
||||
|
||||
|
||||
# class ServerIdInputHandler(sublime_plugin.TextInputHandler):
|
||||
# def initial_text(self):
|
||||
# return "Buffer name on server"
|
||||
|
||||
|
||||
# Disconnect Command
|
||||
#############################################################################
|
||||
class CodempDisconnectCommand(sublime_plugin.WindowCommand):
|
||||
|
@ -300,7 +304,7 @@ class CodempDisconnectCommand(sublime_plugin.WindowCommand):
|
|||
rt.sync(disconnect_client())
|
||||
|
||||
|
||||
# Proxy Commands ( NOT USED )
|
||||
# Proxy Commands ( NOT USED, left just in case we need it again. )
|
||||
#############################################################################
|
||||
# class ProxyCodempShareCommand(sublime_plugin.WindowCommand):
|
||||
# # on_window_command, does not trigger when called from the command palette
|
||||
|
@ -314,30 +318,6 @@ class CodempDisconnectCommand(sublime_plugin.WindowCommand):
|
|||
#
|
||||
# def input_description(self):
|
||||
# return 'Share Buffer:'
|
||||
#
|
||||
# class ProxyCodempJoinCommand(sublime_plugin.WindowCommand):
|
||||
# def run(self, **kwargs):
|
||||
# self.window.run_command("codemp_join", kwargs)
|
||||
#
|
||||
# def input(self, args):
|
||||
# if 'server_buffer' not in args:
|
||||
# return ServerBufferInputHandler()
|
||||
#
|
||||
# def input_description(self):
|
||||
# return 'Join Buffer:'
|
||||
#
|
||||
# class ProxyCodempConnectCommand(sublime_plugin.WindowCommand):
|
||||
# # on_window_command, does not trigger when called from the command palette
|
||||
# # See: https://github.com/sublimehq/sublime_text/issues/2234
|
||||
# def run(self, **kwargs):
|
||||
# self.window.run_command("codemp_connect", kwargs)
|
||||
#
|
||||
# def input(self, args):
|
||||
# if 'server_host' not in args:
|
||||
# return ServerHostInputHandler()
|
||||
#
|
||||
# def input_description(self):
|
||||
# return 'Server host:'
|
||||
|
||||
|
||||
# NOT NEEDED ANYMORE
|
||||
|
|
|
@ -3,8 +3,7 @@ from typing import Optional, Callable
|
|||
|
||||
|
||||
import sublime
|
||||
import asyncio # noqa: F401
|
||||
import typing # noqa: F401
|
||||
import asyncio
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
|
@ -12,7 +11,7 @@ import shutil
|
|||
|
||||
import Codemp.src.globals as g
|
||||
from Codemp.src.wrappers import BufferController, Workspace, Client
|
||||
from Codemp.src.utils import status_log, is_active, rowcol_to_region
|
||||
from Codemp.src.utils import status_log, rowcol_to_region
|
||||
from Codemp.src.TaskManager import TaskManager
|
||||
|
||||
|
||||
|
@ -167,45 +166,42 @@ 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:
|
||||
await self.handle.connect(server_host)
|
||||
except Exception:
|
||||
sublime.error_message("Could not connect:\n Make sure the server is up.")
|
||||
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()
|
||||
print(f"TEST: {id}")
|
||||
status_log(f"Connected to '{server_host}' with user id: {id}")
|
||||
|
||||
async def join_workspace(
|
||||
self, workspace_id: str, user="sublime", password="lmaodefaultpassword"
|
||||
):
|
||||
) -> VirtualWorkspace:
|
||||
try:
|
||||
status_log(f"Logging into workspace: '{workspace_id}'")
|
||||
await self.handle.login(user, password, workspace_id)
|
||||
except Exception as e:
|
||||
sublime.error_message(f"Failed to login to workspace '{workspace_id}': {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}")
|
||||
return
|
||||
|
||||
try:
|
||||
status_log(f"Joining workspace: '{workspace_id}'")
|
||||
workspace_handle = await self.handle.join_workspace(workspace_id)
|
||||
except Exception as e:
|
||||
sublime.error_message(f"Could not join workspace '{workspace_id}': {e}")
|
||||
status_log(f"Could not join workspace '{workspace_id}'.\nerror: {e}")
|
||||
sublime.error_message(f"Could not join workspace '{workspace_id}'.\nerror: {e}")
|
||||
return
|
||||
|
||||
vws = VirtualWorkspace(self, workspace_id, workspace_handle)
|
||||
self.make_active(vws)
|
||||
|
||||
self.workspaces[workspace_id] = vws
|
||||
|
||||
return vws
|
||||
|
||||
def spawn_cursor_manager(self, virtual_workspace: VirtualWorkspace):
|
||||
async def move_cursor_task(vws):
|
||||
status_log(f"spinning up cursor worker for workspace '{vws.id}'...")
|
||||
|
|
30
src/utils.py
30
src/utils.py
|
@ -13,12 +13,30 @@ def rowcol_to_region(view, start, end):
|
|||
return sublime.Region(a, b)
|
||||
|
||||
|
||||
def is_active(view):
|
||||
if view.window().active_view() == view:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def safe_listener_detach(txt_listener: sublime_plugin.TextChangeListener):
|
||||
if txt_listener.is_attached():
|
||||
txt_listener.detach()
|
||||
|
||||
|
||||
def get_contents(view):
|
||||
r = sublime.Region(0, view.size())
|
||||
return view.substr(r)
|
||||
|
||||
|
||||
def populate_view(view, content):
|
||||
view.run_command(
|
||||
"codemp_replace_text",
|
||||
{
|
||||
"start": 0,
|
||||
"end": view.size(),
|
||||
"content": content,
|
||||
"change_id": view.change_id(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_view_from_local_path(path):
|
||||
for window in sublime.windows():
|
||||
for view in window.views():
|
||||
if view.file_name() == path:
|
||||
return view
|
||||
|
|
Loading…
Reference in a new issue