mirror of
https://github.com/hexedtech/codemp-sublime.git
synced 2024-12-24 13:34:54 +01:00
feat: improved logging system
fix: remove hard dependency from virtual workspace from virtual buffer Former-commit-id: cb765c754d225d6a5beb6963facffd4211737bd2
This commit is contained in:
parent
b8a591b7d1
commit
d3388bd6d3
5 changed files with 97 additions and 72 deletions
35
plugin.py
35
plugin.py
|
@ -2,33 +2,47 @@
|
|||
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
import logging
|
||||
import random
|
||||
|
||||
from Codemp.src.task_manager import tm
|
||||
from Codemp.src.client import client, VirtualClient
|
||||
from Codemp.src.logger import logger
|
||||
from Codemp.src.utils import status_log
|
||||
from Codemp.src.logger import inner_logger
|
||||
from Codemp.src.utils import safe_listener_detach
|
||||
from Codemp.src.utils import safe_listener_attach
|
||||
from Codemp.src import globals as g
|
||||
|
||||
LOG_LEVEL = logging.DEBUG
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(
|
||||
logging.Formatter(
|
||||
fmt="<{thread}/{threadName}>[codemp] [{name} :: {funcName}] {levelname}: {message}",
|
||||
style="{",
|
||||
)
|
||||
)
|
||||
package_logger = logging.getLogger(__package__)
|
||||
package_logger.addHandler(handler)
|
||||
package_logger.setLevel(LOG_LEVEL)
|
||||
package_logger.propagate = False
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TEXT_LISTENER = None
|
||||
|
||||
|
||||
# Initialisation and Deinitialisation
|
||||
##############################################################################
|
||||
|
||||
|
||||
def plugin_loaded():
|
||||
global TEXT_LISTENER
|
||||
|
||||
# instantiate and start a global asyncio event loop.
|
||||
# pass in the exit_handler coroutine that will be called upon relasing the event loop.
|
||||
tm.acquire(disconnect_client)
|
||||
tm.dispatch(logger.log(), "codemp-logger")
|
||||
tm.dispatch(inner_logger.listen(), "codemp-logger")
|
||||
|
||||
TEXT_LISTENER = CodempClientTextChangeListener()
|
||||
|
||||
status_log("plugin loaded")
|
||||
logger.debug("plugin loaded")
|
||||
|
||||
|
||||
async def disconnect_client():
|
||||
|
@ -47,7 +61,8 @@ async def disconnect_client():
|
|||
|
||||
def plugin_unloaded():
|
||||
# releasing the runtime, runs the disconnect callback defined when acquiring the event loop.
|
||||
status_log("unloading")
|
||||
logger.debug("unloading")
|
||||
package_logger.removeHandler(handler)
|
||||
tm.release(False)
|
||||
|
||||
|
||||
|
@ -71,8 +86,8 @@ class EventListener(sublime_plugin.EventListener):
|
|||
for wsid in s[g.CODEMP_WINDOW_WORKSPACES]:
|
||||
ws = client[wsid]
|
||||
if ws is None:
|
||||
status_log(
|
||||
"[WARN] a tag on the window was found but not a matching workspace."
|
||||
logger.warning(
|
||||
"a tag on the window was found but not a matching workspace."
|
||||
)
|
||||
continue
|
||||
|
||||
|
@ -137,7 +152,7 @@ class CodempClientTextChangeListener(sublime_plugin.TextChangeListener):
|
|||
def on_text_changed(self, changes):
|
||||
s = self.buffer.primary_view().settings()
|
||||
if s.get(g.CODEMP_IGNORE_NEXT_TEXT_CHANGE, None):
|
||||
status_log("Ignoring echoing back the change.")
|
||||
logger.debug("Ignoring echoing back the change.")
|
||||
s[g.CODEMP_IGNORE_NEXT_TEXT_CHANGE] = False
|
||||
return
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import sublime
|
||||
import os
|
||||
import logging
|
||||
from asyncio import CancelledError
|
||||
|
||||
|
||||
from codemp import BufferController
|
||||
from Codemp.src.workspace import VirtualWorkspace
|
||||
from Codemp.src import globals as g
|
||||
|
||||
from Codemp.src.task_manager import tm
|
||||
from Codemp.src.utils import status_log
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# This class is used as an abstraction between the local buffers (sublime side) and the
|
||||
|
@ -18,18 +17,19 @@ from Codemp.src.utils import status_log
|
|||
class VirtualBuffer:
|
||||
def __init__(
|
||||
self,
|
||||
workspace: VirtualWorkspace,
|
||||
workspace_id: str,
|
||||
workspace_rootdir: str,
|
||||
remote_id: str,
|
||||
buffctl: BufferController,
|
||||
):
|
||||
self.view = sublime.active_window().new_file()
|
||||
self.codemp_id = remote_id
|
||||
self.sublime_id = self.view.buffer_id()
|
||||
|
||||
self.workspace = workspace
|
||||
self.workspace_id = workspace_id
|
||||
self.workspace_rootdir = workspace_rootdir
|
||||
self.buffctl = buffctl
|
||||
|
||||
self.tmpfile = os.path.join(workspace.rootdir, self.codemp_id)
|
||||
self.tmpfile = os.path.join(workspace_rootdir, self.codemp_id)
|
||||
|
||||
self.view.set_name(self.codemp_id)
|
||||
open(self.tmpfile, "a").close()
|
||||
|
@ -46,7 +46,7 @@ class VirtualBuffer:
|
|||
self.view.set_status(g.SUBLIME_STATUS_ID, "[Codemp]")
|
||||
s[g.CODEMP_BUFFER_TAG] = True
|
||||
s[g.CODEMP_REMOTE_ID] = self.codemp_id
|
||||
s[g.CODEMP_WORKSPACE_ID] = self.workspace.id
|
||||
s[g.CODEMP_WORKSPACE_ID] = self.workspace_id
|
||||
|
||||
def cleanup(self):
|
||||
os.remove(self.tmpfile)
|
||||
|
@ -58,15 +58,15 @@ class VirtualBuffer:
|
|||
self.view.erase_status(g.SUBLIME_STATUS_ID)
|
||||
|
||||
tm.stop(f"{g.BUFFCTL_TASK_PREFIX}-{self.codemp_id}")
|
||||
status_log(f"cleaning up virtual buffer '{self.codemp_id}'")
|
||||
logger.info(f"cleaning up virtual buffer '{self.codemp_id}'")
|
||||
|
||||
async def apply_bufferchange_task(self):
|
||||
status_log(f"spinning up '{self.codemp_id}' buffer worker...")
|
||||
logger.debug(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.")
|
||||
logger.debug("change is empty. skipping.")
|
||||
continue
|
||||
# In case a change arrives to a background buffer, just apply it.
|
||||
# We are not listening on it. Otherwise, interrupt the listening
|
||||
|
@ -88,10 +88,10 @@ class VirtualBuffer:
|
|||
)
|
||||
|
||||
except CancelledError:
|
||||
status_log(f"'{self.codemp_id}' buffer worker stopped...")
|
||||
logger.debug(f"'{self.codemp_id}' buffer worker stopped...")
|
||||
raise
|
||||
except Exception as e:
|
||||
status_log(f"buffer worker '{self.codemp_id}' crashed:\n{e}")
|
||||
logger.error(f"buffer worker '{self.codemp_id}' crashed:\n{e}")
|
||||
raise
|
||||
|
||||
def send_buffer_change(self, changes):
|
||||
|
@ -99,14 +99,14 @@ class VirtualBuffer:
|
|||
# sequential indexing, assuming the changes are applied in the order they are received.
|
||||
for change in changes:
|
||||
region = sublime.Region(change.a.pt, change.b.pt)
|
||||
status_log(
|
||||
logger.debug(
|
||||
"sending txt change: Reg({} {}) -> '{}'".format(
|
||||
region.begin(), region.end(), change.str
|
||||
)
|
||||
)
|
||||
self.buffctl.send(region.begin(), region.end(), change.str)
|
||||
|
||||
def send_cursor(self, vws: VirtualWorkspace):
|
||||
def send_cursor(self, vws): # pyright: ignore # noqa: F821
|
||||
# TODO: only the last placed cursor/selection.
|
||||
# status_log(f"sending cursor position in workspace: {vbuff.workspace.id}")
|
||||
region = self.view.sel()[0]
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sublime
|
||||
import logging
|
||||
|
||||
from codemp import Client
|
||||
from Codemp.src import globals as g
|
||||
from Codemp.src.workspace import VirtualWorkspace
|
||||
from Codemp.src.utils import status_log
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VirtualClient:
|
||||
|
@ -18,47 +20,44 @@ class VirtualClient:
|
|||
return self.workspaces.get(key)
|
||||
|
||||
def connect(self, host: str, user: str, password: str):
|
||||
status_log(f"Connecting to {host} with user {user}")
|
||||
logger.info(f"Connecting to {host} with user {user}")
|
||||
try:
|
||||
self.handle = Client(host, user, password)
|
||||
except Exception as e:
|
||||
logger.error(f"Could not connect: {e}")
|
||||
sublime.error_message(
|
||||
f"Could not connect:\n Make sure the server is up.\n\
|
||||
or your credentials are correct\n\nerror: {e}"
|
||||
"Could not connect:\n Make sure the server is up.\n\
|
||||
or your credentials are correct."
|
||||
)
|
||||
return
|
||||
|
||||
id = self.handle.user_id()
|
||||
status_log(f"Connected to '{host}' with user {user} and id: {id}")
|
||||
logger.debug(f"Connected to '{host}' with user {user} and id: {id}")
|
||||
|
||||
async def join_workspace(
|
||||
self,
|
||||
workspace_id: str,
|
||||
) -> VirtualWorkspace | None:
|
||||
if self.handle is None:
|
||||
status_log("Connect to a server first!", True)
|
||||
return
|
||||
|
||||
status_log(f"Joining workspace: '{workspace_id}'")
|
||||
logger.info(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
|
||||
)
|
||||
logger.error(f"Could not join workspace '{workspace_id}'.\n\nerror: {e}")
|
||||
sublime.error_message(f"Could not join workspace '{workspace_id}'")
|
||||
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}'")
|
||||
logger.info(f"Leaving workspace: '{id}'")
|
||||
if self.handle.leave_workspace(id):
|
||||
self.workspaces[id].cleanup()
|
||||
del self.workspaces[id]
|
||||
|
@ -70,9 +69,7 @@ class VirtualClient:
|
|||
|
||||
ws = self.workspaces.get(tag_id)
|
||||
if ws is None:
|
||||
status_log(
|
||||
"[WARN] a tag on the view was found but not a matching workspace."
|
||||
)
|
||||
logging.warning("a tag on the view was found but not a matching workspace.")
|
||||
return
|
||||
|
||||
return ws
|
||||
|
|
|
@ -1,35 +1,44 @@
|
|||
import logging
|
||||
from asyncio import CancelledError
|
||||
|
||||
from codemp import PyLogger
|
||||
from Codemp.src.utils import status_log
|
||||
|
||||
|
||||
class CodempLogger:
|
||||
def __init__(self, debug: bool = False):
|
||||
self.handle = None
|
||||
def __init__(self, log_level):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.level = log_level
|
||||
self.logger.setLevel(self.level)
|
||||
self.internal_logger = None
|
||||
self.started = False
|
||||
|
||||
try:
|
||||
self.handle = PyLogger(debug)
|
||||
# PyLogger spins a tracing_subscriber rust side with a
|
||||
# .try_init() and errors out if already initialized.
|
||||
# initialize only once
|
||||
self.internal_logger = PyLogger(self.level == logging.DEBUG)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def log(self):
|
||||
async def listen(self):
|
||||
if self.started:
|
||||
return
|
||||
|
||||
self.started = True
|
||||
status_log("spinning up the logger...")
|
||||
self.logger.debug("spinning up internal logger listener...")
|
||||
|
||||
assert self.internal_logger is not None
|
||||
try:
|
||||
while msg := await self.handle.listen():
|
||||
print(msg)
|
||||
while msg := await self.internal_logger.listen():
|
||||
self.logger.log(self.level, msg)
|
||||
except CancelledError:
|
||||
status_log("stopping logger")
|
||||
self.logger.debug("inner logger stopped.")
|
||||
self.started = False
|
||||
raise
|
||||
except Exception as e:
|
||||
status_log(f"logger crashed unexpectedly:\n{e}")
|
||||
raise
|
||||
self.logger.error(f"inner logger crashed unexpectedly: \n {e}")
|
||||
raise e
|
||||
|
||||
def log(self, msg):
|
||||
self.logger.log(self.level, msg)
|
||||
|
||||
|
||||
DEBUG = False
|
||||
logger = CodempLogger(DEBUG)
|
||||
inner_logger = CodempLogger(logging.INFO)
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
import sublime
|
||||
import shutil
|
||||
import tempfile
|
||||
import logging
|
||||
from asyncio import CancelledError
|
||||
|
||||
from codemp import Workspace
|
||||
|
@ -10,9 +11,10 @@ from codemp import Workspace
|
|||
from Codemp.src import globals as g
|
||||
from Codemp.src.buffers import VirtualBuffer
|
||||
from Codemp.src.task_manager import tm
|
||||
from Codemp.src.utils import status_log
|
||||
from Codemp.src.utils import rowcol_to_region
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# A virtual workspace is a bridge class that aims to translate
|
||||
# events that happen to the codemp workspaces into sublime actions
|
||||
|
@ -51,7 +53,7 @@ class VirtualWorkspace:
|
|||
)
|
||||
d["folders"] = newf
|
||||
self.sublime_window.set_project_data(d)
|
||||
status_log(f"cleaning up virtual workspace '{self.id}'")
|
||||
logger.info(f"cleaning up virtual workspace '{self.id}'")
|
||||
shutil.rmtree(self.rootdir, ignore_errors=True)
|
||||
|
||||
self.curctl.stop()
|
||||
|
@ -70,7 +72,7 @@ class VirtualWorkspace:
|
|||
|
||||
# initialise the virtual filesystem
|
||||
tmpdir = tempfile.mkdtemp(prefix="codemp_")
|
||||
status_log("setting up virtual fs for workspace in: {} ".format(tmpdir))
|
||||
logging.debug("setting up virtual fs for workspace in: {} ".format(tmpdir))
|
||||
self.rootdir = tmpdir
|
||||
|
||||
# and add a new "project folder"
|
||||
|
@ -119,8 +121,8 @@ class VirtualWorkspace:
|
|||
|
||||
vbuff = self.active_Buffer.get(local_id)
|
||||
if vbuff is None:
|
||||
status_log(
|
||||
"[WARN] a local-remote buffer id pair was found but \
|
||||
logging.warning(
|
||||
"a local-remote buffer id pair was found but \
|
||||
not the matching virtual buffer."
|
||||
)
|
||||
return
|
||||
|
@ -153,7 +155,7 @@ class VirtualWorkspace:
|
|||
try:
|
||||
await self.handle.create(id)
|
||||
except Exception as e:
|
||||
status_log(f"could not create buffer:\n\n {e}", True)
|
||||
logging.error(f"could not create buffer:\n\n {e}", True)
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
@ -162,10 +164,10 @@ class VirtualWorkspace:
|
|||
try:
|
||||
buff_ctl = await self.handle.attach(id)
|
||||
except Exception as e:
|
||||
status_log(f"error when attaching to buffer '{id}':\n\n {e}", True)
|
||||
logging.error(f"error when attaching to buffer '{id}':\n\n {e}", True)
|
||||
return
|
||||
|
||||
vbuff = Buffer.VirtualBuffer(self, id, buff_ctl)
|
||||
vbuff = VirtualBuffer(self.id, self.rootdir, id, buff_ctl)
|
||||
self.add_buffer(id, vbuff)
|
||||
|
||||
# TODO! if the view is already active calling focus_view() will not trigger the on_activate
|
||||
|
@ -177,7 +179,7 @@ class VirtualWorkspace:
|
|||
|
||||
attached_Buffer = self.handle.buffer_by_name(id)
|
||||
if attached_Buffer is None:
|
||||
status_log(f"You are not attached to the buffer '{id}'", True)
|
||||
logging.warning(f"You are not attached to the buffer '{id}'", True)
|
||||
return
|
||||
|
||||
self.handle.detach(id)
|
||||
|
@ -190,7 +192,7 @@ class VirtualWorkspace:
|
|||
await self.handle.fetch_buffers()
|
||||
existing_Buffer = self.handle.filetree()
|
||||
if id not in existing_Buffer:
|
||||
status_log(f"The buffer '{id}' does not exists.", True)
|
||||
logging.info(f"The buffer '{id}' does not exists.", True)
|
||||
return
|
||||
# delete a buffer that exists but you are not attached to
|
||||
attached_Buffer = self.handle.buffer_by_name(id)
|
||||
|
@ -204,7 +206,9 @@ class VirtualWorkspace:
|
|||
try:
|
||||
await self.handle.delete(id)
|
||||
except Exception as e:
|
||||
status_log(f"error when deleting the buffer '{id}':\n\n {e}", True)
|
||||
logging.error(
|
||||
f"error when deleting the buffer '{id}':\n\n {e}", True
|
||||
)
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
@ -221,11 +225,11 @@ class VirtualWorkspace:
|
|||
try:
|
||||
await self.handle.delete(id)
|
||||
except Exception as e:
|
||||
status_log(f"error when deleting the buffer '{id}':\n\n {e}", True)
|
||||
logging.error(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}'...")
|
||||
logger.debug(f"spinning up cursor worker for workspace '{self.id}'...")
|
||||
try:
|
||||
while cursor_event := await self.curctl.recv():
|
||||
vbuff = self.get_by_remote(cursor_event.buffer)
|
||||
|
@ -247,8 +251,8 @@ class VirtualWorkspace:
|
|||
)
|
||||
|
||||
except CancelledError:
|
||||
status_log(f"cursor worker for '{self.id}' stopped...")
|
||||
logger.debug(f"cursor worker for '{self.id}' stopped...")
|
||||
raise
|
||||
except Exception as e:
|
||||
status_log(f"cursor worker '{self.id}' crashed:\n{e}")
|
||||
logger.error(f"cursor worker '{self.id}' crashed:\n{e}")
|
||||
raise
|
||||
|
|
Loading…
Reference in a new issue