Files
ViperC/App/lib/lsp/LspCodeAction.vp
T
TermiNexus 634321a12b refactor: consolidate duplicate ViperC helpers
Merge repeated list lowering, directory scanning, SHA1-store writes, VPI lookup keys, type-parameter splitting, DefineT width normalization, and LSP helpers. Source-only change: 235 added, 793 deleted (net -558 lines).
2026-11-02 13:08:50 +08:00

256 lines
7.6 KiB
Plaintext

from stdint import *
import string
import json
import memhub
import ast
import lib.lsp.LspDocuments as LspDocuments
import lib.lsp.LspSource as LspSource
import lib.lsp.LspSymbols as LspSymbols
import lib.lsp.LspWorkspace as LspWorkspace
import lib.lsp.LspNavigation as LspNavigation
import lib.lsp.LspTransport as LspTransport
import lib.lsp.LspRpc as LspRpc
import lib.lsp.LspTypes as LspTypes
def _IsImportNode(node: ast.AST*) -> int:
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Import:
return 1
if k == ast.ASTKind.ImportFrom:
return 1
return 0
def _IsDocstring(node: ast.AST*) -> int:
if node is None or node.kind() != ast.ASTKind.Expr:
return 0
ex: ast.Expr* = (ast.Expr*)(node)
if ex.value is None or ex.value.kind() != ast.ASTKind.Constant:
return 0
c: ast.Constant* = (ast.Constant*)(ex.value)
return 1 if c.const_kind == ast.CONST_STR else 0
def _LineText(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*,
line0: int) -> str:
if doc is None or doc.index is None or line0 < 0 or line0 >= doc.index.Count():
return None
sc: int = 0
ec: int = LspSource.LineByteLength(doc, line0)
return LspSource.SliceBytes(pool, doc, line0, sc, line0, ec)
def _HasUnbalancedParen(s: str) -> int:
if s is None:
return 0
depth: int = 0
i: sizet = 0
while s[i] != 0:
if s[i] == '(':
depth += 1
elif s[i] == ')':
if depth > 0:
depth -= 1
elif s[i] == '\\':
return 1
i += 1
return 1 if depth != 0 else 0
def _Lower(ch: char) -> char:
if ch >= 'A' and ch <= 'Z':
return char(int(ch) + 32)
return ch
def _CompareImport(a: str, b: str) -> int:
if a is None:
return 1 if b is not None else 0
if b is None:
return -1
n: sizet = string.strlen(a)
m: sizet = string.strlen(b)
i: sizet = 0
lim: sizet = n
if m < lim:
lim = m
while i < lim:
ca: char = _Lower(a[i])
cb: char = _Lower(b[i])
if ca < cb:
return -1
if ca > cb:
return 1
i += 1
if n < m:
return -1
if n > m:
return 1
return string.strcmp(a, b)
def _Contains(list_in: list[str]*, s: str) -> int:
if list_in is None or s is None:
return 0
i: int = 0
while i < int(len(list_in)):
if string.strcmp(list_in.get(sizet(i)), s) == 0:
return 1
i += 1
return 0
def _InsertSorted(pool: memhub.MemManager*, out: list[str]*,
s: str, cap: int) -> list[str]*:
new_list: list[str]* = list[str](pool, cap)
if new_list is None:
return out
n: int = int(len(out))
pos: int = 0
while pos < n:
if _CompareImport(s, out.get(sizet(pos))) < 0:
break
pos += 1
i: int = 0
inserted: int = 0
while i < n:
if i == pos:
new_list.append(s)
inserted = 1
new_list.append(out.get(sizet(i)))
i += 1
if inserted == 0:
new_list.append(s)
return new_list
def _Organize(pool: memhub.MemManager*, a: LspSymbols.LspAnalysis*,
uri: str) -> json.JsonValue*:
if a is None or a.tree is None or a.tree.children is None:
return None
body: list[ast.AST*]* = a.tree.children
n: int = int(len(body))
idx: int = 0
if n > 0 and _IsDocstring(body.get(0)) != 0:
idx = 1
first_line: int = -1
last_line: int = -1
originals: list[str]* = list[str](pool, 8)
while idx < n:
st: ast.AST* = body.get(sizet(idx))
if _IsImportNode(st) == 0:
break
if st.lineno <= 0:
return None
line0: int = st.lineno - 1
txt: str = LspTypes._Trim(pool, _LineText(pool, a.doc, line0))
if txt is None or txt[0] == 0 or _HasUnbalancedParen(txt) != 0:
return None
if first_line < 0:
first_line = line0
last_line = line0
if _Contains(originals, txt) == 0:
originals.append(txt)
idx += 1
if first_line < 0:
return None
# Only organize a contiguous import block: reject imports later in the file.
while idx < n:
if _IsImportNode(body.get(sizet(idx))) != 0:
return None
idx += 1
sorted_imports: list[str]* = list[str](pool, 8)
i: int = 0
while i < int(len(originals)):
sorted_imports = _InsertSorted(pool, sorted_imports, originals.get(sizet(i)), 64)
i += 1
changed: int = 0
if int(len(sorted_imports)) != int(len(originals)):
changed = 1
else:
i = 0
while i < int(len(sorted_imports)):
if string.strcmp(sorted_imports.get(sizet(i)), originals.get(sizet(i))) != 0:
changed = 1
break
i += 1
if changed == 0:
return None
new_text: str = None
i = 0
while i < int(len(sorted_imports)):
if new_text is None:
new_text = LspSymbols._CopyStr(pool, sorted_imports.get(sizet(i)), 1024)
else:
new_text = _Join(pool, new_text, "\n",
sorted_imports.get(sizet(i)))
i += 1
if new_text is None:
return None
rng: LspSource.LspRange* = LspSource.SpanFromBytes(
pool, a.doc, first_line, 0, last_line, LspSource.LineByteLength(a.doc, last_line))
edit: json.JsonValue* = json.object(pool)
if edit is None:
return None
json.object_set(pool, edit, "range", LspSource.RangeToJson(pool, rng))
json.object_set(pool, edit, "newText", json.string_val(pool, new_text))
edits: json.JsonValue* = json.array(pool)
if edits is None:
return None
json.array_append(pool, edits, edit)
changes: json.JsonValue* = json.object(pool)
if changes is None:
return None
json.object_set(pool, changes, uri, edits)
we: json.JsonValue* = json.object(pool)
if we is None:
return None
json.object_set(pool, we, "changes", changes)
action: json.JsonValue* = json.object(pool)
if action is None:
return None
json.object_set(pool, action, "title", json.string_val(pool, "Organize imports"))
json.object_set(pool, action, "kind", json.string_val(pool, "source.organizeImports"))
json.object_set(pool, action, "edit", we)
return action
def _Join(pool: memhub.MemManager*, a: str, sep: str, b: str) -> str:
if a is None:
return b
if b is None:
return a
n1: sizet = string.strlen(a)
n2: sizet = string.strlen(sep)
n3: sizet = string.strlen(b)
buf: bytes = pool.alloc(n1 + n2 + n3 + 1)
if buf is None:
return None
string.memcpy(buf, a, n1)
string.memcpy(buf + n1, sep, n2)
string.memcpy(buf + n1 + n2, b, n3)
buf[n1 + n2 + n3] = 0
return str(buf)
def CodeAction(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
docs: LspDocuments.LspDocuments*, ws: LspWorkspace.LspWorkspace*,
id_node: json.JsonValue*, params: json.JsonValue*) -> int:
arr: json.JsonValue* = json.array(pool)
if arr is None:
return -1
if params is None:
return LspRpc.WriteResponse(pool, tr, id_node, arr)
doc: LspDocuments.LspDocument* = LspNavigation._Doc(pool, docs, params)
if doc is None:
return LspRpc.WriteResponse(pool, tr, id_node, arr)
uri: str = LspNavigation._Uri(params)
a: LspSymbols.LspAnalysis* = LspNavigation._Analyze(pool, docs, doc, uri)
action: json.JsonValue* = _Organize(pool, a, uri)
if action is not None:
json.array_append(pool, arr, action)
return LspRpc.WriteResponse(pool, tr, id_node, arr)