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).
256 lines
7.6 KiB
Plaintext
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)
|