zexus 1.6.8 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/package.json +1 -1
- package/src/__init__.py +7 -0
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
- package/src/zexus/advanced_types.py +17 -2
- package/src/zexus/blockchain/__init__.py +411 -0
- package/src/zexus/blockchain/accelerator.py +1160 -0
- package/src/zexus/blockchain/chain.py +660 -0
- package/src/zexus/blockchain/consensus.py +821 -0
- package/src/zexus/blockchain/contract_vm.py +1019 -0
- package/src/zexus/blockchain/crypto.py +79 -14
- package/src/zexus/blockchain/events.py +526 -0
- package/src/zexus/blockchain/loadtest.py +721 -0
- package/src/zexus/blockchain/monitoring.py +350 -0
- package/src/zexus/blockchain/mpt.py +716 -0
- package/src/zexus/blockchain/multichain.py +951 -0
- package/src/zexus/blockchain/multiprocess_executor.py +338 -0
- package/src/zexus/blockchain/network.py +886 -0
- package/src/zexus/blockchain/node.py +666 -0
- package/src/zexus/blockchain/rpc.py +1203 -0
- package/src/zexus/blockchain/rust_bridge.py +421 -0
- package/src/zexus/blockchain/storage.py +423 -0
- package/src/zexus/blockchain/tokens.py +750 -0
- package/src/zexus/blockchain/upgradeable.py +1004 -0
- package/src/zexus/blockchain/verification.py +1602 -0
- package/src/zexus/blockchain/wallet.py +621 -0
- package/src/zexus/capability_system.py +184 -9
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +383 -34
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/bytecode.py +124 -7
- package/src/zexus/compiler/compat_runtime.py +6 -2
- package/src/zexus/compiler/lexer.py +16 -5
- package/src/zexus/compiler/parser.py +108 -7
- package/src/zexus/compiler/semantic.py +18 -19
- package/src/zexus/compiler/zexus_ast.py +26 -1
- package/src/zexus/concurrency_system.py +79 -0
- package/src/zexus/config.py +54 -0
- package/src/zexus/crypto_bridge.py +244 -8
- package/src/zexus/dap/__init__.py +10 -0
- package/src/zexus/dap/__main__.py +4 -0
- package/src/zexus/dap/dap_server.py +391 -0
- package/src/zexus/dap/debug_engine.py +298 -0
- package/src/zexus/environment.py +112 -9
- package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +457 -37
- package/src/zexus/evaluator/core.py +644 -50
- package/src/zexus/evaluator/expressions.py +358 -62
- package/src/zexus/evaluator/functions.py +458 -20
- package/src/zexus/evaluator/resource_limiter.py +4 -4
- package/src/zexus/evaluator/statements.py +774 -122
- package/src/zexus/evaluator/unified_execution.py +573 -72
- package/src/zexus/evaluator/utils.py +14 -2
- package/src/zexus/evaluator_original.py +1 -1
- package/src/zexus/event_loop.py +186 -0
- package/src/zexus/lexer.py +742 -458
- package/src/zexus/lsp/__init__.py +1 -1
- package/src/zexus/lsp/definition_provider.py +163 -9
- package/src/zexus/lsp/server.py +22 -8
- package/src/zexus/lsp/symbol_provider.py +182 -9
- package/src/zexus/module_cache.py +239 -9
- package/src/zexus/module_manager.py +129 -1
- package/src/zexus/object.py +76 -6
- package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
- package/src/zexus/parser/parser.py +1349 -408
- package/src/zexus/parser/strategy_context.py +755 -58
- package/src/zexus/parser/strategy_structural.py +121 -21
- package/src/zexus/persistence.py +15 -1
- package/src/zexus/renderer/__init__.py +61 -0
- package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
- package/src/zexus/renderer/backend.py +261 -0
- package/src/zexus/renderer/canvas.py +78 -0
- package/src/zexus/renderer/color_system.py +201 -0
- package/src/zexus/renderer/graphics.py +31 -0
- package/src/zexus/renderer/layout.py +222 -0
- package/src/zexus/renderer/main_renderer.py +66 -0
- package/src/zexus/renderer/painter.py +30 -0
- package/src/zexus/renderer/tk_backend.py +208 -0
- package/src/zexus/renderer/web_backend.py +260 -0
- package/src/zexus/runtime/__init__.py +10 -2
- package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
- package/src/zexus/runtime/file_flags.py +137 -0
- package/src/zexus/runtime/load_manager.py +368 -0
- package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
- package/src/zexus/security.py +424 -34
- package/src/zexus/stdlib/fs.py +23 -18
- package/src/zexus/stdlib/http.py +289 -186
- package/src/zexus/stdlib/sockets.py +207 -163
- package/src/zexus/stdlib/websockets.py +282 -0
- package/src/zexus/stdlib_integration.py +369 -2
- package/src/zexus/strategy_recovery.py +6 -3
- package/src/zexus/type_checker.py +423 -0
- package/src/zexus/virtual_filesystem.py +189 -2
- package/src/zexus/vm/__init__.py +113 -3
- package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +80 -6
- package/src/zexus/vm/binary_bytecode.py +659 -0
- package/src/zexus/vm/bytecode.py +59 -11
- package/src/zexus/vm/bytecode_converter.py +26 -12
- package/src/zexus/vm/cabi.c +1985 -0
- package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/cabi.h +127 -0
- package/src/zexus/vm/cache.py +561 -17
- package/src/zexus/vm/compiler.py +818 -51
- package/src/zexus/vm/fastops.c +15743 -0
- package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/fastops.pyx +288 -0
- package/src/zexus/vm/gas_metering.py +50 -9
- package/src/zexus/vm/jit.py +364 -20
- package/src/zexus/vm/native_jit_backend.py +1816 -0
- package/src/zexus/vm/native_runtime.cpp +1388 -0
- package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/optimizer.py +161 -11
- package/src/zexus/vm/parallel_vm.py +140 -45
- package/src/zexus/vm/peephole_optimizer.py +82 -4
- package/src/zexus/vm/profiler.py +38 -18
- package/src/zexus/vm/register_allocator.py +16 -5
- package/src/zexus/vm/register_vm.py +8 -5
- package/src/zexus/vm/vm.py +3581 -531
- package/src/zexus/vm/wasm_compiler.py +658 -0
- package/src/zexus/zexus_ast.py +137 -11
- package/src/zexus/zexus_token.py +16 -5
- package/src/zexus/zpm/installer.py +55 -15
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus/zpm/registry.py +257 -28
- package/src/zexus.egg-info/PKG-INFO +16 -6
- package/src/zexus.egg-info/SOURCES.txt +129 -17
- package/src/zexus.egg-info/entry_points.txt +1 -0
- package/src/zexus.egg-info/requires.txt +4 -0
|
@@ -1,22 +1,176 @@
|
|
|
1
|
-
"""Definition provider for Zexus LSP."""
|
|
1
|
+
"""Definition provider for Zexus LSP — go-to-definition support."""
|
|
2
2
|
|
|
3
3
|
from typing import List, Dict, Any, Optional
|
|
4
|
+
|
|
4
5
|
try:
|
|
5
|
-
from pygls.lsp.types import Position
|
|
6
|
+
from pygls.lsp.types import Position, Location, Range
|
|
6
7
|
PYGLS_AVAILABLE = True
|
|
7
8
|
except ImportError:
|
|
8
9
|
PYGLS_AVAILABLE = False
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
# AST node types that introduce named definitions
|
|
13
|
+
_DEFINITION_NODE_TYPES = (
|
|
14
|
+
'ActionStatement', 'FunctionStatement', 'PureFunctionStatement',
|
|
15
|
+
'LetStatement', 'ConstStatement',
|
|
16
|
+
'EntityStatement', 'ContractStatement', 'DataStatement',
|
|
17
|
+
'EnumStatement', 'InterfaceStatement', 'ProtocolStatement',
|
|
18
|
+
'TypeAliasStatement', 'ModuleStatement', 'PackageStatement',
|
|
19
|
+
'ScreenStatement', 'ComponentStatement', 'MiddlewareStatement',
|
|
20
|
+
'PatternStatement', 'StreamStatement', 'ModifierDeclaration',
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_name(node) -> Optional[str]:
|
|
25
|
+
"""Extract the string name from an AST definition node."""
|
|
26
|
+
name = getattr(node, 'name', None)
|
|
27
|
+
if name is None:
|
|
28
|
+
return None
|
|
29
|
+
if hasattr(name, 'value'):
|
|
30
|
+
return str(name.value)
|
|
31
|
+
return str(name)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _collect_definitions(node, defs: Dict[str, list]):
|
|
35
|
+
"""Walk the AST recursively and collect all named definitions.
|
|
36
|
+
|
|
37
|
+
``defs`` maps name → list of AST nodes (same name may be defined
|
|
38
|
+
more than once, e.g. multiple ``let`` re-assignments).
|
|
39
|
+
"""
|
|
40
|
+
if node is None:
|
|
41
|
+
return
|
|
42
|
+
node_type = type(node).__name__
|
|
43
|
+
|
|
44
|
+
if node_type in _DEFINITION_NODE_TYPES:
|
|
45
|
+
name = _get_name(node)
|
|
46
|
+
if name:
|
|
47
|
+
defs.setdefault(name, []).append(node)
|
|
48
|
+
|
|
49
|
+
# Recurse into child nodes that may contain more definitions
|
|
50
|
+
for attr in ('statements', 'body', 'consequence', 'alternative',
|
|
51
|
+
'methods', 'properties', 'cases', 'block', 'try_block',
|
|
52
|
+
'catch_block', 'finally_block', 'members'):
|
|
53
|
+
child = getattr(node, attr, None)
|
|
54
|
+
if child is None:
|
|
55
|
+
continue
|
|
56
|
+
if isinstance(child, list):
|
|
57
|
+
for c in child:
|
|
58
|
+
_collect_definitions(c, defs)
|
|
59
|
+
else:
|
|
60
|
+
# BlockStatement, Program, etc.
|
|
61
|
+
stmts = getattr(child, 'statements', None)
|
|
62
|
+
if isinstance(stmts, list):
|
|
63
|
+
for c in stmts:
|
|
64
|
+
_collect_definitions(c, defs)
|
|
65
|
+
else:
|
|
66
|
+
_collect_definitions(child, defs)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _find_token_position(tokens, name: str, definition_keywords=None):
|
|
70
|
+
"""Scan the token list for the *first* definition of ``name``.
|
|
71
|
+
|
|
72
|
+
We look for a keyword token (``action``, ``let``, ``const``, ``entity``,
|
|
73
|
+
``contract``, ``data``, ``enum``, ``interface``, ``protocol``, ``type``,
|
|
74
|
+
``module``, ``package``, ``screen``, ``component``, ``middleware``,
|
|
75
|
+
``pattern``, ``stream``, ``pure``) followed (possibly after a type
|
|
76
|
+
annotation) by an ``IDENT`` token whose ``literal`` matches ``name``.
|
|
77
|
+
Returns ``(line, column)`` (0-based) or ``None``.
|
|
78
|
+
"""
|
|
79
|
+
if definition_keywords is None:
|
|
80
|
+
definition_keywords = {
|
|
81
|
+
'action', 'let', 'const', 'entity', 'contract', 'data',
|
|
82
|
+
'enum', 'interface', 'protocol', 'type', 'module', 'package',
|
|
83
|
+
'screen', 'component', 'middleware', 'pattern', 'stream',
|
|
84
|
+
'pure', 'fn', 'function', 'def', 'class', 'struct', 'modifier',
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
want_ident = False
|
|
88
|
+
for tok in tokens:
|
|
89
|
+
lit = getattr(tok, 'literal', '')
|
|
90
|
+
ttype = getattr(tok, 'type', '')
|
|
91
|
+
|
|
92
|
+
if lit in definition_keywords:
|
|
93
|
+
want_ident = True
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if want_ident and ttype == 'IDENT' and lit == name:
|
|
97
|
+
line = getattr(tok, 'line', 0)
|
|
98
|
+
col = getattr(tok, 'column', 0)
|
|
99
|
+
# Token lines are 1-based; LSP uses 0-based
|
|
100
|
+
return (max(0, line - 1), max(0, col))
|
|
101
|
+
|
|
102
|
+
if want_ident and ttype not in ('IDENT', 'COLON', ':', 'LBRACKET', 'RBRACKET',
|
|
103
|
+
'LT', 'GT', 'COMMA', 'STRING'):
|
|
104
|
+
# Not part of a type annotation — reset
|
|
105
|
+
want_ident = False
|
|
106
|
+
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _word_at_position(text: str, line: int, character: int) -> Optional[str]:
|
|
111
|
+
"""Extract the identifier under the cursor."""
|
|
112
|
+
lines = text.split('\n')
|
|
113
|
+
if line >= len(lines):
|
|
114
|
+
return None
|
|
115
|
+
row = lines[line]
|
|
116
|
+
if character >= len(row):
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
# Walk left
|
|
120
|
+
start = character
|
|
121
|
+
while start > 0 and (row[start - 1].isalnum() or row[start - 1] == '_'):
|
|
122
|
+
start -= 1
|
|
123
|
+
# Walk right
|
|
124
|
+
end = character
|
|
125
|
+
while end < len(row) and (row[end].isalnum() or row[end] == '_'):
|
|
126
|
+
end += 1
|
|
127
|
+
|
|
128
|
+
word = row[start:end]
|
|
129
|
+
return word if word else None
|
|
130
|
+
|
|
131
|
+
|
|
11
132
|
class DefinitionProvider:
|
|
12
133
|
"""Provides go-to-definition for Zexus code."""
|
|
13
134
|
|
|
14
|
-
def get_definition(self, uri: str, position
|
|
15
|
-
"""
|
|
135
|
+
def get_definition(self, uri: str, position, doc_info: Dict[str, Any]) -> Optional[List]:
|
|
136
|
+
"""Return a list of ``Location`` objects for the definition of the
|
|
137
|
+
symbol under the cursor, or ``None`` if not found.
|
|
138
|
+
"""
|
|
16
139
|
if not PYGLS_AVAILABLE:
|
|
17
140
|
return None
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
141
|
+
|
|
142
|
+
text = doc_info.get('text', '')
|
|
143
|
+
ast = doc_info.get('ast')
|
|
144
|
+
tokens = doc_info.get('tokens', [])
|
|
145
|
+
|
|
146
|
+
if not text:
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
line = position.line
|
|
150
|
+
character = position.character
|
|
151
|
+
word = _word_at_position(text, line, character)
|
|
152
|
+
if not word:
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
# 1. Collect all definitions from the AST
|
|
156
|
+
defs: Dict[str, list] = {}
|
|
157
|
+
if ast:
|
|
158
|
+
_collect_definitions(ast, defs)
|
|
159
|
+
|
|
160
|
+
if word not in defs:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
# 2. Find the first definition position in the token stream
|
|
164
|
+
pos = _find_token_position(tokens, word)
|
|
165
|
+
if pos is None:
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
def_line, def_col = pos
|
|
169
|
+
location = Location(
|
|
170
|
+
uri=uri,
|
|
171
|
+
range=Range(
|
|
172
|
+
start=Position(line=def_line, character=def_col),
|
|
173
|
+
end=Position(line=def_line, character=def_col + len(word)),
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
return [location]
|
package/src/zexus/lsp/server.py
CHANGED
|
@@ -107,10 +107,18 @@ if PYGLS_AVAILABLE:
|
|
|
107
107
|
|
|
108
108
|
# Parse the document
|
|
109
109
|
try:
|
|
110
|
-
lexer = Lexer(text)
|
|
111
|
-
tokens
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
lexer = Lexer(text, filename=uri)
|
|
111
|
+
# Collect tokens for position mapping (used by providers)
|
|
112
|
+
tokens = []
|
|
113
|
+
while True:
|
|
114
|
+
tok = lexer.next_token()
|
|
115
|
+
tokens.append(tok)
|
|
116
|
+
if getattr(tok, 'type', '') == 'EOF':
|
|
117
|
+
break
|
|
118
|
+
# Re-lex for the parser (parser consumes the lexer)
|
|
119
|
+
parse_lexer = Lexer(text, filename=uri)
|
|
120
|
+
parser = Parser(parse_lexer)
|
|
121
|
+
ast = parser.parse_program()
|
|
114
122
|
ls.documents[uri] = {
|
|
115
123
|
'text': text,
|
|
116
124
|
'ast': ast,
|
|
@@ -136,10 +144,16 @@ if PYGLS_AVAILABLE:
|
|
|
136
144
|
text = changes[0].text
|
|
137
145
|
# Re-parse the document
|
|
138
146
|
try:
|
|
139
|
-
lexer = Lexer(text)
|
|
140
|
-
tokens =
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
lexer = Lexer(text, filename=uri)
|
|
148
|
+
tokens = []
|
|
149
|
+
while True:
|
|
150
|
+
tok = lexer.next_token()
|
|
151
|
+
tokens.append(tok)
|
|
152
|
+
if getattr(tok, 'type', '') == 'EOF':
|
|
153
|
+
break
|
|
154
|
+
parse_lexer = Lexer(text, filename=uri)
|
|
155
|
+
parser = Parser(parse_lexer)
|
|
156
|
+
ast = parser.parse_program()
|
|
143
157
|
ls.documents[uri] = {
|
|
144
158
|
'text': text,
|
|
145
159
|
'ast': ast,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
"""Symbol provider for Zexus LSP."""
|
|
1
|
+
"""Symbol provider for Zexus LSP — document outline and symbol search."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any, Optional
|
|
2
4
|
|
|
3
|
-
from typing import List, Dict, Any
|
|
4
5
|
try:
|
|
5
6
|
from pygls.lsp.types import (DocumentSymbol, SymbolKind, Range, Position)
|
|
6
7
|
PYGLS_AVAILABLE = True
|
|
@@ -8,7 +9,169 @@ except ImportError:
|
|
|
8
9
|
PYGLS_AVAILABLE = False
|
|
9
10
|
# Define minimal stubs when pygls not available
|
|
10
11
|
class SymbolKind:
|
|
11
|
-
|
|
12
|
+
Function = 12
|
|
13
|
+
Variable = 13
|
|
14
|
+
Constant = 14
|
|
15
|
+
Class = 5
|
|
16
|
+
Struct = 23
|
|
17
|
+
Enum = 10
|
|
18
|
+
Interface = 11
|
|
19
|
+
Module = 2
|
|
20
|
+
Package = 4
|
|
21
|
+
TypeParameter = 26
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Map AST node type name → (SymbolKind, detail label)
|
|
25
|
+
_SYMBOL_MAP = {
|
|
26
|
+
'ActionStatement': (SymbolKind.Function, 'action'),
|
|
27
|
+
'FunctionStatement': (SymbolKind.Function, 'function'),
|
|
28
|
+
'PureFunctionStatement': (SymbolKind.Function, 'pure function'),
|
|
29
|
+
'LetStatement': (SymbolKind.Variable, 'let'),
|
|
30
|
+
'ConstStatement': (SymbolKind.Constant, 'const'),
|
|
31
|
+
'EntityStatement': (SymbolKind.Class, 'entity'),
|
|
32
|
+
'ContractStatement': (SymbolKind.Class, 'contract'),
|
|
33
|
+
'DataStatement': (SymbolKind.Struct, 'data'),
|
|
34
|
+
'EnumStatement': (SymbolKind.Enum, 'enum'),
|
|
35
|
+
'InterfaceStatement': (SymbolKind.Interface, 'interface'),
|
|
36
|
+
'ProtocolStatement': (SymbolKind.Interface, 'protocol'),
|
|
37
|
+
'TypeAliasStatement': (SymbolKind.TypeParameter, 'type'),
|
|
38
|
+
'ModuleStatement': (SymbolKind.Module, 'module'),
|
|
39
|
+
'PackageStatement': (SymbolKind.Package, 'package'),
|
|
40
|
+
'ScreenStatement': (SymbolKind.Function, 'screen'),
|
|
41
|
+
'ComponentStatement': (SymbolKind.Function, 'component'),
|
|
42
|
+
'MiddlewareStatement': (SymbolKind.Function, 'middleware'),
|
|
43
|
+
'PatternStatement': (SymbolKind.Function, 'pattern'),
|
|
44
|
+
'StreamStatement': (SymbolKind.Variable, 'stream'),
|
|
45
|
+
'ModifierDeclaration': (SymbolKind.Function, 'modifier'),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _get_name(node) -> Optional[str]:
|
|
50
|
+
"""Extract the string name from an AST definition node."""
|
|
51
|
+
name = getattr(node, 'name', None)
|
|
52
|
+
if name is None:
|
|
53
|
+
return None
|
|
54
|
+
if hasattr(name, 'value'):
|
|
55
|
+
return str(name.value)
|
|
56
|
+
return str(name)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _find_name_line(tokens, name: str, start_line: int = 0) -> int:
|
|
60
|
+
"""Return the 0-based line number where ``name`` appears as an IDENT
|
|
61
|
+
in the token stream, starting from ``start_line``. Returns
|
|
62
|
+
``start_line`` if not found.
|
|
63
|
+
"""
|
|
64
|
+
for tok in tokens:
|
|
65
|
+
ttype = getattr(tok, 'type', '')
|
|
66
|
+
lit = getattr(tok, 'literal', '')
|
|
67
|
+
line = getattr(tok, 'line', 0)
|
|
68
|
+
if ttype == 'IDENT' and lit == name and (line - 1) >= start_line:
|
|
69
|
+
return max(0, line - 1)
|
|
70
|
+
return start_line
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _make_range(line: int, col: int = 0, end_line: Optional[int] = None, length: int = 1):
|
|
74
|
+
"""Build a pygls Range. Lines/cols are 0-based."""
|
|
75
|
+
if end_line is None:
|
|
76
|
+
end_line = line
|
|
77
|
+
return Range(
|
|
78
|
+
start=Position(line=line, character=col),
|
|
79
|
+
end=Position(line=end_line, character=col + length),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _node_to_symbol(node, tokens, text_lines) -> Optional[DocumentSymbol]:
|
|
84
|
+
"""Convert an AST definition node into a ``DocumentSymbol``.
|
|
85
|
+
|
|
86
|
+
Returns ``None`` if the node does not map to a symbol.
|
|
87
|
+
"""
|
|
88
|
+
if not PYGLS_AVAILABLE:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
node_type = type(node).__name__
|
|
92
|
+
entry = _SYMBOL_MAP.get(node_type)
|
|
93
|
+
if entry is None:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
kind, detail = entry
|
|
97
|
+
name = _get_name(node)
|
|
98
|
+
if not name:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
# Locate the name in the token stream
|
|
102
|
+
line = _find_name_line(tokens, name)
|
|
103
|
+
symbol_range = _make_range(line, length=len(name))
|
|
104
|
+
selection_range = symbol_range
|
|
105
|
+
|
|
106
|
+
# Build children for container types (entity, contract, enum, data)
|
|
107
|
+
children = []
|
|
108
|
+
|
|
109
|
+
if node_type == 'EntityStatement':
|
|
110
|
+
# Properties
|
|
111
|
+
for prop in (getattr(node, 'properties', None) or []):
|
|
112
|
+
prop_name = _get_name(prop)
|
|
113
|
+
if prop_name:
|
|
114
|
+
pline = _find_name_line(tokens, prop_name, line)
|
|
115
|
+
children.append(DocumentSymbol(
|
|
116
|
+
name=prop_name,
|
|
117
|
+
kind=SymbolKind.Variable,
|
|
118
|
+
range=_make_range(pline, length=len(prop_name)),
|
|
119
|
+
selection_range=_make_range(pline, length=len(prop_name)),
|
|
120
|
+
detail='property',
|
|
121
|
+
))
|
|
122
|
+
# Methods
|
|
123
|
+
for method in (getattr(node, 'methods', None) or []):
|
|
124
|
+
child_sym = _node_to_symbol(method, tokens, text_lines)
|
|
125
|
+
if child_sym:
|
|
126
|
+
children.append(child_sym)
|
|
127
|
+
|
|
128
|
+
elif node_type == 'ContractStatement':
|
|
129
|
+
body = getattr(node, 'body', None)
|
|
130
|
+
stmts = getattr(body, 'statements', None) if body else None
|
|
131
|
+
if isinstance(stmts, list):
|
|
132
|
+
for stmt in stmts:
|
|
133
|
+
child_sym = _node_to_symbol(stmt, tokens, text_lines)
|
|
134
|
+
if child_sym:
|
|
135
|
+
children.append(child_sym)
|
|
136
|
+
|
|
137
|
+
elif node_type == 'EnumStatement':
|
|
138
|
+
for member in (getattr(node, 'members', None) or []):
|
|
139
|
+
m_name = _get_name(member) or getattr(member, 'value', None)
|
|
140
|
+
if isinstance(m_name, str):
|
|
141
|
+
pass
|
|
142
|
+
elif hasattr(m_name, 'value'):
|
|
143
|
+
m_name = m_name.value
|
|
144
|
+
if m_name:
|
|
145
|
+
mline = _find_name_line(tokens, str(m_name), line)
|
|
146
|
+
children.append(DocumentSymbol(
|
|
147
|
+
name=str(m_name),
|
|
148
|
+
kind=SymbolKind.Constant,
|
|
149
|
+
range=_make_range(mline, length=len(str(m_name))),
|
|
150
|
+
selection_range=_make_range(mline, length=len(str(m_name))),
|
|
151
|
+
detail='member',
|
|
152
|
+
))
|
|
153
|
+
|
|
154
|
+
elif node_type == 'DataStatement':
|
|
155
|
+
for field in (getattr(node, 'fields', None) or []):
|
|
156
|
+
f_name = _get_name(field)
|
|
157
|
+
if f_name:
|
|
158
|
+
fline = _find_name_line(tokens, f_name, line)
|
|
159
|
+
children.append(DocumentSymbol(
|
|
160
|
+
name=f_name,
|
|
161
|
+
kind=SymbolKind.Variable,
|
|
162
|
+
range=_make_range(fline, length=len(f_name)),
|
|
163
|
+
selection_range=_make_range(fline, length=len(f_name)),
|
|
164
|
+
detail='field',
|
|
165
|
+
))
|
|
166
|
+
|
|
167
|
+
return DocumentSymbol(
|
|
168
|
+
name=name,
|
|
169
|
+
kind=kind,
|
|
170
|
+
range=symbol_range,
|
|
171
|
+
selection_range=selection_range,
|
|
172
|
+
detail=detail,
|
|
173
|
+
children=children if children else None,
|
|
174
|
+
)
|
|
12
175
|
|
|
13
176
|
|
|
14
177
|
class SymbolProvider:
|
|
@@ -18,14 +181,24 @@ class SymbolProvider:
|
|
|
18
181
|
"""Get document symbols from AST."""
|
|
19
182
|
if not PYGLS_AVAILABLE:
|
|
20
183
|
return []
|
|
21
|
-
|
|
184
|
+
|
|
22
185
|
symbols = []
|
|
23
186
|
ast = doc_info.get('ast')
|
|
24
|
-
|
|
187
|
+
tokens = doc_info.get('tokens', [])
|
|
188
|
+
text = doc_info.get('text', '')
|
|
189
|
+
text_lines = text.split('\n') if text else []
|
|
190
|
+
|
|
25
191
|
if not ast:
|
|
26
192
|
return symbols
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
193
|
+
|
|
194
|
+
# Walk top-level statements
|
|
195
|
+
stmts = getattr(ast, 'statements', [])
|
|
196
|
+
if not isinstance(stmts, list):
|
|
197
|
+
return symbols
|
|
198
|
+
|
|
199
|
+
for stmt in stmts:
|
|
200
|
+
sym = _node_to_symbol(stmt, tokens, text_lines)
|
|
201
|
+
if sym:
|
|
202
|
+
symbols.append(sym)
|
|
203
|
+
|
|
31
204
|
return symbols
|