update formatter
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import re
|
||||
|
||||
|
||||
def format_tcl(src: str, indent_str=" ") -> str:
|
||||
"""
|
||||
Simple Tcl formatter with special handling for:
|
||||
1) Single-line 'if {cond} {action}' blocks remain on one line.
|
||||
2) Combined closing-and-opening lines like '} else {' dedent then re-indent.
|
||||
3) Lines like '} Tag' stay on the same line: '} Tag'.
|
||||
4) Standard multi-line blocks for 'if', 'elseif', 'else', '{', '}'.
|
||||
"""
|
||||
level = 0
|
||||
out_lines = []
|
||||
|
||||
for raw_line in src.splitlines():
|
||||
stripped = raw_line.strip()
|
||||
|
||||
# 1) Single-line 'if {cond} {action}' → no indent change
|
||||
if re.match(r"^(if|elseif)\s*\{[^}]+\}\s*\{[^}]+\}$", stripped):
|
||||
out_lines.append(indent_str * level + stripped)
|
||||
continue
|
||||
|
||||
# 2) Combined '} else {' → dedent, print, then indent
|
||||
if re.match(r"^\}\s*(elseif|else)\b.*\{$", stripped):
|
||||
level = max(level - 1, 0)
|
||||
out_lines.append(indent_str * level + stripped)
|
||||
level += 1
|
||||
continue
|
||||
|
||||
# 3) SPECIAL: closing brace plus tag on same line: '} Tag'
|
||||
m = re.match(r"^\}\s+(\w+)$", stripped)
|
||||
if m:
|
||||
# close one block
|
||||
level = max(level - 1, 0)
|
||||
# stay on one line: "} Tag"
|
||||
out_lines.append(f"{indent_str * level}}} {m.group(1)}")
|
||||
continue
|
||||
|
||||
# 4) Pure '}' → dedent then print
|
||||
if stripped == "}":
|
||||
level = max(level - 1, 0)
|
||||
out_lines.append(f"{indent_str * level}{stripped}")
|
||||
continue
|
||||
|
||||
# 5) 'elseif' or 'else' alone → align with matching 'if'
|
||||
if re.match(r"^(elseif|else)\b(?!.*\{)", stripped):
|
||||
level = max(level - 1, 0)
|
||||
out_lines.append(f"{indent_str * level}{stripped}")
|
||||
continue
|
||||
|
||||
# 6) Default: print at current indent
|
||||
out_lines.append(f"{indent_str * level}{stripped}")
|
||||
|
||||
# 7) Open a new block on lines ending with '{'
|
||||
if re.match(r"^(if|elseif)\b.*\{$", stripped) or stripped.endswith("{"):
|
||||
level += 1
|
||||
|
||||
return "\n".join(out_lines)
|
||||
@@ -0,0 +1,81 @@
|
||||
# tokens.py
|
||||
from typing import List
|
||||
import attrs
|
||||
import enum
|
||||
from lark import Tree, Token
|
||||
|
||||
# Legend must match the client
|
||||
SEMANTIC_TOKEN_TYPES = {
|
||||
"keyword": 0,
|
||||
"variable": 2,
|
||||
"string": 3,
|
||||
}
|
||||
SEMANTIC_TOKEN_MODIFIERS = {
|
||||
"declaration": 1 << 0,
|
||||
}
|
||||
|
||||
|
||||
class TokenModifier(enum.IntFlag):
|
||||
deprecated = enum.auto()
|
||||
readonly = enum.auto()
|
||||
defaultLibrary = enum.auto()
|
||||
definition = enum.auto()
|
||||
|
||||
|
||||
@attrs.define
|
||||
class TokenData:
|
||||
line: int
|
||||
offset: int
|
||||
text: str
|
||||
|
||||
tok_type: str = ""
|
||||
tok_modifiers: List[TokenModifier] = attrs.field(factory=list)
|
||||
|
||||
|
||||
TokenTypes = ["keyword", "variable", "function", "operator", "parameter", "type"]
|
||||
|
||||
|
||||
def collect_semantic_tokens(tree: Tree) -> list[int]:
|
||||
"""
|
||||
Walk the parse tree and return a flat LSP semanticTokens/full array:
|
||||
[line, char, length, tokenType, tokenModifiers, …]
|
||||
"""
|
||||
data = []
|
||||
for tok in tree.scan_values(lambda v: isinstance(v, Token)):
|
||||
# 'set' keyword
|
||||
if tok.type == "SET":
|
||||
data.append(
|
||||
[
|
||||
tok.line - 1,
|
||||
tok.column - 1,
|
||||
len(tok.value),
|
||||
SEMANTIC_TOKEN_TYPES["keyword"],
|
||||
0,
|
||||
]
|
||||
)
|
||||
# variable being declared
|
||||
elif tok.type == "NAME":
|
||||
data.append(
|
||||
[
|
||||
tok.line - 1,
|
||||
tok.column - 1,
|
||||
len(tok.value),
|
||||
SEMANTIC_TOKEN_TYPES["variable"],
|
||||
SEMANTIC_TOKEN_MODIFIERS["declaration"],
|
||||
]
|
||||
)
|
||||
# string literal
|
||||
elif tok.type == "STRING":
|
||||
data.append(
|
||||
[
|
||||
tok.line - 1,
|
||||
tok.column - 1,
|
||||
len(tok.value),
|
||||
SEMANTIC_TOKEN_TYPES["string"],
|
||||
0,
|
||||
]
|
||||
)
|
||||
|
||||
# sort by position and flatten
|
||||
data.sort(key=lambda x: (x[0], x[1]))
|
||||
return [p for token in data for p in token]
|
||||
Reference in New Issue
Block a user