feat(lsp): 支持 from module import * 与相对 includes
通配导入的名字通过索引解析(高亮/悬停/跳转/成员),typedef 使用 type token;project.vpj 中任意相对 source/includes 按项目目录解析;修复通配符号表步长导致的越界崩溃;关闭文档释放文档对象,didOpen 不再强制重建索引。
This commit is contained in:
1 parent
879cf7ab9f
commit
d13eb6b063
11 files changed
+245
-7
No files matched your search
@@ -85,6 +85,10 @@ class LspDocuments:
|
||||
if doc.spare is not None:
|
||||
doc.spare.Destroy()
|
||||
doc.spare = None
|
||||
if doc.uri is not None:
|
||||
self.pool.free(rawptr(doc.uri))
|
||||
doc.uri = None
|
||||
self.pool.free(rawptr(doc))
|
||||
return 0
|
||||
|
||||
def Open(self, uri: str, text: str, version: int) -> LspDocument*:
|
||||
|
||||
@@ -62,6 +62,7 @@ class LspTarget:
|
||||
sel: LspSource.LspRange*
|
||||
has_sel: int
|
||||
is_macro: int
|
||||
is_alias: int
|
||||
|
||||
def __new__(self, pool: memhub.MemManager*) -> rawptr:
|
||||
p: LspTarget* = (LspTarget*)(pool.alloc(LspTarget.__sizeof__()))
|
||||
@@ -250,6 +251,7 @@ def _CopySymbol(pool: memhub.MemManager*, s: LspSymbols.LspSymbol*,
|
||||
c.name = LspPools.DupStr(pool, s.name)
|
||||
c.kind = s.kind
|
||||
c.is_macro = s.is_macro
|
||||
c.is_alias = s.is_alias
|
||||
c.detail = LspSymbols._CopyStr(pool, s.detail, LspSymbols.LSP_SIGNATURE_CAP)
|
||||
c.doc = LspSymbols._CopyStr(pool, s.doc, LspSymbols.LSP_DOC_CAP)
|
||||
c.file = file
|
||||
@@ -742,6 +744,7 @@ def _SymbolTarget(pool: memhub.MemManager*, s: LspSymbols.LspSymbol*) -> LspTarg
|
||||
t.name = s.name
|
||||
t.kind = s.kind
|
||||
t.is_macro = s.is_macro
|
||||
t.is_alias = s.is_alias
|
||||
t.detail = s.detail
|
||||
t.doc = s.doc
|
||||
t.container = s.container
|
||||
@@ -954,6 +957,34 @@ def ResolveSymbol(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
|
||||
return _SymbolTarget(pool, s)
|
||||
|
||||
|
||||
def ResolveWildcard(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
|
||||
table_pool: memhub.MemManager*, from_file: str,
|
||||
wildcard: LspSymbols.LspSymbol*,
|
||||
name: str) -> LspTarget*:
|
||||
"""Resolve a name (optionally `Name.member...`) supplied by `from module import *`."""
|
||||
if wildcard is None or name is None:
|
||||
return None
|
||||
file: str = _ResolveFromFile(ws, pool, from_file, wildcard)
|
||||
if file is None or not os.isfile(file):
|
||||
return None
|
||||
h: str = None
|
||||
r: str = None
|
||||
LspSymbols._SplitHead(pool, name, &h, &r)
|
||||
if h is None:
|
||||
return None
|
||||
member: LspSymbols.LspSymbol* = ResolveInFile(pool, table_pool, file, None, h)
|
||||
if member is None:
|
||||
return None
|
||||
base: LspTarget* = None
|
||||
if member.import_kind != LspSymbols.LSP_IMPORT_NONE:
|
||||
base = _ResolveFromTarget(ws, pool, table_pool, member.file, member, 0)
|
||||
else:
|
||||
base = _SymbolTarget(pool, member)
|
||||
if r is None:
|
||||
return base
|
||||
return ResolveMembers(pool, table_pool, base, r)
|
||||
|
||||
|
||||
def ResolveMembers(pool: memhub.MemManager*, table_pool: memhub.MemManager*,
|
||||
base: LspTarget*, rest: str) -> LspTarget*:
|
||||
"""Walk class members from an already-resolved base target (no import re-resolution)."""
|
||||
@@ -986,6 +1017,8 @@ def ResolveChain(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
|
||||
rest: str) -> LspTarget*:
|
||||
if head is None:
|
||||
return None
|
||||
if head.import_attr is not None and string.strcmp(head.import_attr, "*") == 0:
|
||||
return ResolveWildcard(ws, pool, table_pool, from_file, head, rest)
|
||||
if head.import_kind == LspSymbols.LSP_IMPORT_FROM:
|
||||
base: LspTarget* = _ResolveFromTarget(ws, pool, table_pool, from_file, head, 0)
|
||||
if base is None or rest is None:
|
||||
|
||||
@@ -161,6 +161,32 @@ def _LiveSymbol(a: LspSymbols.LspAnalysis*, container: str,
|
||||
return None
|
||||
|
||||
|
||||
def _WildcardTarget(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
|
||||
table_pool: memhub.MemManager*,
|
||||
a: LspSymbols.LspAnalysis*,
|
||||
o: LspSymbols.LspOccurrence*) -> LspIndex.LspTarget*:
|
||||
if a is None or a.wildcards is None or o is None or o.name is None:
|
||||
return None
|
||||
i: int = 0
|
||||
while i < int(len(a.wildcards)):
|
||||
w: LspSymbols.LspSymbol* = a.wildcards.get(sizet(i))
|
||||
i += 1
|
||||
t: LspIndex.LspTarget* = LspIndex.ResolveWildcard(
|
||||
ws, pool, table_pool, a.file, w, o.name)
|
||||
if t is not None:
|
||||
return t
|
||||
return None
|
||||
|
||||
|
||||
def WildcardNameMismatch(o: LspSymbols.LspOccurrence*, target: str) -> int:
|
||||
"""Skip wildcard resolution for occurrences whose name cannot match the target."""
|
||||
if o is None or target is None or o.name is None:
|
||||
return 0
|
||||
if o.sym is not None or o.head is not None:
|
||||
return 0
|
||||
return 1 if string.strcmp(o.name, target) != 0 else 0
|
||||
|
||||
|
||||
def _TargetAt(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
|
||||
table_pool: memhub.MemManager*,
|
||||
a: LspSymbols.LspAnalysis*, doc: LspDocuments.LspDocument*,
|
||||
@@ -173,7 +199,7 @@ def _TargetAt(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
|
||||
return LspIndex.ResolveSymbol(ws, pool, table_pool, a.file, occ.sym, 0)
|
||||
if occ.head is not None and occ.rest is not None:
|
||||
return LspIndex.ResolveChain(ws, pool, table_pool, a.file, occ.head, occ.rest)
|
||||
return None
|
||||
return _WildcardTarget(ws, pool, table_pool, a, occ)
|
||||
s: LspSymbols.LspSymbol* = _SymAt(a, line0, ch)
|
||||
if s is not None:
|
||||
return LspIndex.ResolveSymbol(ws, pool, table_pool, a.file, s, 0)
|
||||
@@ -400,6 +426,13 @@ def _IdentityForOcc(pool: memhub.MemManager*, a: LspSymbols.LspAnalysis*,
|
||||
out_container[0] = t.container
|
||||
out_name[0] = t.name
|
||||
return 1
|
||||
return 0
|
||||
t2: LspIndex.LspTarget* = _WildcardTarget(ws, pool, table_pool, a, o)
|
||||
if t2 is not None and t2.name is not None and t2.file is not None:
|
||||
out_file[0] = LspFiles.Normalize(pool, t2.file)
|
||||
out_container[0] = t2.container
|
||||
out_name[0] = t2.name
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -531,6 +564,8 @@ def References(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
|
||||
while j < int(len(oa.occs)):
|
||||
o: LspSymbols.LspOccurrence* = oa.occs.get(sizet(j))
|
||||
j += 1
|
||||
if WildcardNameMismatch(o, tn) != 0:
|
||||
continue
|
||||
of: str = None
|
||||
oc: str = None
|
||||
on: str = None
|
||||
@@ -636,6 +671,8 @@ def DocumentHighlight(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
|
||||
while i2 < int(len(a.occs)):
|
||||
o2: LspSymbols.LspOccurrence* = a.occs.get(sizet(i2))
|
||||
i2 += 1
|
||||
if WildcardNameMismatch(o2, tn) != 0:
|
||||
continue
|
||||
of: str = None
|
||||
oc: str = None
|
||||
on: str = None
|
||||
|
||||
@@ -278,6 +278,8 @@ def Rename(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
|
||||
while j < int(len(oa.occs)):
|
||||
o: LspSymbols.LspOccurrence* = oa.occs.get(sizet(j))
|
||||
j += 1
|
||||
if LspNavigation.WildcardNameMismatch(o, tn) != 0:
|
||||
continue
|
||||
of: str = None
|
||||
oc: str = None
|
||||
on: str = None
|
||||
|
||||
@@ -31,6 +31,7 @@ define ST_NUMBER = 10
|
||||
define ST_OPERATOR = 11
|
||||
define ST_DECORATOR = 12
|
||||
define ST_MACRO = 13
|
||||
define ST_TYPE = 14
|
||||
|
||||
|
||||
class LspSemToken:
|
||||
@@ -83,6 +84,7 @@ def Provider(pool: memhub.MemManager*) -> json.JsonValue*:
|
||||
json.array_append(pool, types, json.string_val(pool, "operator"))
|
||||
json.array_append(pool, types, json.string_val(pool, "decorator"))
|
||||
json.array_append(pool, types, json.string_val(pool, "macro"))
|
||||
json.array_append(pool, types, json.string_val(pool, "type"))
|
||||
json.array_append(pool, mods, json.string_val(pool, "declaration"))
|
||||
json.array_append(pool, mods, json.string_val(pool, "readonly"))
|
||||
json.object_set(pool, legend, "tokenTypes", types)
|
||||
@@ -105,6 +107,8 @@ def _IsDefineMacro(s: LspSymbols.LspSymbol*) -> int:
|
||||
def _TypeFor(s: LspSymbols.LspSymbol*) -> int:
|
||||
if s is None:
|
||||
return ST_VARIABLE
|
||||
if s.is_alias != 0:
|
||||
return ST_TYPE
|
||||
if s.kind == LspSymbols.LSP_KIND_MODULE:
|
||||
return ST_NAMESPACE
|
||||
if s.kind == LspSymbols.LSP_KIND_CLASS:
|
||||
@@ -128,6 +132,8 @@ def _ModsFor(s: LspSymbols.LspSymbol*) -> int:
|
||||
"""readonly = bit 1 in the legend; constants use the theme's constant color (as Vipeye does)."""
|
||||
if s is None:
|
||||
return 0
|
||||
if s.is_alias != 0:
|
||||
return 0
|
||||
if s.kind == LspSymbols.LSP_KIND_CONSTANT:
|
||||
return 2
|
||||
return 0
|
||||
@@ -137,6 +143,7 @@ class LspSemCtx:
|
||||
ws: LspWorkspace.LspWorkspace*
|
||||
table_pool: memhub.MemManager*
|
||||
file: str
|
||||
wildcards: list[LspSymbols.LspSymbol*]*
|
||||
sym_keys: list[LspSymbols.LspSymbol*]*
|
||||
sym_tgts: list[LspIndex.LspTarget*]*
|
||||
chain_heads: list[LspSymbols.LspSymbol*]*
|
||||
@@ -153,6 +160,8 @@ class LspSemCtx:
|
||||
def _TypeForTarget(t: LspIndex.LspTarget*) -> int:
|
||||
if t is None:
|
||||
return -1
|
||||
if t.is_alias != 0:
|
||||
return ST_TYPE
|
||||
if t.kind == LspSymbols.LSP_KIND_MODULE:
|
||||
return ST_NAMESPACE
|
||||
if t.kind == LspSymbols.LSP_KIND_CLASS:
|
||||
@@ -167,7 +176,7 @@ def _TypeForTarget(t: LspIndex.LspTarget*) -> int:
|
||||
|
||||
|
||||
def _ModsForTarget(t: LspIndex.LspTarget*) -> int:
|
||||
if t is not None and t.kind == LspSymbols.LSP_KIND_CONSTANT:
|
||||
if t is not None and t.is_alias == 0 and t.kind == LspSymbols.LSP_KIND_CONSTANT:
|
||||
return 2
|
||||
return 0
|
||||
|
||||
@@ -209,12 +218,16 @@ def _CachedChain(ctx: LspSemCtx*, pool: memhub.MemManager*,
|
||||
if ctx is None or head is None or rest is None:
|
||||
return None
|
||||
slot: sizet = (_SemHash(rest) ^ _SemHash(head.name)) & sizet(LSP_SEM_CACHE_MASK)
|
||||
if head.import_mod is not None:
|
||||
slot = (slot ^ _SemHash(head.import_mod)) & sizet(LSP_SEM_CACHE_MASK)
|
||||
if ctx.chain_heads.get(slot) == head:
|
||||
cached: str = ctx.chain_rests.get(slot)
|
||||
if cached is not None and string.strcmp(cached, rest) == 0:
|
||||
return ctx.chain_tgts.get(slot)
|
||||
t: LspIndex.LspTarget* = None
|
||||
if head.import_kind == LspSymbols.LSP_IMPORT_FROM:
|
||||
if head.import_attr is not None and string.strcmp(head.import_attr, "*") == 0:
|
||||
t = LspIndex.ResolveWildcard(ctx.ws, pool, ctx.table_pool, ctx.file, head, rest)
|
||||
elif head.import_kind == LspSymbols.LSP_IMPORT_FROM:
|
||||
base: LspIndex.LspTarget* = _CachedSymbol(ctx, pool, head)
|
||||
if base is not None:
|
||||
t = LspIndex.ResolveMembers(pool, ctx.table_pool, base, rest)
|
||||
@@ -263,6 +276,16 @@ def _TokenForOcc(ctx: LspSemCtx*, pool: memhub.MemManager*,
|
||||
if t is not None:
|
||||
typ = _TypeForTarget(t)
|
||||
mods = _ModsForTarget(t)
|
||||
if typ < 0 and o.head is None and o.rest is None and o.name is not None:
|
||||
if ctx is not None and ctx.wildcards is not None:
|
||||
wi: int = 0
|
||||
while wi < int(len(ctx.wildcards)) and typ < 0:
|
||||
w: LspSymbols.LspSymbol* = ctx.wildcards.get(sizet(wi))
|
||||
wi += 1
|
||||
tw: LspIndex.LspTarget* = _CachedChain(ctx, pool, w, o.name)
|
||||
if tw is not None:
|
||||
typ = _TypeForTarget(tw)
|
||||
mods = _ModsForTarget(tw)
|
||||
if typ < 0:
|
||||
# Builtin type names have no declaration; color them like class types.
|
||||
if o.builtin_type == 0:
|
||||
@@ -568,6 +591,7 @@ def Tokens(pool: memhub.MemManager*, tr: LspTransport.LspTransport*,
|
||||
ctx.ws = ws
|
||||
ctx.table_pool = docs.pool
|
||||
ctx.file = a.file
|
||||
ctx.wildcards = a.wildcards
|
||||
ctx.sym_keys = list[LspSymbols.LspSymbol*](pool, 8)
|
||||
ctx.sym_tgts = list[LspIndex.LspTarget*](pool, 8)
|
||||
ctx.chain_heads = list[LspSymbols.LspSymbol*](pool, 8)
|
||||
|
||||
@@ -474,7 +474,6 @@ def Run(pool: memhub.MemBuddy*, perm_pool: memhub.MemBuddy*) -> int:
|
||||
pass
|
||||
elif string.strcmp(method, "textDocument/didOpen") == 0:
|
||||
_HandleDidOpen(pool, tr, docs, ws, params)
|
||||
LspIndex.Invalidate()
|
||||
elif string.strcmp(method, "textDocument/didChange") == 0:
|
||||
_HandleDidChange(pool, tr, docs, params)
|
||||
elif string.strcmp(method, "textDocument/didClose") == 0:
|
||||
|
||||
@@ -48,6 +48,8 @@ class LspSymbol:
|
||||
kind: int
|
||||
# 1 for `define` macro functions (colored as macro), 0 otherwise.
|
||||
is_macro: int
|
||||
# 1 for typedef/type aliases (colored as type), 0 otherwise.
|
||||
is_alias: int
|
||||
detail: str
|
||||
doc: str
|
||||
file: str
|
||||
@@ -108,6 +110,7 @@ class LspAnalysis:
|
||||
module_scope: LspScope*
|
||||
symbols: list[LspSymbol*]*
|
||||
occs: list[LspOccurrence*]*
|
||||
wildcards: list[LspSymbol*]*
|
||||
root: list[LspSymbol*]*
|
||||
|
||||
def __new__(self, pool: memhub.MemManager*) -> rawptr:
|
||||
@@ -464,6 +467,12 @@ def _BindType(pool: memhub.MemManager*, a: LspAnalysis*, scope: LspScope*,
|
||||
return
|
||||
head: LspSymbol* = _Lookup(a, scope, nm.id, 0)
|
||||
if head is None:
|
||||
# A type imported by `from module import *` has no local binding; bind it to the
|
||||
# wildcard module so member access can resolve through the index.
|
||||
if _IsBuiltinTypeName(nm.id) == 0 and a.wildcards is not None:
|
||||
if int(len(a.wildcards)) > 0:
|
||||
sym.type_sym = a.wildcards.get(0)
|
||||
sym.type_rest = nm.id
|
||||
return
|
||||
sym.type_sym = head
|
||||
sym.type_rest = None
|
||||
@@ -619,6 +628,22 @@ def _NameOnLine(pool: memhub.MemManager*, a: LspAnalysis*, line1: int,
|
||||
return None
|
||||
|
||||
|
||||
def _IsTypeAliasDecl(decl: ast.AST*) -> int:
|
||||
"""1 when decl is `name: t.TypedefT = type` (the canonical typedef form)."""
|
||||
if decl is None or decl.kind() != ast.ASTKind.AnnAssign:
|
||||
return 0
|
||||
ann: ast.AST* = ((ast.AnnAssign*)(decl)).annotation
|
||||
if ann is None:
|
||||
return 0
|
||||
if ann.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = (ast.Name*)(ann)
|
||||
return 1 if nm.id is not None and string.strcmp(nm.id, "TypedefT") == 0 else 0
|
||||
if ann.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = (ast.Attribute*)(ann)
|
||||
return 1 if at.attr is not None and string.strcmp(at.attr, "CTypedef") == 0 else 0
|
||||
return 0
|
||||
|
||||
|
||||
def _Declare(pool: memhub.MemManager*, a: LspAnalysis*, scope: LspScope*,
|
||||
name: str, kind: int, decl: ast.AST*, line: int, col: int,
|
||||
detail: str) -> LspSymbol*:
|
||||
@@ -636,6 +661,8 @@ def _Declare(pool: memhub.MemManager*, a: LspAnalysis*, scope: LspScope*,
|
||||
fd: ast.FunctionDef* = (ast.FunctionDef*)(decl)
|
||||
if (fd.flags & ast.FLAG_IS_DEFINE) != 0:
|
||||
s.is_macro = 1
|
||||
if _IsTypeAliasDecl(decl) != 0:
|
||||
s.is_alias = 1
|
||||
s.sel = _NameSpan(pool, a.doc, line, col, name)
|
||||
if s.sel is None or _RangeTextIsName(pool, a, s.sel, name) == 0:
|
||||
if decl is None or (decl.kind() != ast.ASTKind.Import and decl.kind() != ast.ASTKind.ImportFrom):
|
||||
@@ -1188,6 +1215,17 @@ def _WalkStmt(pool: memhub.MemManager*, a: LspAnalysis*, scope: LspScope*,
|
||||
sym.import_attr = al.name
|
||||
sym.import_level = ifr.level
|
||||
_DeclImportOcc(pool, a, scope, al, bound, sym)
|
||||
elif ifr.module is not None and a.wildcards is not None:
|
||||
# Wildcard imports bind no local names; keep the module for on-demand
|
||||
# resolution of unresolved names (highlighting/navigation).
|
||||
w: LspSymbol* = _NewSymbol(pool, "*", LSP_KIND_MODULE, node, scope)
|
||||
if w is not None:
|
||||
w.file = a.file
|
||||
w.import_kind = LSP_IMPORT_FROM
|
||||
w.import_mod = ifr.module
|
||||
w.import_attr = "*"
|
||||
w.import_level = ifr.level
|
||||
a.wildcards.append(w)
|
||||
i += 1
|
||||
return
|
||||
# Pass/Break/Continue/FallThrough and unknown statements carry no names.
|
||||
@@ -1484,6 +1522,7 @@ def Analyze(pool: memhub.MemManager*, table_pool: memhub.MemManager*,
|
||||
a.file = file
|
||||
a.symbols = list[LspSymbol*](pool, 64)
|
||||
a.occs = list[LspOccurrence*](pool, 128)
|
||||
a.wildcards = list[LspSymbol*](pool, 8)
|
||||
a.module_scope = _NewScope(pool, LSP_SCOPE_MODULE, None, None, None)
|
||||
a.tree = ast.parse_ex(doc.text, pool, table_pool)
|
||||
a.errors = ast.get_parse_errors()
|
||||
|
||||
@@ -10,6 +10,68 @@ import lib.lsp.LspTransport as LspTransport
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
def _AbsJoin(pool: memhub.MemManager*, base: str, path: str) -> str:
|
||||
"""Resolve a project-relative path against the project directory (any relative form)."""
|
||||
if path is None or path[0] == 0:
|
||||
return path
|
||||
plen: sizet = string.strlen(path)
|
||||
if path[0] == '/' or path[0] == '\\':
|
||||
return LspPools.DupStr(pool, path)
|
||||
if plen >= 2 and path[1] == ':':
|
||||
return LspPools.DupStr(pool, path)
|
||||
if base is None or base[0] == 0:
|
||||
return LspPools.DupStr(pool, path)
|
||||
joined: str = os.path.join(base, path)
|
||||
if joined is None:
|
||||
return LspPools.DupStr(pool, path)
|
||||
norm: str = os.path.normpath(joined)
|
||||
if norm is None:
|
||||
norm = joined
|
||||
return LspPools.DupStr(pool, norm)
|
||||
|
||||
|
||||
def _AbsList(pool: memhub.MemManager*, base: str, dirs: str) -> str:
|
||||
"""Resolve a space-separated directory list against base."""
|
||||
if dirs is None or dirs[0] == 0:
|
||||
return dirs
|
||||
n: sizet = string.strlen(dirs)
|
||||
out: str = pool.alloc(n * 3 + 256)
|
||||
if out is None:
|
||||
return dirs
|
||||
out[0] = 0
|
||||
pos: sizet = 0
|
||||
first: int = 1
|
||||
i: sizet = 0
|
||||
while i < n:
|
||||
while i < n and dirs[i] == ' ':
|
||||
i += 1
|
||||
if i >= n:
|
||||
break
|
||||
start: sizet = i
|
||||
while i < n and dirs[i] != ' ':
|
||||
i += 1
|
||||
seg_len: sizet = i - start
|
||||
if seg_len == 0:
|
||||
continue
|
||||
seg: str = pool.alloc(seg_len + 1)
|
||||
if seg is None:
|
||||
continue
|
||||
string.memcpy(seg, dirs + start, seg_len)
|
||||
seg[seg_len] = 0
|
||||
resolved: str = _AbsJoin(pool, base, str(seg))
|
||||
if resolved is None:
|
||||
continue
|
||||
alen: sizet = string.strlen(resolved)
|
||||
if first == 0:
|
||||
out[pos] = ' '
|
||||
pos += 1
|
||||
string.strcpy(out + pos, resolved)
|
||||
pos += alen
|
||||
first = 0
|
||||
out[pos] = 0
|
||||
return str(out)
|
||||
|
||||
|
||||
def _HexVal(ch: char) -> int:
|
||||
if ch >= '0' and ch <= '9':
|
||||
return int(ch) - int('0')
|
||||
@@ -145,8 +207,9 @@ class LspWorkspace:
|
||||
rc: int = Config.Load_project_config(project)
|
||||
if rc == 0:
|
||||
Config.resolve_paths(os.path.dirname(project))
|
||||
self.source_dir = Config.SourceDir
|
||||
self.includes = Config.IncludesDirs
|
||||
base: str = os.path.dirname(project)
|
||||
self.source_dir = _AbsJoin(self.pool, base, Config.SourceDir)
|
||||
self.includes = _AbsList(self.pool, base, Config.IncludesDirs)
|
||||
Config._mbuddy = old_pool
|
||||
return rc
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Gate 10 fixture: exports consumed through `from module import *`."""
|
||||
|
||||
|
||||
class Extra:
|
||||
amount: int
|
||||
|
||||
|
||||
define EXTRA_CONST = 5
|
||||
|
||||
|
||||
def extra_fn(x: int) -> int:
|
||||
return x
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
from .gate10_types import Widget, DOUBLE
|
||||
from .gate10_stub import StubThing
|
||||
from .gate10_extra import *
|
||||
import gate10_types
|
||||
|
||||
|
||||
def star_use(e: Extra) -> int:
|
||||
return extra_fn(EXTRA_CONST) + e.amount
|
||||
|
||||
|
||||
def macro_use(n: int) -> int:
|
||||
return DOUBLE(n)
|
||||
|
||||
|
||||
+21
-1
@@ -1420,7 +1420,7 @@ def gate10(exe: str) -> int:
|
||||
# The fixtures live outside the project's source roots; copy them into an isolated workspace
|
||||
# so the cross-file index contains the module, the stub and the consumer.
|
||||
workspace = Path(tempfile.mkdtemp(prefix="transpyc_lsp_gate10_"))
|
||||
names = ("gate10_types.vp", "gate10_stub.vpi", "gate10_use.vp")
|
||||
names = ("gate10_types.vp", "gate10_stub.vpi", "gate10_extra.vp", "gate10_use.vp")
|
||||
for name in names:
|
||||
shutil.copy(fixtures / name, workspace / name)
|
||||
type_path = workspace / "gate10_types.vp"
|
||||
@@ -1449,6 +1449,7 @@ def gate10(exe: str) -> int:
|
||||
|
||||
legend = response.get("result", {}).get("capabilities", {}).get("semanticTokensProvider", {}).get("legend", {})
|
||||
sem_types = legend.get("tokenTypes", [])
|
||||
sem_mods = legend.get("tokenModifiers", [])
|
||||
semantic = client.request(
|
||||
"textDocument/semanticTokens/full", {"textDocument": {"uri": uri}}, timeout=120.0
|
||||
).get("result")
|
||||
@@ -1461,11 +1462,30 @@ def gate10(exe: str) -> int:
|
||||
("macro call", "return DOUBLE", 7, "macro"),
|
||||
("member field", "s.value", 2, "property"),
|
||||
("member method", "s.resize", 2, "method"),
|
||||
("star class", "e: Extra", 3, "class"),
|
||||
("star function", "return extra_fn", 7, "function"),
|
||||
("star constant", "EXTRA_CONST", 0, "variable"),
|
||||
("star member", "e.amount", 2, "property"),
|
||||
):
|
||||
pos = _position(text, needle)
|
||||
pos["character"] += delta
|
||||
if not _find_token(sem_tokens, pos["line"], pos["character"], sem_types.index(token_name)):
|
||||
raise AssertionError(f"missing cross-module semantic token for {label}: {pos}")
|
||||
const_pos = _position(text, "EXTRA_CONST")
|
||||
if not _find_token_mods(sem_tokens, const_pos["line"], const_pos["character"],
|
||||
sem_types.index("variable"),
|
||||
1 << sem_mods.index("readonly")):
|
||||
raise AssertionError(f"wildcard constant is not readonly: {const_pos}")
|
||||
star_def = client.request(
|
||||
"textDocument/definition",
|
||||
{"textDocument": {"uri": uri}, "position": {"line": _position(text, "e: Extra")["line"],
|
||||
"character": _position(text, "e: Extra")["character"] + 3}},
|
||||
timeout=120.0,
|
||||
).get("result")
|
||||
if not isinstance(star_def, list) or not star_def:
|
||||
raise AssertionError(f"wildcard definition failed: {star_def}")
|
||||
if not star_def[0]["uri"].endswith("gate10_extra.vp"):
|
||||
raise AssertionError(f"wildcard definition target is wrong: {star_def}")
|
||||
|
||||
def at(needle: str, delta: int = 0, occurrence: int = 0) -> dict[str, int]:
|
||||
pos = _position(text, needle, occurrence)
|
||||
|
||||
Reference in new issue
Block a user