feat(lsp, ui, core): refactor LSP initialization, improve config handling, and clean up TreeSitter

* LSP Client & Manager
  Refactored initialization flow to use internal state instead of passing params
  Added workspace_path and init_opts to LSPClient
  Simplified send_initialize_message() (no external args)
  Updated manager/client APIs to require explicit workspace_path and typed init_opts
  Improved client lifecycle handling and UI button toggling

* LSP Manager UI
  Added {user.home} substitution support in configs
  Use live editor buffer for JSON config parsing instead of static template
  Minor cleanup and formatting improvements

* Core Widgets
  Prevent external modification checks for buffer-only files in SourceFile
  Minor formatting cleanup in SourceView

* Plugins / Manifest
  Expanded manifest schema: added description and copyright, reordered fields

* Cleanup
  Removed TreeSitter compile script and TODO entry
  General code formatting and small consistency fixes

chore: remove unused TreeSitter tooling and related TODO entry
This commit is contained in:
2026-03-28 16:14:04 -05:00
parent 70877a7ee1
commit dc2997ec16
10 changed files with 62 additions and 78 deletions

View File

@@ -1,6 +1,5 @@
___ ___
### Add ### Add
1. Add TreeSitter
1. Add Collapsable code blocks 1. Add Collapsable code blocks
1. Add Godot LSP Client 1. Add Godot LSP Client
1. Add Terminal plugin 1. Add Terminal plugin

View File

@@ -1,39 +0,0 @@
from .libs.tree_sitter_language_pack import \
init, download, get_language, get_parser, available_languages, process, ProcessConfig
# Optional: Pre-download specific languages for offline use
#init(["python", "javascript", "rust"])
# Get a language (auto-downloads if not cached)
language = get_language("python")
# Get a pre-configured parser (auto-downloads if needed)
parser = get_parser("python")
tree = parser.parse(b"def hello(): pass")
print(tree.root_node)
# List all available languages
for lang in available_languages():
print(lang)
# Extract file intelligence (auto-downloads language if needed)
result = process(
"def hello(): pass",
ProcessConfig( language = "python")
)
print(f"Functions: {len(result['structure'])}")
# Pre-download languages for offline use
download(["python", "javascript"])
# With chunking
result = process(
source,
ProcessConfig(
language = "python",
chunk_max_size = 1000,
comments = True
)
)
print(f"Chunks: {len(result['chunks'])}")

View File

@@ -19,13 +19,10 @@ class LSPClient(LSPClientWebsocket):
def __init__(self): def __init__(self):
super(LSPClient, self).__init__() super(LSPClient, self).__init__()
# https://github.com/microsoft/multilspy/tree/main/src/multilspy/language_servers
# initialize-params-slim.json was created off of jedi_language_server one
# self._init_params = settings_manager.get_lsp_init_data()
self._language: str = "" self._language: str = ""
self._workspace_path: str = ""
self._init_params: dict = {} self._init_params: dict = {}
self._event_history: dict[int, str] = {} self._init_opts: dict = {}
try: try:
_USER_HOME = path.expanduser('~') _USER_HOME = path.expanduser('~')
@@ -33,19 +30,29 @@ class LSPClient(LSPClientWebsocket):
_LSP_INIT_CONFIG = f"{_SCRIPT_PTH}/../configs/initialize-params-slim.json" _LSP_INIT_CONFIG = f"{_SCRIPT_PTH}/../configs/initialize-params-slim.json"
with open(_LSP_INIT_CONFIG) as file: with open(_LSP_INIT_CONFIG) as file:
data = file.read().replace("{user.home}", _USER_HOME) data = file.read()
self._init_params = json.loads(data) self._init_params = json.loads(data)
except Exception as e: except Exception as e:
logger.error( f"LSP Controller: {_LSP_INIT_CONFIG}\n\t\t{repr(e)}" ) logger.error( f"LSP Controller: {_LSP_INIT_CONFIG}\n\t\t{repr(e)}" )
self._message_id: int = -1
self._socket = None self._socket = None
self.read_lock = threading.Lock() self._message_id: int = -1
self.write_lock = threading.Lock() self._event_history: dict[int, str] = {}
self.read_lock = threading.Lock()
self.write_lock = threading.Lock()
def set_language(self, language: str): def set_language(self, language: str):
self._language = language self._language = language
def set_workspace_path(self, workspace_path: str):
self._workspace_path = workspace_path
def set_init_opts(self, init_opts: dict[str, str]):
self._init_opts = init_opts
def set_socket(self, socket: str): def set_socket(self, socket: str):
self._socket = socket self._socket = socket

