Minor fixes in the lib, finished the python objects wrappers. Started working on plugin.py.

Former-commit-id: 3a5e587b1ba019bb9a263478cd2e08b9b532322e
This commit is contained in:
Camillo Schenone 2023-08-25 14:29:11 +02:00
parent e387b726c8
commit 6fe5effb68
4 changed files with 104 additions and 64 deletions

View file

@ -1 +1 @@
80ba7b16f0b0647e2f1d8520f3a90c51000bbd2c 8114809f135d7ea9f88d0a45a9de8fc93fdbd9ff

View file

@ -9,9 +9,9 @@ import time
# UGLYYYY, find a way to not have global variables laying around. # UGLYYYY, find a way to not have global variables laying around.
_tasks = [] _tasks = []
_client = CodempClient() _client = None
_cursor_controller = None _cursor_controller = None
_op_controller = None _buffer_controller = None
_setting_key = "codemp_buffer" _setting_key = "codemp_buffer"
def store_task(name = None): def store_task(name = None):
@ -23,6 +23,8 @@ def store_task(name = None):
return store_named_task return store_named_task
def plugin_loaded(): def plugin_loaded():
global _client
_client = CodempClient()
sublime_asyncio.acquire() # instantiate and start a global event loop. sublime_asyncio.acquire() # instantiate and start a global event loop.
class CodempClientViewEventListener(sublime_plugin.ViewEventListener): class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
@ -47,24 +49,24 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
return False return False
def on_text_changed(self, changes): def on_text_changed(self, changes):
global _op_controller global _buffer_controller
if _op_controller: if _buffer_controller:
for change in changes: for change in changes:
sublime_asyncio.dispatch(apply_changes(change)) sublime_asyncio.dispatch(apply_changes(change))
async def apply_changes(change): async def apply_changes(change):
global _op_controller global _buffer_controller
text = change.str text = change.str
skip = change.a.pt skip = change.a.pt
if change.len_utf8 == 0: # we are inserting new text. if change.len_utf8 == 0: # we are inserting new text.
tail = len(_op_controller.get_content()) - skip tail = len(_buffer_controller.get_content()) - skip
else: # we are changing an existing region of text of length len_utf8 else: # we are changing an existing region of text of length len_utf8
tail = len(_op_controller.get_content()) - skip - change.len_utf8 tail = len(_buffer_controller.get_content()) - skip - change.len_utf8
tail_skip = len(_op_controller.get_content()) - tail tail_skip = len(_buffer_controller.get_content()) - tail
print("[buff change]", skip, text, tail_skip) print("[buff change]", skip, text, tail_skip)
await _op_controller.apply(skip, text, tail) await _buffer_controller.apply(skip, text, tail)
async def make_connection(server_host): async def make_connection(server_host):
global _client global _client
@ -90,7 +92,7 @@ async def sync_buffer(caller, start, end, txt):
async def share_buffer(buffer): async def share_buffer(buffer):
global _client global _client
global _cursor_controller global _cursor_controller
global _op_controller global _buffer_controller
if not _client.ready: if not _client.ready:
sublime.error_message("No connected client.") sublime.error_message("No connected client.")
@ -106,8 +108,8 @@ async def share_buffer(buffer):
sublime.error_message("Could not share buffer.") sublime.error_message("Could not share buffer.")
return return
_op_controller = await _client.attach(buffer) _buffer_controller = await _client.attach(buffer)
_op_controller.callback(sync_buffer, _client.id) _buffer_controller.callback(sync_buffer, _client.id)
_cursor_controller = await _client.listen() _cursor_controller = await _client.listen()
_cursor_controller.callback(move_cursor, _client.id) _cursor_controller.callback(move_cursor, _client.id)
@ -115,7 +117,7 @@ async def share_buffer(buffer):
if not _cursor_controller: if not _cursor_controller:
sublime.error_message("Could not subsribe a listener.") sublime.error_message("Could not subsribe a listener.")
return return
if not _op_controller: if not _buffer_controller:
sublime.error_message("Could not attach to the buffer.") sublime.error_message("Could not attach to the buffer.")
return return
@ -127,7 +129,7 @@ async def share_buffer(buffer):
async def join_buffer(window, buffer): async def join_buffer(window, buffer):
global _client global _client
global _cursor_controller global _cursor_controller
global _op_controller global _buffer_controller
if not _client.ready: if not _client.ready:
sublime.error_message("No connected client.") sublime.error_message("No connected client.")
@ -138,8 +140,8 @@ async def join_buffer(window, buffer):
sublime.status_message("[codemp] Joining buffer {}".format(buffer)) sublime.status_message("[codemp] Joining buffer {}".format(buffer))
print("[codemp] Joining buffer {}".format(buffer)) print("[codemp] Joining buffer {}".format(buffer))
_op_controller = await _client.attach(buffer) _buffer_controller = await _client.attach(buffer)
content = _op_controller.get_content() content = _buffer_controller.get_content()
view.run_command("codemp_replace_view", {"content": content}) view.run_command("codemp_replace_view", {"content": content})
_cursor_controller = await _client.listen() _cursor_controller = await _client.listen()
@ -149,7 +151,7 @@ async def join_buffer(window, buffer):
if not _cursor_controller: if not _cursor_controller:
sublime.error_message("Could not subsribe a listener.") sublime.error_message("Could not subsribe a listener.")
return return
if not _op_controller: if not _buffer_controller:
sublime.error_message("Could not attach to the buffer.") sublime.error_message("Could not attach to the buffer.")
return return

