From 7263b6f5305f559ece42f11e8dcc28db9cc3cb71 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Tue, 12 Aug 2025 07:48:05 +0200 Subject: [PATCH] optimze Server --- server/src/lsp_server.py | 70 ++++++++++++++++++++++--------------- server/src/lsp_tclserver.py | 38 +++++++++++++++++--- 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/server/src/lsp_server.py b/server/src/lsp_server.py index aa33920..a73491f 100644 --- a/server/src/lsp_server.py +++ b/server/src/lsp_server.py @@ -9,6 +9,8 @@ import os import pathlib import re import sys +import threading + from typing import Any, Optional import operator from functools import reduce @@ -151,7 +153,9 @@ def inlay_hints(params: lsp.InlayHintParams): if not GLOBAL_SETTINGS.get("inlayHint", False): return [] document = LSP_SERVER.workspace.get_text_document(params.text_document.uri) - tree = LSP_SERVER.parser.parse(document.source) + + # Reuse cached AST + tree = LSP_SERVER.get_tree(document) # Merge proc signatures across files and traverse once merged_signatures = {} @@ -177,7 +181,8 @@ def semantic_tokens(params: lsp.SemanticTokensParams): plugins = [] hl = _Highlighter(plugins, LSP_SERVER.poco_completion) - tree = LSP_SERVER.parser.parse(document.source) + # Reuse cached AST + tree = LSP_SERVER.get_tree(document) tree.accept(hl, recurse=True) tokens = hl.tokens() @@ -407,33 +412,42 @@ def initialize(params: lsp.InitializeParams) -> lsp.InitializeResult: @LSP_SERVER.feature(lsp.INITIALIZED) -def initialized(params: lsp.InitializedParams): - root = LSP_SERVER.workspace.root_path - psc_files = get_all_psc_files(pathlib.Path(root)) - for psc_file in psc_files: - poco_files = read_psc_file(psc_file) - for sourced_layer in poco_files: - completion.reset() - try: - file_root = pathlib.Path(root).joinpath(sourced_layer.subfolder if sourced_layer.subfolder else "") - for tcl_file in sourced_layer.files: - filepath = pathlib.Path(file_root).joinpath(f"{tcl_file}.tcl") - if not filepath.exists(): - continue - completion.reset() - document = LSP_SERVER.workspace.get_text_document(filepath.as_uri()) # filepath.read_text(encoding="utf-8") - tree = LSP_SERVER.parser.parse(document.source) - tree.accept(completion, recurse=True) - remove_existing_items(completion.custom_functions, LSP_SERVER.poco_completion) - LSP_SERVER.poco_completion[str(filepath)] = completion.custom_functions - remove_shared_keys(LSP_SERVER.proc_signatures, completion.proc_signatures) - LSP_SERVER.proc_signatures[str(filepath)] = completion.proc_signatures - # Build proc docs for this file - from tools.proc_docs import build_proc_docs +def initialized(_params: lsp.InitializedParams): + """Kick off background indexing to avoid blocking initialization.""" - LSP_SERVER.proc_docs[str(filepath)] = build_proc_docs(tree, document.source) - except Exception as e: - log_to_output(f"Fehler beim Parsen von {filepath}: {e}") + def index_workspace(): + try: + root = LSP_SERVER.workspace.root_path + log_to_output("Background indexing started...") + psc_files = get_all_psc_files(pathlib.Path(root)) + for psc_file in psc_files: + poco_files = read_psc_file(psc_file) + for sourced_layer in poco_files: + completion.reset() + try: + file_root = pathlib.Path(root).joinpath(sourced_layer.subfolder if sourced_layer.subfolder else "") + for tcl_file in sourced_layer.files: + filepath = pathlib.Path(file_root).joinpath(f"{tcl_file}.tcl") + if not filepath.exists(): + continue + completion.reset() + document = LSP_SERVER.workspace.get_text_document(filepath.as_uri()) + tree = LSP_SERVER.parser.parse(document.source) + tree.accept(completion, recurse=True) + remove_existing_items(completion.custom_functions, LSP_SERVER.poco_completion) + LSP_SERVER.poco_completion[str(filepath)] = completion.custom_functions + remove_shared_keys(LSP_SERVER.proc_signatures, completion.proc_signatures) + LSP_SERVER.proc_signatures[str(filepath)] = completion.proc_signatures + from tools.proc_docs import build_proc_docs + + LSP_SERVER.proc_docs[str(filepath)] = build_proc_docs(tree, document.source) + except Exception as e: + log_to_output(f"Fehler beim Parsen von {filepath}: {e}") + log_to_output("Background indexing completed.") + except Exception as e: + log_to_output(f"Background indexing failed: {e}") + + threading.Thread(target=index_workspace, name="nxps-indexer", daemon=True).start() @LSP_SERVER.feature(lsp.EXIT) diff --git a/server/src/lsp_tclserver.py b/server/src/lsp_tclserver.py index c88db35..d59a340 100644 --- a/server/src/lsp_tclserver.py +++ b/server/src/lsp_tclserver.py @@ -26,6 +26,37 @@ class TclLanguageServer(server.LanguageServer): self.poco_completion: dict = {} self.proc_signatures: dict = {} self.proc_docs: dict = {} + # Cache: (uri, version) -> (tree, violations) + self._ast_cache = {} + + def get_tree(self, document: TextDocument): + key = (document.uri, document.version) + cached = self._ast_cache.get(key) + if cached: + return cached[0] + # Parse and cache + self.parser.violations = [] + tree = self.parser.parse(document.source) + violations = list(self.parser.violations) + self._ast_cache[key] = (tree, violations) + return tree + + def get_tree_and_violations(self, document: TextDocument): + key = (document.uri, document.version) + cached = self._ast_cache.get(key) + if cached: + return cached + # Parse and cache + self.parser.violations = [] + tree = self.parser.parse(document.source) + violations = list(self.parser.violations) + self._ast_cache[key] = (tree, violations) + return tree, violations + + def clear_cache_for_uri(self, uri: str): + to_delete = [k for k in self._ast_cache.keys() if k[0] == uri] + for k in to_delete: + del self._ast_cache[k] def update_poco_completion_for_file(self, document: TextDocument): """Update poco_completion for a specific file when it changes""" @@ -42,7 +73,7 @@ class TclLanguageServer(server.LanguageServer): # Parse and extract new completion items completion.reset() try: - tree = self.parser.parse(document.source) + tree = self.get_tree(document) tree.accept(completion, recurse=True) remove_existing_items(completion.custom_functions, self.poco_completion) self.poco_completion[filepath] = completion.custom_functions @@ -81,10 +112,7 @@ class TclLanguageServer(server.LanguageServer): self, document: TextDocument, ) -> List[Violation]: - violations = [] - self.parser.violations = [] - tree = self.parser.parse(document.source) - violations += self.parser.violations + tree, violations = self.get_tree_and_violations(document) for checker in checks.get_checkers(): violations += checker.check(document.source, tree) return violations