refacxtor the code
This commit is contained in:
+20
-198
@@ -9,7 +9,7 @@ import os
|
|||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, Optional
|
||||||
import operator
|
import operator
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
@@ -49,7 +49,6 @@ from lsp_tclserver import TclLanguageServer
|
|||||||
|
|
||||||
WORKSPACE_SETTINGS = {}
|
WORKSPACE_SETTINGS = {}
|
||||||
GLOBAL_SETTINGS = {}
|
GLOBAL_SETTINGS = {}
|
||||||
RUNNER = pathlib.Path(__file__).parent / "lsp_runner.py"
|
|
||||||
|
|
||||||
|
|
||||||
MAX_WORKERS = 5
|
MAX_WORKERS = 5
|
||||||
@@ -58,9 +57,7 @@ LSP_SERVER = TclLanguageServer(name="NX Postprocessor Support", version="0.0.1",
|
|||||||
# **********************************************************
|
# **********************************************************
|
||||||
# Tool specific code goes below this.
|
# Tool specific code goes below this.
|
||||||
# **********************************************************
|
# **********************************************************
|
||||||
TOOL_MODULE = "nx-post-support"
|
|
||||||
TOOL_DISPLAY = "NX Postprocessor Support"
|
|
||||||
TOOL_ARGS = [] # default arguments always passed to your tool.
|
|
||||||
|
|
||||||
# Delete "Linting features" section if your tool is NOT a linter.
|
# Delete "Linting features" section if your tool is NOT a linter.
|
||||||
# **********************************************************
|
# **********************************************************
|
||||||
@@ -87,7 +84,7 @@ def did_save(params: lsp.DidSaveTextDocumentParams) -> None:
|
|||||||
|
|
||||||
|
|
||||||
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DID_CLOSE)
|
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DID_CLOSE)
|
||||||
def did_close(params: lsp.DidCloseTextDocumentParams) -> None:
|
def did_close(_: lsp.DidCloseTextDocumentParams) -> None:
|
||||||
"""LSP handler for textDocument/didClose request."""
|
"""LSP handler for textDocument/didClose request."""
|
||||||
|
|
||||||
|
|
||||||
@@ -125,12 +122,9 @@ def document_diagnostic(params: lsp.DocumentDiagnosticParams):
|
|||||||
|
|
||||||
|
|
||||||
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_COMPLETION)
|
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_COMPLETION)
|
||||||
def on_completion(params: lsp.CompletionParams) -> list[lsp.CompletionItem]:
|
def on_completion(params: lsp.CompletionParams) -> lsp.CompletionList:
|
||||||
_ = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
_ = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
||||||
poco = []
|
poco = [item for items in LSP_SERVER.poco_completion.values() for item in items]
|
||||||
|
|
||||||
for key, value in LSP_SERVER.poco_completion.items():
|
|
||||||
poco.extend(value)
|
|
||||||
items = standard_items.tcl_keyword_list + standard_items.nx_procs + standard_items.nx_variables + poco
|
items = standard_items.tcl_keyword_list + standard_items.nx_procs + standard_items.nx_variables + poco
|
||||||
return lsp.CompletionList(is_incomplete=False, items=items)
|
return lsp.CompletionList(is_incomplete=False, items=items)
|
||||||
|
|
||||||
@@ -146,183 +140,10 @@ def on_completion(params: lsp.CompletionParams) -> list[lsp.CompletionItem]:
|
|||||||
|
|
||||||
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
|
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
|
||||||
def document_symbols(params: lsp.DocumentSymbolParams):
|
def document_symbols(params: lsp.DocumentSymbolParams):
|
||||||
|
from tools.document_symbols import build_document_symbols
|
||||||
|
|
||||||
doc = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
doc = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
||||||
lines = doc.source.split("\n")
|
return build_document_symbols(doc.source)
|
||||||
|
|
||||||
# Regex patterns similar to the previous TS implementation
|
|
||||||
ns_re = re.compile(r"^\s*namespace\s+eval\s+([^\s\{]+)")
|
|
||||||
proc_re = re.compile(r"^\s*proc\s+([^\s\{]+)\s+\{.*\}\s+\{")
|
|
||||||
set_re = re.compile(r"^\s*set\s+([^\s\}]+)")
|
|
||||||
event_start_re = re.compile(r"^\s*LIB_GE_command_buffer_edit_(prepend|append|insert|replace)\b")
|
|
||||||
event_close_inline_re = re.compile(r"^\s*\}\s*(\S+)\s*.*$")
|
|
||||||
event_close_brace_only_re = re.compile(r"^\s*\}\s*$")
|
|
||||||
event_name_line_re = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\b")
|
|
||||||
pending_event = None
|
|
||||||
|
|
||||||
class Scope:
|
|
||||||
def __init__(self, name: str, symbol: lsp.DocumentSymbol, start_line: int):
|
|
||||||
self.name = name
|
|
||||||
self.symbol = symbol
|
|
||||||
self.start_line = start_line
|
|
||||||
self.brace_count = 0
|
|
||||||
|
|
||||||
root_symbol = lsp.DocumentSymbol(
|
|
||||||
name="root",
|
|
||||||
detail="",
|
|
||||||
kind=lsp.SymbolKind.Namespace,
|
|
||||||
range=lsp.Range(start=lsp.Position(0, 0), end=lsp.Position(len(lines), 0)),
|
|
||||||
selection_range=lsp.Range(start=lsp.Position(0, 0), end=lsp.Position(0, 0)),
|
|
||||||
children=[],
|
|
||||||
)
|
|
||||||
|
|
||||||
scope_stack: list[Scope] = [Scope("", root_symbol, 0)]
|
|
||||||
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
# If inside an event, update its brace count for this line
|
|
||||||
if "pending_event" in locals() and pending_event is not None:
|
|
||||||
pe_open = line.count("{")
|
|
||||||
pe_close = line.count("}")
|
|
||||||
pending_event["brace_count"] = pending_event.get("brace_count", 1) + pe_open - pe_close
|
|
||||||
# If this line closed the outer event block, finalize the event
|
|
||||||
if pending_event["brace_count"] <= 0:
|
|
||||||
# Try inline name on the same line
|
|
||||||
close_inline = event_close_inline_re.match(line)
|
|
||||||
if close_inline:
|
|
||||||
event_name = close_inline.group(1)
|
|
||||||
name_line_index = i
|
|
||||||
else:
|
|
||||||
# Look ahead to next non-empty line for the name
|
|
||||||
j = i + 1
|
|
||||||
while j < len(lines) and lines[j].strip() == "":
|
|
||||||
j += 1
|
|
||||||
event_name = None
|
|
||||||
name_line_index = i
|
|
||||||
if j < len(lines):
|
|
||||||
name_line = lines[j]
|
|
||||||
name_match = event_name_line_re.match(name_line)
|
|
||||||
if name_match:
|
|
||||||
event_name = name_match.group(1)
|
|
||||||
name_line_index = j
|
|
||||||
if event_name:
|
|
||||||
start_line = pending_event["start"]
|
|
||||||
edit_type = pending_event["edit_type"]
|
|
||||||
children = pending_event.get("children", [])
|
|
||||||
detail = f"Event ({edit_type})"
|
|
||||||
event_symbol = lsp.DocumentSymbol(
|
|
||||||
name=event_name,
|
|
||||||
detail=detail,
|
|
||||||
kind=lsp.SymbolKind.Event,
|
|
||||||
range=lsp.Range(start=lsp.Position(start_line, 0), end=lsp.Position(name_line_index, len(lines[name_line_index]))),
|
|
||||||
selection_range=lsp.Range(start=lsp.Position(name_line_index, 0), end=lsp.Position(name_line_index, len(lines[name_line_index]))),
|
|
||||||
children=children or [],
|
|
||||||
)
|
|
||||||
if scope_stack[-1].symbol.children is None:
|
|
||||||
scope_stack[-1].symbol.children = []
|
|
||||||
scope_stack[-1].symbol.children.append(event_symbol)
|
|
||||||
# Clear event tracking and continue
|
|
||||||
pending_event = None
|
|
||||||
continue
|
|
||||||
ns_match = ns_re.match(line)
|
|
||||||
proc_match = proc_re.match(line)
|
|
||||||
set_match = set_re.match(line)
|
|
||||||
|
|
||||||
# Namespace
|
|
||||||
if ns_match:
|
|
||||||
ns_name = ns_match.group(1)
|
|
||||||
start = lsp.Position(i, 0)
|
|
||||||
end = lsp.Position(i, len(line))
|
|
||||||
sel_start_char = line.find(ns_name)
|
|
||||||
sel_end_char = sel_start_char + len(ns_name) if sel_start_char >= 0 else len(line)
|
|
||||||
ns_symbol = lsp.DocumentSymbol(
|
|
||||||
name=ns_name,
|
|
||||||
detail="Namespace",
|
|
||||||
kind=lsp.SymbolKind.Namespace,
|
|
||||||
range=lsp.Range(start=start, end=end),
|
|
||||||
selection_range=lsp.Range(
|
|
||||||
start=lsp.Position(i, max(sel_start_char, 0)),
|
|
||||||
end=lsp.Position(i, max(sel_end_char, 0)),
|
|
||||||
),
|
|
||||||
children=[],
|
|
||||||
)
|
|
||||||
scope = Scope(ns_name, ns_symbol, i)
|
|
||||||
if scope_stack[-1].symbol.children is None:
|
|
||||||
scope_stack[-1].symbol.children = []
|
|
||||||
scope_stack[-1].symbol.children.append(ns_symbol)
|
|
||||||
scope_stack.append(scope)
|
|
||||||
|
|
||||||
# Proc
|
|
||||||
elif proc_match:
|
|
||||||
proc_name = proc_match.group(1)
|
|
||||||
start = lsp.Position(i, 0)
|
|
||||||
end = lsp.Position(i, len(line))
|
|
||||||
sel_start_char = line.find(proc_name)
|
|
||||||
sel_end_char = sel_start_char + len(proc_name) if sel_start_char >= 0 else len(line)
|
|
||||||
proc_symbol = lsp.DocumentSymbol(
|
|
||||||
name=proc_name,
|
|
||||||
detail="Procedure",
|
|
||||||
kind=lsp.SymbolKind.Function,
|
|
||||||
range=lsp.Range(start=start, end=end),
|
|
||||||
selection_range=lsp.Range(
|
|
||||||
start=lsp.Position(i, max(sel_start_char, 0)),
|
|
||||||
end=lsp.Position(i, max(sel_end_char, 0)),
|
|
||||||
),
|
|
||||||
children=[],
|
|
||||||
)
|
|
||||||
scope = Scope(proc_name, proc_symbol, i)
|
|
||||||
if scope_stack[-1].symbol.children is None:
|
|
||||||
scope_stack[-1].symbol.children = []
|
|
||||||
scope_stack[-1].symbol.children.append(proc_symbol)
|
|
||||||
scope_stack.append(scope)
|
|
||||||
|
|
||||||
# set variable
|
|
||||||
elif set_match:
|
|
||||||
var_name = set_match.group(1)
|
|
||||||
start = lsp.Position(i, 0)
|
|
||||||
end = lsp.Position(i, len(line))
|
|
||||||
sel_start_char = line.find(var_name)
|
|
||||||
sel_end_char = sel_start_char + len(var_name) if sel_start_char >= 0 else len(line)
|
|
||||||
var_symbol = lsp.DocumentSymbol(
|
|
||||||
name=var_name,
|
|
||||||
detail="Variable",
|
|
||||||
kind=lsp.SymbolKind.Variable,
|
|
||||||
range=lsp.Range(start=start, end=end),
|
|
||||||
selection_range=lsp.Range(
|
|
||||||
start=lsp.Position(i, max(sel_start_char, 0)),
|
|
||||||
end=lsp.Position(i, max(sel_end_char, 0)),
|
|
||||||
),
|
|
||||||
children=None,
|
|
||||||
)
|
|
||||||
# Attach to current scope or pending event as child
|
|
||||||
if "pending_event" in locals() and pending_event is not None:
|
|
||||||
pending_event["children"].append(var_symbol)
|
|
||||||
else:
|
|
||||||
if scope_stack[-1].symbol.children is None:
|
|
||||||
scope_stack[-1].symbol.children = []
|
|
||||||
scope_stack[-1].symbol.children.append(var_symbol)
|
|
||||||
|
|
||||||
# Event start (buffer edit)
|
|
||||||
if event_start_re.match(line):
|
|
||||||
m = event_start_re.match(line)
|
|
||||||
if m:
|
|
||||||
edit_type = m.group(1)
|
|
||||||
pending_event = {"start": i, "edit_type": edit_type, "children": []}
|
|
||||||
continue
|
|
||||||
# Brace balancing for scopes (namespace/proc)
|
|
||||||
open_count = line.count("{")
|
|
||||||
close_count = line.count("}")
|
|
||||||
scope_stack[-1].brace_count += open_count - close_count
|
|
||||||
|
|
||||||
# Close finished scopes
|
|
||||||
while len(scope_stack) > 1 and scope_stack[-1].brace_count <= 0:
|
|
||||||
finished = scope_stack.pop()
|
|
||||||
# Set the full range from startLine to current line
|
|
||||||
finished.symbol.range = lsp.Range(
|
|
||||||
start=lsp.Position(finished.start_line, 0),
|
|
||||||
end=lsp.Position(i, len(line)),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Return top-level children
|
|
||||||
return root_symbol.children
|
|
||||||
|
|
||||||
|
|
||||||
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_INLAY_HINT)
|
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_INLAY_HINT)
|
||||||
@@ -332,13 +153,14 @@ def inlay_hints(params: lsp.InlayHintParams):
|
|||||||
document = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
document = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
||||||
tree = LSP_SERVER.parser.parse(document.source)
|
tree = LSP_SERVER.parser.parse(document.source)
|
||||||
|
|
||||||
# collect Inlay Hints
|
# Merge proc signatures across files and traverse once
|
||||||
hints = []
|
merged_signatures = {}
|
||||||
for key, value in LSP_SERVER.proc_signatures.items():
|
for sigs in LSP_SERVER.proc_signatures.values():
|
||||||
generator = InlayHintGenerator(LSP_SERVER.proc_signatures[key])
|
merged_signatures.update(sigs)
|
||||||
tree.accept(generator, recurse=True)
|
|
||||||
hints += generator.hints
|
generator = InlayHintGenerator(merged_signatures)
|
||||||
return hints
|
tree.accept(generator, recurse=True)
|
||||||
|
return generator.hints
|
||||||
|
|
||||||
|
|
||||||
@LSP_SERVER.feature(
|
@LSP_SERVER.feature(
|
||||||
@@ -364,7 +186,7 @@ def semantic_tokens(params: lsp.SemanticTokensParams):
|
|||||||
[
|
[
|
||||||
token.line,
|
token.line,
|
||||||
token.offset,
|
token.offset,
|
||||||
token.lenght,
|
token.length,
|
||||||
TOKEN_TYPES.index(token.tok_type),
|
TOKEN_TYPES.index(token.tok_type),
|
||||||
reduce(operator.or_, token.tok_modifiers, 0),
|
reduce(operator.or_, token.tok_modifiers, 0),
|
||||||
]
|
]
|
||||||
@@ -492,7 +314,7 @@ def goto_definition(params: lsp.DefinitionParams):
|
|||||||
|
|
||||||
# 2) Search in indexed files from proc_signatures
|
# 2) Search in indexed files from proc_signatures
|
||||||
# Build list of candidate files that declare this token as a proc
|
# Build list of candidate files that declare this token as a proc
|
||||||
candidate_files: List[str] = []
|
candidate_files: list[str] = []
|
||||||
for file_path, procs in LSP_SERVER.proc_signatures.items():
|
for file_path, procs in LSP_SERVER.proc_signatures.items():
|
||||||
if token in procs:
|
if token in procs:
|
||||||
candidate_files.append(file_path)
|
candidate_files.append(file_path)
|
||||||
@@ -553,7 +375,7 @@ def formatting(params: lsp.DocumentFormattingParams) -> list[lsp.TextEdit] | Non
|
|||||||
# Required Language Server Initialization and Exit handlers.
|
# Required Language Server Initialization and Exit handlers.
|
||||||
# **********************************************************
|
# **********************************************************
|
||||||
@LSP_SERVER.feature(lsp.WORKSPACE_DID_CHANGE_CONFIGURATION)
|
@LSP_SERVER.feature(lsp.WORKSPACE_DID_CHANGE_CONFIGURATION)
|
||||||
def did_change_configuration(params: lsp.DidChangeConfigurationParams):
|
def did_change_configuration(_: lsp.DidChangeConfigurationParams):
|
||||||
"""LSP Handler for Config Changes"""
|
"""LSP Handler for Config Changes"""
|
||||||
|
|
||||||
|
|
||||||
@@ -573,7 +395,7 @@ def initialize(params: lsp.InitializeParams) -> lsp.InitializeResult:
|
|||||||
log_to_output(f"Global settings:\r\n{json.dumps(GLOBAL_SETTINGS, indent=4, ensure_ascii=False)}\r\n")
|
log_to_output(f"Global settings:\r\n{json.dumps(GLOBAL_SETTINGS, indent=4, ensure_ascii=False)}\r\n")
|
||||||
semantic_tokens_legend = lsp.SemanticTokensLegend(
|
semantic_tokens_legend = lsp.SemanticTokensLegend(
|
||||||
token_types=TOKEN_TYPES,
|
token_types=TOKEN_TYPES,
|
||||||
token_modifiers=TokenModifier,
|
token_modifiers=[m.name for m in TokenModifier],
|
||||||
)
|
)
|
||||||
return lsp.InitializeResult(
|
return lsp.InitializeResult(
|
||||||
capabilities=lsp.ServerCapabilities(
|
capabilities=lsp.ServerCapabilities(
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import lsprotocol.types as lsp
|
||||||
|
|
||||||
|
# Precompiled regex patterns for performance
|
||||||
|
NS_RE = re.compile(r"^\s*namespace\s+eval\s+([^\s\{]+)")
|
||||||
|
PROC_RE = re.compile(r"^\s*proc\s+([^\s\{]+)\s+\{.*\}\s+\{")
|
||||||
|
SET_RE = re.compile(r"^\s*set\s+([^\s\}]+)")
|
||||||
|
EVENT_START_RE = re.compile(r"^\s*LIB_GE_command_buffer_edit_(prepend|append|insert|replace)\b")
|
||||||
|
EVENT_CLOSE_INLINE_RE = re.compile(r"^\s*\}\s*(\S+)\s*.*$")
|
||||||
|
EVENT_NAME_LINE_RE = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\b")
|
||||||
|
|
||||||
|
|
||||||
|
def build_document_symbols(source: str) -> list[lsp.DocumentSymbol]:
|
||||||
|
"""Parse a Tcl document source and return DocumentSymbols.
|
||||||
|
|
||||||
|
This mirrors the previous inline implementation in lsp_server, but is extracted
|
||||||
|
for readability and reuse.
|
||||||
|
"""
|
||||||
|
lines = source.split("\n")
|
||||||
|
|
||||||
|
class Scope:
|
||||||
|
def __init__(self, name: str, symbol: lsp.DocumentSymbol, start_line: int):
|
||||||
|
self.name = name
|
||||||
|
self.symbol = symbol
|
||||||
|
self.start_line = start_line
|
||||||
|
self.brace_count = 0
|
||||||
|
|
||||||
|
root_symbol = lsp.DocumentSymbol(
|
||||||
|
name="root",
|
||||||
|
detail="",
|
||||||
|
kind=lsp.SymbolKind.Namespace,
|
||||||
|
range=lsp.Range(start=lsp.Position(0, 0), end=lsp.Position(len(lines), 0)),
|
||||||
|
selection_range=lsp.Range(start=lsp.Position(0, 0), end=lsp.Position(0, 0)),
|
||||||
|
children=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
scope_stack: list[Scope] = [Scope("", root_symbol, 0)]
|
||||||
|
pending_event: dict | None = None
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
# If inside an event, update its brace count for this line
|
||||||
|
if pending_event is not None:
|
||||||
|
pe_open = line.count("{")
|
||||||
|
pe_close = line.count("}")
|
||||||
|
pending_event["brace_count"] = pending_event.get("brace_count", 1) + pe_open - pe_close
|
||||||
|
# If this line closed the outer event block, finalize the event
|
||||||
|
if pending_event["brace_count"] <= 0:
|
||||||
|
# Try inline name on the same line
|
||||||
|
close_inline = EVENT_CLOSE_INLINE_RE.match(line)
|
||||||
|
if close_inline:
|
||||||
|
event_name = close_inline.group(1)
|
||||||
|
name_line_index = i
|
||||||
|
else:
|
||||||
|
# Look ahead to next non-empty line for the name
|
||||||
|
j = i + 1
|
||||||
|
while j < len(lines) and lines[j].strip() == "":
|
||||||
|
j += 1
|
||||||
|
event_name = None
|
||||||
|
name_line_index = i
|
||||||
|
if j < len(lines):
|
||||||
|
name_line = lines[j]
|
||||||
|
name_match = EVENT_NAME_LINE_RE.match(name_line)
|
||||||
|
if name_match:
|
||||||
|
event_name = name_match.group(1)
|
||||||
|
name_line_index = j
|
||||||
|
if event_name:
|
||||||
|
start_line = pending_event["start"]
|
||||||
|
edit_type = pending_event["edit_type"]
|
||||||
|
children = pending_event.get("children", [])
|
||||||
|
detail = f"Event ({edit_type})"
|
||||||
|
event_symbol = lsp.DocumentSymbol(
|
||||||
|
name=event_name,
|
||||||
|
detail=detail,
|
||||||
|
kind=lsp.SymbolKind.Event,
|
||||||
|
range=lsp.Range(start=lsp.Position(start_line, 0), end=lsp.Position(name_line_index, len(lines[name_line_index]))),
|
||||||
|
selection_range=lsp.Range(start=lsp.Position(name_line_index, 0), end=lsp.Position(name_line_index, len(lines[name_line_index]))),
|
||||||
|
children=children or [],
|
||||||
|
)
|
||||||
|
if scope_stack[-1].symbol.children is None:
|
||||||
|
scope_stack[-1].symbol.children = []
|
||||||
|
scope_stack[-1].symbol.children.append(event_symbol)
|
||||||
|
# Clear event tracking and continue
|
||||||
|
pending_event = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
ns_match = NS_RE.match(line)
|
||||||
|
proc_match = PROC_RE.match(line)
|
||||||
|
set_match = SET_RE.match(line)
|
||||||
|
|
||||||
|
# Namespace
|
||||||
|
if ns_match:
|
||||||
|
ns_name = ns_match.group(1)
|
||||||
|
start = lsp.Position(i, 0)
|
||||||
|
end = lsp.Position(i, len(line))
|
||||||
|
sel_start_char = line.find(ns_name)
|
||||||
|
sel_end_char = sel_start_char + len(ns_name) if sel_start_char >= 0 else len(line)
|
||||||
|
ns_symbol = lsp.DocumentSymbol(
|
||||||
|
name=ns_name,
|
||||||
|
detail="Namespace",
|
||||||
|
kind=lsp.SymbolKind.Namespace,
|
||||||
|
range=lsp.Range(start=start, end=end),
|
||||||
|
selection_range=lsp.Range(
|
||||||
|
start=lsp.Position(i, max(sel_start_char, 0)),
|
||||||
|
end=lsp.Position(i, max(sel_end_char, 0)),
|
||||||
|
),
|
||||||
|
children=[],
|
||||||
|
)
|
||||||
|
scope = Scope(ns_name, ns_symbol, i)
|
||||||
|
if scope_stack[-1].symbol.children is None:
|
||||||
|
scope_stack[-1].symbol.children = []
|
||||||
|
scope_stack[-1].symbol.children.append(ns_symbol)
|
||||||
|
scope_stack.append(scope)
|
||||||
|
|
||||||
|
# Proc
|
||||||
|
elif proc_match:
|
||||||
|
proc_name = proc_match.group(1)
|
||||||
|
start = lsp.Position(i, 0)
|
||||||
|
end = lsp.Position(i, len(line))
|
||||||
|
sel_start_char = line.find(proc_name)
|
||||||
|
sel_end_char = sel_start_char + len(proc_name) if sel_start_char >= 0 else len(line)
|
||||||
|
proc_symbol = lsp.DocumentSymbol(
|
||||||
|
name=proc_name,
|
||||||
|
detail="Procedure",
|
||||||
|
kind=lsp.SymbolKind.Function,
|
||||||
|
range=lsp.Range(start=start, end=end),
|
||||||
|
selection_range=lsp.Range(
|
||||||
|
start=lsp.Position(i, max(sel_start_char, 0)),
|
||||||
|
end=lsp.Position(i, max(sel_end_char, 0)),
|
||||||
|
),
|
||||||
|
children=[],
|
||||||
|
)
|
||||||
|
scope = Scope(proc_name, proc_symbol, i)
|
||||||
|
if scope_stack[-1].symbol.children is None:
|
||||||
|
scope_stack[-1].symbol.children = []
|
||||||
|
scope_stack[-1].symbol.children.append(proc_symbol)
|
||||||
|
scope_stack.append(scope)
|
||||||
|
|
||||||
|
# set variable
|
||||||
|
elif set_match:
|
||||||
|
var_name = set_match.group(1)
|
||||||
|
start = lsp.Position(i, 0)
|
||||||
|
end = lsp.Position(i, len(line))
|
||||||
|
sel_start_char = line.find(var_name)
|
||||||
|
sel_end_char = sel_start_char + len(var_name) if sel_start_char >= 0 else len(line)
|
||||||
|
var_symbol = lsp.DocumentSymbol(
|
||||||
|
name=var_name,
|
||||||
|
detail="Variable",
|
||||||
|
kind=lsp.SymbolKind.Variable,
|
||||||
|
range=lsp.Range(start=start, end=end),
|
||||||
|
selection_range=lsp.Range(
|
||||||
|
start=lsp.Position(i, max(sel_start_char, 0)),
|
||||||
|
end=lsp.Position(i, max(sel_end_char, 0)),
|
||||||
|
),
|
||||||
|
children=None,
|
||||||
|
)
|
||||||
|
# Attach to current scope or pending event as child
|
||||||
|
if pending_event is not None:
|
||||||
|
pending_event["children"].append(var_symbol)
|
||||||
|
else:
|
||||||
|
if scope_stack[-1].symbol.children is None:
|
||||||
|
scope_stack[-1].symbol.children = []
|
||||||
|
scope_stack[-1].symbol.children.append(var_symbol)
|
||||||
|
|
||||||
|
# Event start (buffer edit)
|
||||||
|
m = EVENT_START_RE.match(line)
|
||||||
|
if m:
|
||||||
|
edit_type = m.group(1)
|
||||||
|
pending_event = {"start": i, "edit_type": edit_type, "children": []}
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Brace balancing for scopes (namespace/proc)
|
||||||
|
open_count = line.count("{")
|
||||||
|
close_count = line.count("}")
|
||||||
|
scope_stack[-1].brace_count += open_count - close_count
|
||||||
|
|
||||||
|
# Close finished scopes
|
||||||
|
while len(scope_stack) > 1 and scope_stack[-1].brace_count <= 0:
|
||||||
|
finished = scope_stack.pop()
|
||||||
|
# Set the full range from startLine to current line
|
||||||
|
finished.symbol.range = lsp.Range(
|
||||||
|
start=lsp.Position(finished.start_line, 0),
|
||||||
|
end=lsp.Position(i, len(line)),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return top-level children
|
||||||
|
return root_symbol.children
|
||||||
|
|
||||||
@@ -26,6 +26,11 @@ class Token:
|
|||||||
tok_type: str = ""
|
tok_type: str = ""
|
||||||
tok_modifiers: List[TokenModifier] = attrs.field(factory=list)
|
tok_modifiers: List[TokenModifier] = attrs.field(factory=list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def length(self) -> int:
|
||||||
|
"""Compatibility alias for misspelled 'lenght' field."""
|
||||||
|
return self.lenght
|
||||||
|
|
||||||
|
|
||||||
TOKEN_TYPES = [
|
TOKEN_TYPES = [
|
||||||
"keyword",
|
"keyword",
|
||||||
|
|||||||
Reference in New Issue
Block a user