View File

@@ -17,11 +17,12 @@ from ..dto.code.lsp.lsp_messages import symbols_request
class LSPClientEvents: class LSPClientEvents:
def send_initialize_message(self, init_ops: dict, workspace_file: str, workspace_uri: str): def send_initialize_message(self):
folder_name = os.path.basename(workspace_file) folder_name = os.path.basename(self._workspace_path)
workspace_uri = f"file://{self._workspace_path}"
self._init_params["processId"] = None self._init_params["processId"] = None
self._init_params["rootPath"] = workspace_file self._init_params["rootPath"] = self._workspace_path
self._init_params["rootUri"] = workspace_uri self._init_params["rootUri"] = workspace_uri
self._init_params["workspaceFolders"] = [ self._init_params["workspaceFolders"] = [
{ {
@@ -30,7 +31,7 @@ class LSPClientEvents:
} }
] ]
self._init_params["initializationOptions"] = init_ops self._init_params["initializationOptions"] = self._init_opts
self.send_request("initialize", self._init_params) self.send_request("initialize", self._init_params)
def send_initialized_message(self): def send_initialized_message(self):

View File

@@ -54,17 +54,21 @@ class LSPManager(ControllerBase):
self.response_registry.unregister_handler(event.lang_id) self.response_registry.unregister_handler(event.lang_id)
self.lsp_manager_ui.remove_client_listing(event.lang_id) self.lsp_manager_ui.remove_client_listing(event.lang_id)
def _on_create_client(self, ui, lang_id: str, workspace_uri: str) -> bool: def _on_create_client(self, ui, lang_id: str, workspace_path: str) -> bool:
init_opts = ui.get_init_opts(lang_id) init_opts = ui.get_init_opts(lang_id)
result = self.create_client(lang_id, workspace_uri, init_opts) result = self.create_client(lang_id, workspace_path, init_opts)
if result: if result:
ui.toggle_client_buttons(show_close=True) ui.toggle_client_buttons(show_close = True)
return result return result
def _on_close_client(self, ui, lang_id: str) -> bool: def _on_close_client(self, ui, lang_id: str) -> bool:
result = self.close_client(lang_id) result = self.close_client(lang_id)
if result: if result:
ui.toggle_client_buttons(show_close=False) ui.toggle_client_buttons(show_close = False)
return result return result
def handle_destroy(self): def handle_destroy(self):
@@ -73,12 +77,12 @@ class LSPManager(ControllerBase):
def create_client( def create_client(
self, self,
lang_id: str = "python", lang_id: str,
workspace_uri: str = "", workspace_path: str,
init_opts: dict = {} init_opts: dict[str, str]
) -> bool: ) -> bool:
client = self.lsp_manager_client.create_client( client = self.lsp_manager_client.create_client(
lang_id, workspace_uri, init_opts lang_id, workspace_path, init_opts
) )
handler = self.response_registry.get_handler(lang_id) handler = self.response_registry.get_handler(lang_id)
self.lsp_manager_client.active_language_id = lang_id self.lsp_manager_client.active_language_id = lang_id
@@ -92,7 +96,7 @@ class LSPManager(ControllerBase):
handler.set_response_cache(self.response_cache) handler.set_response_cache(self.response_cache)
client.handle_lsp_response = self.server_response client.handle_lsp_response = self.server_response
client.send_initialize_message(init_opts, "", f"file://{workspace_uri}") client.send_initialize_message()
return True return True

View File

@@ -22,9 +22,9 @@ class LSPManagerClient(LSPClientEventsMixin):
def create_client( def create_client(
self, self,
lang_id: str = "python", lang_id: str,
workspace_uri: str = "", workspace_path: str,
init_opts: dict = {} init_opts: dict[str, str]
) -> LSPClient: ) -> LSPClient:
if lang_id in self.clients: return None if lang_id in self.clients: return None
@@ -33,8 +33,10 @@ class LSPManagerClient(LSPClientEventsMixin):
uri = f"ws://{address}:{port}/{lang_id}" uri = f"ws://{address}:{port}/{lang_id}"
client = LSPClient() client = LSPClient()
client.set_language(lang_id)
client.set_socket(uri) client.set_socket(uri)
client.set_language(lang_id)
client.set_workspace_path(workspace_path)
client.set_init_opts(init_opts)
client.start_client() client.start_client()
if not client.ws_client.wait_for_connection(timeout = 5.0): if not client.ws_client.wait_for_connection(timeout = 5.0):

