This commit is contained in:
Christoph Brandau
2025-08-01 13:35:02 +02:00
parent 98e2d104a8
commit 5e2e85830b
6 changed files with 178 additions and 9 deletions
+5 -5
View File
@@ -179,11 +179,11 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(formatDefProvider)
const tclOutlineProvider = vscode.languages.registerDocumentSymbolProvider(
{ scheme: "file", language: "tcl" },
{ provideDocumentSymbols: tclDocumentSymbolProvider }
)
context.subscriptions.push(tclOutlineProvider)
// const tclOutlineProvider = vscode.languages.registerDocumentSymbolProvider(
// { scheme: "file", language: "tcl" },
// { provideDocumentSymbols: tclDocumentSymbolProvider }
// )
// context.subscriptions.push(tclOutlineProvider)
// Diagnostics collection
const diagnosticCollectionCdl = vscode.languages.createDiagnosticCollection("cdl")
+25
View File
@@ -55,6 +55,8 @@ from plugins.poco_plugin import commands
from tools import checks, parser
from tools.semantic_tokens import _Highlighter, TOKEN_TYPES, TokenModifier
from tools.completion_items import completion
from tools.inlay_hint import InlayHintGenerator
from tools.symbols import OutlineVisitor
DIAGNOSTIC_SOURCE = "nx-post-support"
@@ -259,6 +261,29 @@ def on_completion(params: lsp.CompletionParams) -> list[lsp.CompletionItem]:
return lsp.CompletionList(is_incomplete=False, items=items)
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
def document_symbols(params: lsp.DocumentSymbolParams):
doc = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
tree = LSP_SERVER.parser.parse(doc.source)
visitor = OutlineVisitor(LSP_SERVER.parser)
tree.accept(visitor, recurse=True)
return visitor.stack[0]
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_INLAY_HINT)
def inlay_hints(params: lsp.InlayHintParams):
document = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
tree = LSP_SERVER.parser.parse(document.source)
# Inlay Hints sammeln
generator = InlayHintGenerator(completion.proc_signatures)
tree.accept(generator, recurse=True)
return generator.hints
@LSP_SERVER.feature(
lsp.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
lsp.SemanticTokensLegend(
+25 -2
View File
@@ -1,4 +1,4 @@
from tclint.syntax_tree import Visitor
from tclint.syntax_tree import Visitor, Command, BareWord, List
import lsprotocol.types as lsp
@@ -19,15 +19,21 @@ class _Completion(Visitor):
def __init__(self):
super().__init__()
self._custom_functions: list[lsp.CompletionItem] = []
self._proc_signatures = {}
@property
def custom_functions(self) -> list[lsp.CompletionItem]:
return self._custom_functions
@property
def proc_signatures(self):
return self._proc_signatures
def reset(self):
self._custom_functions = []
self._proc_signatures = {}
def visit_command(self, command):
def visit_command(self, command: Command):
routine = command.routine
if routine.contents == "proc" and command.args:
@@ -40,6 +46,23 @@ class _Completion(Visitor):
label=first_arg.value, kind=lsp.CompletionItemKind.Function
)
)
if len(command.args) < 2:
return
param_list_node = command.args[1]
if not hasattr(param_list_node, "children"):
return
param_names = []
for arg in param_list_node.children:
if isinstance(arg, BareWord):
param_names.append(arg.value)
elif isinstance(arg, List) and len(arg.children) >= 1:
first = arg.children[0]
if isinstance(first, BareWord):
param_names.append(first.value)
self._proc_signatures[first_arg.value] = param_names
completion = _Completion()
+29
View File
@@ -0,0 +1,29 @@
import lsprotocol.types as lsp
from tclint.syntax_tree import Visitor, Command
class InlayHintGenerator(Visitor):
def __init__(self, proc_signatures):
self.proc_signatures = proc_signatures
self.hints = []
def visit_command(self, command: Command):
name = getattr(command.routine, "contents", None)
if name not in self.proc_signatures:
return
param_names = self.proc_signatures[name]
for idx, arg in enumerate(command.args):
if idx >= len(param_names):
break
param_name = param_names[idx]
if arg.pos:
line, col = arg.pos
self.hints.append(
lsp.InlayHint(
position=lsp.Position(line=line - 1, character=col - 1),
label=f"{param_name}:",
kind=lsp.InlayHintKind.Parameter,
)
)
+82
View File
@@ -0,0 +1,82 @@
from tclint.syntax_tree import Visitor, BareWord, BracedWord, Script, Command
from tclint.parser import Parser
import lsprotocol.types as lsp
class OutlineVisitor(Visitor):
def __init__(self, parser: Parser):
self.parser = parser
self.stack = [[]] # Root symbol list
def _range(self, node) -> lsp.Range:
line = node.line - 1
col = node.col - 1
if node.end_pos:
end_line = node.end_pos[0] - 1
end_col = node.end_pos[1] - 1
else:
end_line = line
end_col = col + 1
return lsp.Range(
start=lsp.Position(line=line, character=col),
end=lsp.Position(line=end_line, character=end_col),
)
def _add(self, name: str, kind: lsp.SymbolKind, node, children=None):
symbol = lsp.DocumentSymbol(
name=name,
kind=kind,
range=self._range(node),
selection_range=self._range(node),
children=children or [],
)
self.stack[-1].append(symbol)
return symbol
def visit_script(self, script):
for child in script.children:
child.accept(self, recurse=False)
def visit_command(self, command: Command):
if not isinstance(command.routine, BareWord):
return
name = command.routine.contents
# --- NAMESPACE EVAL ---
if name == "namespace" and len(command.args) >= 3:
subcmd = command.args[0]
if isinstance(subcmd, BareWord) and subcmd.contents == "eval":
ns_arg = command.args[1]
ns_name = (
ns_arg.contents if isinstance(ns_arg, BareWord) else "<namespace>"
)
ns_body = command.args[2]
ns_symbol = self._add(
ns_name, lsp.SymbolKind.Namespace, command, children=[]
)
self.stack.append(ns_symbol.children)
if isinstance(ns_body, BracedWord):
try:
subtree = self.parser.parse_script(ns_body)
subtree.accept(self, recurse=False)
except Exception as e:
print(f"Failed parsing namespace body: {e}")
self.stack.pop()
# --- PROC ---
elif name == "proc" and len(command.args) >= 1:
proc_arg = command.args[0]
proc_name = (
proc_arg.contents if isinstance(proc_arg, BareWord) else "<proc>"
)
self._add(proc_name, lsp.SymbolKind.Function, command)
# --- SET ---
elif name == "set" and len(command.args) >= 1:
var_arg = command.args[0]
var_name = var_arg.contents if isinstance(var_arg, BareWord) else "<var>"
self._add(var_name, lsp.SymbolKind.Variable, command)
+12 -2
View File
@@ -5,21 +5,31 @@ if {$main == 1 && 1 == 1} {
proc test {} {
puts "main"
proc llll {} {}
set rrrrrrr
}
LIB_GE_command_buffer_edit_insert MOM_tool_change_LIB TOOL_CHANGE_AUTO {CUSTOM_after_tool_change_call} mytag after @TOOL_CHANGE_AUTO
MOM_abort
namespace eval myns {
proc add {a b} {
set sum [expr {$a + $b}]
return $sum
}
set config "debug"
}
#_________________________________________________________________________________________________
# <Documentation>
# Function to output a spacer line or empty line
#_________________________________________________________________________________________________
proc SERVICE_spacer_output {type {length 20} {line_num 0} {output 1} check} {
proc SERVICE_spacer_output {type {length 20} {line_num 0} {output 1}} {
LIB_GE_message [string repeat $type $length] "output_$output" $line_num
}
SERVICE_spacer_output "*" 50 0 1
SERVICE_remove_file $filename
#_________________________________________________________________________________________________
# <Documentation>