View file

@ -4,68 +4,109 @@ import Codemp.bindings.codemp_client as libcodemp
class CodempClient(): class CodempClient():
def __init__(self): def __init__(self):
self.handle = None self.handle = libcodemp.codemp_init()
self.id = None
self.ready = False self.ready = False
async def connect(self, server_host): async def connect(self, server_host): # -> None
self.handle = await libcodemp.connect(server_host) await self.handle.connect(server_host)
self.id = await self.handle.get_id()
self.ready = True self.ready = True
def disconnect(self): def disconnect(self): # -> None
# disconnect all buffers
# stop all callbacks
self.handle = None self.handle = None
self.id = None
self.ready = False self.ready = False
# some code that tells the server to unsubscribe stuff as well.
async def get_id(self): async def create(self, path, content=None): # -> None
if self.ready and not self.id:
self.id = await self.handle.get_id()
return self.id
elif self.ready:
return self.id
else:
raise RuntimeError("Attemp to get id without an established connection.")
async def create(self, path, content=None):
if self.ready: if self.ready:
return await self.handle.create(path, content) return await self.handle.create(path, content)
else:
raise RuntimeError("Attemp to create a buffer without a connection.")
async def listen(self): async def join(self, session): # -> CursorController
if self.ready: if self.ready:
return CursorController(await self.handle.listen()) return CursorController(await self.handle.join(session))
else:
raise RuntimeError("Attempt to listen without a connection.")
async def attach(self, path): async def attach(self, path): # -> BufferController
if self.ready: if self.ready:
return ContentController(await self.handle.attach(path)) return BufferController(await self.handle.attach(path))
else:
raise RuntimeError("Attempt to attach without a connection.") async def get_cursor(self): # -> CursorController
if self.ready:
return CursorController(await self.handle.get_cursor())
async def get_buffer(self, path): # -> BufferController
if self.ready:
return BufferController(await self.handle.get_buffer())
async def remove_buffer(self, path): # -> None
if self.ready:
await self.handle.disconnect_buffer(path)
class CursorController(): class CursorController():
def __init__(self, handle): def __init__(self, handle):
self.handle = handle self.handle = handle
async def send(self, path, start, end): def send(self, path, start, end): # -> None
await self.handle.send(path, start, end) self.handle.send(path, start, end)
def callback(self, coro, id): def try_recv(self): # -> Optional[CursorEvent]
return self.handle.try_recv()
async def recv(self): # -> CursorEvent
return await self.handle.recv()
async def poll(self): # -> None
# await until new cursor event, then returns
return await self.handle.poll()
def drop_callback(self): # -> None
self.handle.drop_callback()
def callback(self, coro): # -> None
self.handle.callback(coro, id) self.handle.callback(coro, id)
class ContentController(): class BufferController():
def __init__(self, handle): def __init__(self, handle):
self.handle = handle self.handle = handle
def get_content(self): def get_content(self): # -> String
return self.handle.content() return self.handle.content()
async def apply(self, skip, text, tail): def replace(self, txt): # -> None
return await self.handle.apply(skip, text, tail) # replace the whole buffer.
self.handle.replace(txt)
def insert(self, txt, pos): # -> None
# insert text at buffer position pos
self.handle.insert(txt, pos)
def delta(self, start, txt, end): # -> None
# delta in the region start..end with txt new content
self.handle.delta(start, txt, end)
def delete(self, pos, count): # -> None
# delete starting from pos, count chars.
self.handle.delete(pos, count)
def cancel(self, pos, count): # -> None
# cancel backward `count` elements from pos.
self.handle.cancle(pos, count)
def try_recv(self): # -> Optional[TextChange]
return self.handle.try_recv()
async def recv(self): # -> TextChange
return await self.handle.recv()
async def poll(self): # -> ??
return await self.handle.poll()
def drop_callback(self): # -> None
self.handle.drop_callback()
def callback(self, coro): # -> None
self.handle.callback(coro)
def callback(self, coro, id):
self.handle.callback(coro, id)

View file

@ -53,6 +53,7 @@ impl From::<CodempInstance> for PyClientHandle {
#[pymethods] #[pymethods]
impl PyClientHandle { impl PyClientHandle {
fn connect<'a>(&'a self, py: Python<'a>, addr: String) ->PyResult<&'a PyAny> { fn connect<'a>(&'a self, py: Python<'a>, addr: String) ->PyResult<&'a PyAny> {
let rc = self.0.clone(); let rc = self.0.clone();
@ -232,7 +233,7 @@ impl PyCursorController {
} }
} }
fn send<'a>(&'a self, py: Python<'a>, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<&'a PyAny> { fn send<'a>(&'a self, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<()> {
let rc = self.handle.clone(); let rc = self.handle.clone();
let pos = CodempCursorPosition { let pos = CodempCursorPosition {
buffer: path, buffer: path,
@ -240,12 +241,8 @@ impl PyCursorController {
end: Some(end.into()) end: Some(end.into())
}; };
pyo3_asyncio::tokio::future_into_py(py, async move { rc.send(pos).map_err(PyCodempError::from)?;
rc.send(pos)
.map_err(PyCodempError::from)?;
Ok(()) Ok(())
})
} }
fn try_recv(&self, py: Python<'_>) -> PyResult<PyObject> { fn try_recv(&self, py: Python<'_>) -> PyResult<PyObject> {