View File

@@ -1,4 +1,5 @@
# Python imports # Python imports
from os import path
import json import json
# Lib imports # Lib imports
@@ -24,9 +25,11 @@ class LSPManagerUI(Gtk.Dialog):
def __init__(self): def __init__(self):
super(LSPManagerUI, self).__init__() super(LSPManagerUI, self).__init__()
self._USER_HOME = path.expanduser('~')
self.client_configs: dict[str, str] = {} self.client_configs: dict[str, str] = {}
self.source_view = None self.source_view = None
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
@@ -167,9 +170,11 @@ class LSPManagerUI(Gtk.Dialog):
lang_id = self.combo_box.get_active_text() lang_id = self.combo_box.get_active_text()
if not lang_id: return if not lang_id: return
json_str = self.client_configs[lang_id].replace("{workspace.folder}", workspace_dir) json_str = self.client_configs[lang_id] \
buffer = self.source_view.get_buffer() .replace("{workspace.folder}", workspace_dir) \
.replace("{user.home}", self._USER_HOME)
buffer = self.source_view.get_buffer()
buffer.set_text(json_str, -1) buffer.set_text(json_str, -1)
def map_parent_resize_event(self, parent): def map_parent_resize_event(self, parent):
@@ -204,7 +209,7 @@ class LSPManagerUI(Gtk.Dialog):
model = self.combo_box.get_model() model = self.combo_box.get_model()
for i, row in enumerate(model): for i, row in enumerate(model):
if row[0] == lang_id: # assuming text is in column 0 if row[0] == lang_id:
self.combo_box.remove(i) self.combo_box.remove(i)
break break
@@ -215,7 +220,9 @@ class LSPManagerUI(Gtk.Dialog):
if not lang_id or lang_id not in self.client_configs: return {} if not lang_id or lang_id not in self.client_configs: return {}
try: try:
lang_config = json.loads(self.client_configs[lang_id]) buffer = self.source_view.get_buffer()
json_str = buffer.get_text(*buffer.get_bounds(), -1)
lang_config = json.loads(json_str)
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
logger.error(f"Invalid JSON for {lang_id}: {e}") logger.error(f"Invalid JSON for {lang_id}: {e}")
return {} return {}

View File

@@ -155,9 +155,10 @@ class SourceFile(GtkSource.File):
self.buffer.unblock_modified_changed_signal() self.buffer.unblock_modified_changed_signal()
def is_externally_modified(self) -> bool: def is_externally_modified(self) -> bool:
stat = os.stat(self.fpath) if self.fname == "buffer": return
current = (stat.st_mtime_ns, stat.st_size)
stat = os.stat(self.fpath)
current = (stat.st_mtime_ns, stat.st_size)
is_modified = \ is_modified = \
hasattr(self, "last_state") and not current == self.last_state hasattr(self, "last_state") and not current == self.last_state

View File

@@ -19,10 +19,10 @@ class SourceView(GtkSource.View, SourceViewDnDMixin):
def __init__(self, state: SourceViewStates = SourceViewStates.INSERT): def __init__(self, state: SourceViewStates = SourceViewStates.INSERT):
super(SourceView, self).__init__() super(SourceView, self).__init__()
self.state = state self.state = state
self.sibling_right = None self.sibling_right = None
self.sibling_left = None self.sibling_left = None
self._setup_styles() self._setup_styles()
self._setup_signals() self._setup_signals()

View File

@@ -13,9 +13,11 @@ from .requests import Requests
class Manifest: class Manifest:
name: str = "" name: str = ""
author: str = "" author: str = ""
credit: str = "" description: str = ""
version: str = "0.0.1" version: str = "0.0.1"
support: str = "support@mail.com" support: str = "support@mail.com"
credit: str = ""
copyright: str = "GPLv2"
pre_launch: bool = False pre_launch: bool = False
autoload: bool = True autoload: bool = True
requests: Requests = field(default_factory = lambda: Requests()) requests: Requests = field(default_factory = lambda: Requests())