mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-12-23 13:14:52 +01:00
Added tracing logging, removed some unhelpful python level logging messages
Former-commit-id: 68610be7c21c24dc9c354fc0162bed4dd3438606
This commit is contained in:
parent
9003215014
commit
a302180a19
7 changed files with 103 additions and 43 deletions
|
@ -14,6 +14,8 @@ pyo3 = { version = "0.20", features = ["extension-module"] }
|
||||||
pyo3-asyncio = { version = "0.20", features = ["tokio-runtime"] }
|
pyo3-asyncio = { version = "0.20", features = ["tokio-runtime"] }
|
||||||
serde = { version = "1.0.196", features = ["derive"] }
|
serde = { version = "1.0.196", features = ["derive"] }
|
||||||
tokio = "1.29.1"
|
tokio = "1.29.1"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = "0.3.18"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = "0.19.2"
|
pyo3-build-config = "0.19.2"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
cbf3777125b44718cba5f2e67dcbe9a01700840b
|
28b1d0465b66af6afb0266207d139e48c95173fd
|
26
plugin.py
26
plugin.py
|
@ -2,6 +2,7 @@ import sublime
|
||||||
import sublime_plugin
|
import sublime_plugin
|
||||||
|
|
||||||
from Codemp.src.codemp_client import VirtualClient
|
from Codemp.src.codemp_client import VirtualClient
|
||||||
|
from Codemp.src.codemp_client import CodempLogger
|
||||||
from Codemp.src.TaskManager import rt
|
from Codemp.src.TaskManager import rt
|
||||||
from Codemp.src.utils import status_log
|
from Codemp.src.utils import status_log
|
||||||
from Codemp.src.utils import safe_listener_detach
|
from Codemp.src.utils import safe_listener_detach
|
||||||
|
@ -9,6 +10,7 @@ from Codemp.src.utils import get_contents
|
||||||
from Codemp.src.utils import populate_view
|
from Codemp.src.utils import populate_view
|
||||||
from Codemp.src.utils import get_view_from_local_path
|
from Codemp.src.utils import get_view_from_local_path
|
||||||
import Codemp.src.globals as g
|
import Codemp.src.globals as g
|
||||||
|
from Codemp.bindings.codemp_client import init_logger
|
||||||
|
|
||||||
CLIENT = None
|
CLIENT = None
|
||||||
TEXT_LISTENER = None
|
TEXT_LISTENER = None
|
||||||
|
@ -25,6 +27,9 @@ def plugin_loaded():
|
||||||
CLIENT = VirtualClient(disconnect_client)
|
CLIENT = VirtualClient(disconnect_client)
|
||||||
TEXT_LISTENER = CodempClientTextChangeListener()
|
TEXT_LISTENER = CodempClientTextChangeListener()
|
||||||
|
|
||||||
|
logger = CodempLogger(init_logger(True))
|
||||||
|
CLIENT.tm.dispatch(logger.spawn_logger(), "codemp-logger")
|
||||||
|
|
||||||
status_log("plugin loaded")
|
status_log("plugin loaded")
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +48,6 @@ def plugin_unloaded():
|
||||||
global CLIENT
|
global CLIENT
|
||||||
# releasing the runtime, runs the disconnect callback defined when acquiring the event loop.
|
# releasing the runtime, runs the disconnect callback defined when acquiring the event loop.
|
||||||
CLIENT.tm.release(False)
|
CLIENT.tm.release(False)
|
||||||
status_log("plugin unloaded")
|
|
||||||
|
|
||||||
|
|
||||||
# Listeners
|
# Listeners
|
||||||
|
@ -60,9 +64,6 @@ class EventListener(sublime_plugin.EventListener):
|
||||||
for wsid in s[g.CODEMP_WINDOW_WORKSPACES]:
|
for wsid in s[g.CODEMP_WINDOW_WORKSPACES]:
|
||||||
ws = CLIENT[wsid]
|
ws = CLIENT[wsid]
|
||||||
if ws is not None:
|
if ws is not None:
|
||||||
status_log(
|
|
||||||
f"current active: {CLIENT.active_workspace.id}, ws = {ws.id}"
|
|
||||||
)
|
|
||||||
if ws.id == CLIENT.active_workspace.id:
|
if ws.id == CLIENT.active_workspace.id:
|
||||||
CLIENT.active_workspace = None
|
CLIENT.active_workspace = None
|
||||||
CLIENT.tm.stop(f"{g.CURCTL_TASK_PREFIX}-{ws.id}")
|
CLIENT.tm.stop(f"{g.CURCTL_TASK_PREFIX}-{ws.id}")
|
||||||
|
@ -87,23 +88,18 @@ class CodempClientViewEventListener(sublime_plugin.ViewEventListener):
|
||||||
if vbuff is not None:
|
if vbuff is not None:
|
||||||
CLIENT.send_cursor(vbuff)
|
CLIENT.send_cursor(vbuff)
|
||||||
|
|
||||||
# We only edit on one view at a time, therefore we only need one TextChangeListener
|
|
||||||
# Each time we focus a view to write on it, we first attach the listener to that buffer.
|
|
||||||
# When we defocus, we detach it.
|
|
||||||
def on_activated(self):
|
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
|
# 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
|
# callback (i know right?), so we have to manually keep track of which view has the focus
|
||||||
g.ACTIVE_CODEMP_VIEW = self.view.id()
|
g.ACTIVE_CODEMP_VIEW = self.view.id()
|
||||||
print("view {} activated".format(self.view.id()))
|
# print("view {} activated".format(self.view.id()))
|
||||||
|
global TEXT_LISTENER
|
||||||
TEXT_LISTENER.attach(self.view.buffer())
|
TEXT_LISTENER.attach(self.view.buffer())
|
||||||
|
|
||||||
def on_deactivated(self):
|
def on_deactivated(self):
|
||||||
global TEXT_LISTENER
|
|
||||||
|
|
||||||
g.ACTIVE_CODEMP_VIEW = None
|
g.ACTIVE_CODEMP_VIEW = None
|
||||||
print("view {} deactivated".format(self.view.id()))
|
# print("view {} deactivated".format(self.view.id()))
|
||||||
|
global TEXT_LISTENER
|
||||||
safe_listener_detach(TEXT_LISTENER)
|
safe_listener_detach(TEXT_LISTENER)
|
||||||
|
|
||||||
def on_pre_close(self):
|
def on_pre_close(self):
|
||||||
|
@ -193,7 +189,7 @@ class CodempJoinCommand(sublime_plugin.WindowCommand):
|
||||||
class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
|
class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
|
||||||
def run(self, workspace_id):
|
def run(self, workspace_id):
|
||||||
global CLIENT
|
global CLIENT
|
||||||
rt.dispatch(CLIENT.join_workspace(workspace_id))
|
rt.dispatch(CLIENT.join_workspace(workspace_id, "sublime2"))
|
||||||
|
|
||||||
def input_description(self):
|
def input_description(self):
|
||||||
return "Join specific workspace"
|
return "Join specific workspace"
|
||||||
|
@ -208,7 +204,7 @@ class CodempJoinWorkspaceCommand(sublime_plugin.WindowCommand):
|
||||||
class CodempJoinBufferCommand(sublime_plugin.WindowCommand):
|
class CodempJoinBufferCommand(sublime_plugin.WindowCommand):
|
||||||
def run(self, buffer_id):
|
def run(self, buffer_id):
|
||||||
global CLIENT
|
global CLIENT
|
||||||
if CLIENT.active_workspace is not None:
|
if CLIENT.active_workspace is None:
|
||||||
sublime.error_message(
|
sublime.error_message(
|
||||||
"You haven't joined any worksapce yet. \
|
"You haven't joined any worksapce yet. \
|
||||||
use `Codemp: Join Workspace` or `Codemp: Join`"
|
use `Codemp: Join Workspace` or `Codemp: Join`"
|
||||||
|
|
|
@ -56,9 +56,7 @@ class TaskManager:
|
||||||
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:
|
||||||
rt.sync(
|
rt.sync(self._stop(t))
|
||||||
self._stop(t)
|
|
||||||
) # awaiting the task blocks until it actually is finished.
|
|
||||||
|
|
||||||
def stop_all(self):
|
def stop_all(self):
|
||||||
for task in self.tasks:
|
for task in self.tasks:
|
||||||
|
|
|
@ -15,6 +15,26 @@ from Codemp.src.utils import status_log, rowcol_to_region
|
||||||
from Codemp.src.TaskManager import TaskManager
|
from Codemp.src.TaskManager import TaskManager
|
||||||
|
|
||||||
|
|
||||||
|
class CodempLogger:
|
||||||
|
def __init__(self, handle):
|
||||||
|
self.handle = handle
|
||||||
|
|
||||||
|
async def message(self):
|
||||||
|
return await self.handle.message()
|
||||||
|
|
||||||
|
async def spawn_logger(self):
|
||||||
|
status_log("spinning up the logger...")
|
||||||
|
try:
|
||||||
|
while msg := await self.handle.message():
|
||||||
|
print(msg)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
status_log("stopping logger")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
status_log(f"logger crashed unexpectedly:\n{e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
# This class is used as an abstraction between the local buffers (sublime side) and the
|
# This class is used as an abstraction between the local buffers (sublime side) and the
|
||||||
# remote buffers (codemp side), to handle the syncronicity.
|
# remote buffers (codemp side), to handle the syncronicity.
|
||||||
# This class is mainly manipulated by a VirtualWorkspace, that manages its buffers
|
# This class is mainly manipulated by a VirtualWorkspace, that manages its buffers
|
||||||
|
@ -133,7 +153,6 @@ class VirtualWorkspace:
|
||||||
|
|
||||||
async def attach(self, id: str):
|
async def attach(self, id: str):
|
||||||
if id is None:
|
if id is None:
|
||||||
status_log("can't attach if buffer does not exist, aborting.")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
await self.handle.fetch_buffers()
|
await self.handle.fetch_buffers()
|
||||||
|
@ -172,10 +191,8 @@ class VirtualClient:
|
||||||
return self.workspaces.get(key)
|
return self.workspaces.get(key)
|
||||||
|
|
||||||
def make_active(self, ws: VirtualWorkspace):
|
def make_active(self, ws: VirtualWorkspace):
|
||||||
# TODO: Logic to deal with swapping to and from workspaces,
|
|
||||||
# what happens to the cursor tasks etc..
|
|
||||||
if self.active_workspace is not None:
|
if self.active_workspace is not None:
|
||||||
self.tm.stop_and_pop(f"{g.CURCTL_TASK_PREFIX}-{self.active_workspace.id}")
|
self.tm.stop(f"{g.CURCTL_TASK_PREFIX}-{self.active_workspace.id}")
|
||||||
self.active_workspace = ws
|
self.active_workspace = ws
|
||||||
self.spawn_cursor_manager(ws)
|
self.spawn_cursor_manager(ws)
|
||||||
|
|
||||||
|
@ -193,26 +210,20 @@ class VirtualClient:
|
||||||
status_log(f"Connected to '{server_host}' with user id: {id}")
|
status_log(f"Connected to '{server_host}' with user id: {id}")
|
||||||
|
|
||||||
async def join_workspace(
|
async def join_workspace(
|
||||||
self, workspace_id: str, user="sublime", password="***REMOVED***"
|
self, workspace_id: str, user="sublime2", password="***REMOVED***"
|
||||||
) -> VirtualWorkspace:
|
) -> VirtualWorkspace:
|
||||||
try:
|
try:
|
||||||
status_log(f"Logging into workspace: '{workspace_id}'")
|
status_log(f"Logging into workspace: '{workspace_id}'")
|
||||||
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}", True)
|
||||||
sublime.error_message(
|
|
||||||
f"Failed to login to workspace '{workspace_id}'.\nerror: {e}"
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
status_log(f"Joining workspace: '{workspace_id}'")
|
status_log(f"Joining workspace: '{workspace_id}'")
|
||||||
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}", True)
|
||||||
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)
|
||||||
|
@ -229,10 +240,6 @@ class VirtualClient:
|
||||||
vbuff = vws.get_by_remote(cursor_event.buffer)
|
vbuff = vws.get_by_remote(cursor_event.buffer)
|
||||||
|
|
||||||
if vbuff is None:
|
if vbuff is None:
|
||||||
status_log(
|
|
||||||
f"Received a cursor event for an unknown \
|
|
||||||
or inactive buffer: {cursor_event.buffer}"
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
reg = rowcol_to_region(
|
reg = rowcol_to_region(
|
||||||
|
@ -264,7 +271,7 @@ class VirtualClient:
|
||||||
|
|
||||||
def send_cursor(self, vbuff: VirtualBuffer):
|
def send_cursor(self, vbuff: VirtualBuffer):
|
||||||
# TODO: only the last placed cursor/selection.
|
# TODO: only the last placed cursor/selection.
|
||||||
status_log(f"sending cursor position in workspace: {vbuff.workspace.id}")
|
# status_log(f"sending cursor position in workspace: {vbuff.workspace.id}")
|
||||||
region = vbuff.view.sel()[0]
|
region = vbuff.view.sel()[0]
|
||||||
start = vbuff.view.rowcol(region.begin()) # only counts UTF8 chars
|
start = vbuff.view.rowcol(region.begin()) # only counts UTF8 chars
|
||||||
end = vbuff.view.rowcol(region.end())
|
end = vbuff.view.rowcol(region.end())
|
||||||
|
@ -283,9 +290,6 @@ class VirtualClient:
|
||||||
# We are not listening on it. Otherwise, interrupt the listening
|
# We are not listening on it. Otherwise, interrupt the listening
|
||||||
# to avoid echoing back the change just received.
|
# to avoid echoing back the change just received.
|
||||||
if vb.view.id() == g.ACTIVE_CODEMP_VIEW:
|
if vb.view.id() == g.ACTIVE_CODEMP_VIEW:
|
||||||
status_log(
|
|
||||||
"received a text change with view active, stopping the echo."
|
|
||||||
)
|
|
||||||
vb.view.settings()[g.CODEMP_IGNORE_NEXT_TEXT_CHANGE] = True
|
vb.view.settings()[g.CODEMP_IGNORE_NEXT_TEXT_CHANGE] = True
|
||||||
|
|
||||||
# we need to go through a sublime text command, since the method,
|
# we need to go through a sublime text command, since the method,
|
||||||
|
|
62
src/lib.rs
62
src/lib.rs
|
@ -1,7 +1,9 @@
|
||||||
use codemp::proto::common::Identity;
|
use codemp::proto::common::Identity;
|
||||||
use pyo3::types::PyList;
|
use pyo3::types::PyList;
|
||||||
use std::{format, sync::Arc};
|
use std::{format, sync::Arc};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::{mpsc, Mutex, RwLock};
|
||||||
|
use tracing;
|
||||||
|
use tracing_subscriber;
|
||||||
|
|
||||||
use codemp::prelude::*;
|
use codemp::prelude::*;
|
||||||
use codemp::{errors::Error as CodempError, proto::files::BufferNode};
|
use codemp::{errors::Error as CodempError, proto::files::BufferNode};
|
||||||
|
@ -12,7 +14,7 @@ use pyo3::{
|
||||||
types::{PyString, PyType},
|
types::{PyString, PyType},
|
||||||
};
|
};
|
||||||
|
|
||||||
// ERRORS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ERRORS And LOGGING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
struct PyCodempError(CodempError);
|
struct PyCodempError(CodempError);
|
||||||
impl From<CodempError> for PyCodempError {
|
impl From<CodempError> for PyCodempError {
|
||||||
fn from(err: CodempError) -> Self {
|
fn from(err: CodempError) -> Self {
|
||||||
|
@ -39,6 +41,33 @@ impl From<PyCodempError> for PyErr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct LoggerProducer(mpsc::Sender<String>);
|
||||||
|
|
||||||
|
impl std::io::Write for LoggerProducer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
// TODO this is a LOSSY logger!!
|
||||||
|
let _ = self.0.try_send(String::from_utf8_lossy(buf).to_string()); // ignore: logger disconnected or with full buffer
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct PyLogger(Arc<Mutex<mpsc::Receiver<String>>>);
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyLogger {
|
||||||
|
fn message<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
|
||||||
|
let rc = self.0.clone();
|
||||||
|
|
||||||
|
pyo3_asyncio::tokio::future_into_py(py, async move { Ok(rc.lock().await.recv().await) })
|
||||||
|
}
|
||||||
|
}
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
// Workflow:
|
// Workflow:
|
||||||
|
@ -57,6 +86,33 @@ fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClient>> {
|
||||||
Ok(Py::new(py, PyClient::default())?)
|
Ok(Py::new(py, PyClient::default())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyfunction]
|
||||||
|
fn init_logger(py: Python<'_>, debug: Option<bool>) -> PyResult<Py<PyLogger>> {
|
||||||
|
let (tx, rx) = mpsc::channel(256);
|
||||||
|
let level = if debug.unwrap_or(false) {
|
||||||
|
tracing::Level::DEBUG
|
||||||
|
} else {
|
||||||
|
tracing::Level::INFO
|
||||||
|
};
|
||||||
|
let format = tracing_subscriber::fmt::format()
|
||||||
|
.without_time()
|
||||||
|
.with_level(true)
|
||||||
|
.with_target(true)
|
||||||
|
.with_thread_ids(false)
|
||||||
|
.with_thread_names(false)
|
||||||
|
.with_file(false)
|
||||||
|
.with_line_number(false)
|
||||||
|
.with_source_location(false)
|
||||||
|
.compact();
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_ansi(false)
|
||||||
|
.event_format(format)
|
||||||
|
.with_max_level(level)
|
||||||
|
.with_writer(std::sync::Mutex::new(LoggerProducer(tx)))
|
||||||
|
.init();
|
||||||
|
Ok(Py::new(py, PyLogger(Arc::new(Mutex::new(rx))))?)
|
||||||
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct PyClient(Arc<RwLock<Option<CodempClient>>>);
|
struct PyClient(Arc<RwLock<Option<CodempClient>>>);
|
||||||
|
|
||||||
|
@ -477,10 +533,12 @@ impl PyTextChange {
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
|
m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
|
||||||
|
m.add_function(wrap_pyfunction!(init_logger, m)?)?;
|
||||||
m.add_class::<PyClient>()?;
|
m.add_class::<PyClient>()?;
|
||||||
m.add_class::<PyWorkspace>()?;
|
m.add_class::<PyWorkspace>()?;
|
||||||
m.add_class::<PyCursorController>()?;
|
m.add_class::<PyCursorController>()?;
|
||||||
m.add_class::<PyBufferController>()?;
|
m.add_class::<PyBufferController>()?;
|
||||||
|
m.add_class::<PyLogger>()?;
|
||||||
|
|
||||||
m.add_class::<PyId>()?;
|
m.add_class::<PyId>()?;
|
||||||
m.add_class::<PyCursorEvent>()?;
|
m.add_class::<PyCursorEvent>()?;
|
||||||
|
|
|
@ -2,9 +2,11 @@ import sublime
|
||||||
import sublime_plugin
|
import sublime_plugin
|
||||||
|
|
||||||
|
|
||||||
def status_log(msg):
|
def status_log(msg, popup=False):
|
||||||
sublime.status_message("[codemp] {}".format(msg))
|
sublime.status_message("[codemp] {}".format(msg))
|
||||||
print("[codemp] {}".format(msg))
|
print("[codemp] {}".format(msg))
|
||||||
|
if popup:
|
||||||
|
sublime.error_message(msg)
|
||||||
|
|
||||||
|
|
||||||
def rowcol_to_region(view, start, end):
|
def rowcol_to_region(view, start, end):
|
||||||
|
|
Loading…
Reference in a new issue