avrae-ls 0.4.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
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.
- avrae_ls/alias_preview.py +175 -9
- avrae_ls/code_actions.py +282 -0
- avrae_ls/codes.py +3 -0
- avrae_ls/completions.py +485 -78
- avrae_ls/diagnostics.py +62 -5
- avrae_ls/server.py +14 -5
- avrae_ls/signature_help.py +56 -5
- {avrae_ls-0.4.0.dist-info → avrae_ls-0.4.1.dist-info}/METADATA +1 -1
- {avrae_ls-0.4.0.dist-info → avrae_ls-0.4.1.dist-info}/RECORD +13 -11
- {avrae_ls-0.4.0.dist-info → avrae_ls-0.4.1.dist-info}/WHEEL +0 -0
- {avrae_ls-0.4.0.dist-info → avrae_ls-0.4.1.dist-info}/entry_points.txt +0 -0
- {avrae_ls-0.4.0.dist-info → avrae_ls-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {avrae_ls-0.4.0.dist-info → avrae_ls-0.4.1.dist-info}/top_level.txt +0 -0
avrae_ls/diagnostics.py
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import inspect
|
|
4
3
|
import ast
|
|
4
|
+
import inspect
|
|
5
5
|
import logging
|
|
6
|
-
from typing import Dict, Iterable, List, Sequence, Set
|
|
6
|
+
from typing import Any, Dict, Iterable, List, Sequence, Set
|
|
7
7
|
|
|
8
8
|
import draconic
|
|
9
9
|
from lsprotocol import types
|
|
10
10
|
|
|
11
|
+
from .alias_preview import simulate_command
|
|
12
|
+
from .codes import MISSING_GVAR_CODE, UNDEFINED_NAME_CODE, UNSUPPORTED_IMPORT_CODE
|
|
11
13
|
from .argument_parsing import apply_argument_parsing
|
|
12
14
|
from .completions import _infer_type_map, _resolve_type_name, _type_meta
|
|
13
15
|
from .config import DiagnosticSettings
|
|
@@ -41,6 +43,10 @@ class DiagnosticProvider:
|
|
|
41
43
|
source = apply_argument_parsing(source)
|
|
42
44
|
blocks = find_draconic_blocks(source)
|
|
43
45
|
if not blocks:
|
|
46
|
+
plain = _plain_command_diagnostics(source)
|
|
47
|
+
if plain is not None:
|
|
48
|
+
diagnostics.extend(plain)
|
|
49
|
+
return diagnostics
|
|
44
50
|
diagnostics.extend(await self._analyze_code(source, ctx_data, gvar_resolver))
|
|
45
51
|
return diagnostics
|
|
46
52
|
|
|
@@ -150,6 +156,8 @@ class DiagnosticProvider:
|
|
|
150
156
|
node,
|
|
151
157
|
f"'{node.id}' may be undefined in this scope",
|
|
152
158
|
severity_level,
|
|
159
|
+
code=UNDEFINED_NAME_CODE,
|
|
160
|
+
data={"name": node.id},
|
|
153
161
|
)
|
|
154
162
|
)
|
|
155
163
|
|
|
@@ -237,6 +245,8 @@ async def _check_gvars(
|
|
|
237
245
|
arg_node,
|
|
238
246
|
f"Unknown gvar '{gvar_id}'",
|
|
239
247
|
settings.semantic_level,
|
|
248
|
+
code=MISSING_GVAR_CODE,
|
|
249
|
+
data={"gvar": gvar_id},
|
|
240
250
|
)
|
|
241
251
|
)
|
|
242
252
|
|
|
@@ -487,7 +497,14 @@ def _build_parent_map(root: ast.AST) -> Dict[ast.AST, ast.AST]:
|
|
|
487
497
|
return parents
|
|
488
498
|
|
|
489
499
|
|
|
490
|
-
def _make_diagnostic(
|
|
500
|
+
def _make_diagnostic(
|
|
501
|
+
node: ast.AST,
|
|
502
|
+
message: str,
|
|
503
|
+
level: str,
|
|
504
|
+
*,
|
|
505
|
+
code: str | None = None,
|
|
506
|
+
data: Dict[str, Any] | None = None,
|
|
507
|
+
) -> types.Diagnostic:
|
|
491
508
|
severity = SEVERITY.get(level, types.DiagnosticSeverity.Warning)
|
|
492
509
|
if hasattr(node, "lineno"):
|
|
493
510
|
rng = _range_from_positions(
|
|
@@ -506,6 +523,8 @@ def _make_diagnostic(node: ast.AST, message: str, level: str) -> types.Diagnosti
|
|
|
506
523
|
range=rng,
|
|
507
524
|
severity=severity,
|
|
508
525
|
source="avrae-ls",
|
|
526
|
+
code=code,
|
|
527
|
+
data=data,
|
|
509
528
|
)
|
|
510
529
|
|
|
511
530
|
|
|
@@ -666,10 +685,27 @@ def _check_imports(body: Sequence[ast.AST], severity_level: str) -> List[types.D
|
|
|
666
685
|
|
|
667
686
|
class Visitor(ast.NodeVisitor):
|
|
668
687
|
def visit_Import(self, node: ast.Import):
|
|
669
|
-
|
|
688
|
+
module = node.names[0].name if node.names else None
|
|
689
|
+
diagnostics.append(
|
|
690
|
+
_make_diagnostic(
|
|
691
|
+
node,
|
|
692
|
+
"Imports are not supported in draconic aliases",
|
|
693
|
+
severity_level,
|
|
694
|
+
code=UNSUPPORTED_IMPORT_CODE,
|
|
695
|
+
data={"module": module} if module else None,
|
|
696
|
+
)
|
|
697
|
+
)
|
|
670
698
|
|
|
671
699
|
def visit_ImportFrom(self, node: ast.ImportFrom):
|
|
672
|
-
diagnostics.append(
|
|
700
|
+
diagnostics.append(
|
|
701
|
+
_make_diagnostic(
|
|
702
|
+
node,
|
|
703
|
+
"Imports are not supported in draconic aliases",
|
|
704
|
+
severity_level,
|
|
705
|
+
code=UNSUPPORTED_IMPORT_CODE,
|
|
706
|
+
data={"module": node.module},
|
|
707
|
+
)
|
|
708
|
+
)
|
|
673
709
|
|
|
674
710
|
visitor = Visitor()
|
|
675
711
|
for stmt in body:
|
|
@@ -692,3 +728,24 @@ def _range_from_positions(
|
|
|
692
728
|
character=max(((end_col_offset or col_offset or 1) - 1), 0),
|
|
693
729
|
)
|
|
694
730
|
return types.Range(start=start, end=end)
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def _plain_command_diagnostics(source: str) -> list[types.Diagnostic] | None:
|
|
734
|
+
"""Handle simple commands (embed/echo) without draconic blocks."""
|
|
735
|
+
simulated = simulate_command(source)
|
|
736
|
+
if not simulated.command_name:
|
|
737
|
+
return None
|
|
738
|
+
if simulated.command_name == "embed":
|
|
739
|
+
if simulated.validation_error:
|
|
740
|
+
return [
|
|
741
|
+
types.Diagnostic(
|
|
742
|
+
message=simulated.validation_error,
|
|
743
|
+
range=_range_from_positions(1, 1, 1, 1),
|
|
744
|
+
severity=SEVERITY["warning"],
|
|
745
|
+
source="avrae-ls",
|
|
746
|
+
)
|
|
747
|
+
]
|
|
748
|
+
return []
|
|
749
|
+
if simulated.command_name == "echo":
|
|
750
|
+
return []
|
|
751
|
+
return None
|
avrae_ls/server.py
CHANGED
|
@@ -18,6 +18,7 @@ from .alias_preview import render_alias_command, simulate_command
|
|
|
18
18
|
from .parser import find_draconic_blocks
|
|
19
19
|
from .signature_help import load_signatures, signature_help_for_code
|
|
20
20
|
from .completions import gather_suggestions, completion_items_for_position, hover_for_position
|
|
21
|
+
from .code_actions import code_actions_for_document
|
|
21
22
|
from .symbols import build_symbol_table, document_symbols, find_definition_range, find_references, range_for_word
|
|
22
23
|
from .argument_parsing import apply_argument_parsing
|
|
23
24
|
|
|
@@ -260,6 +261,12 @@ def on_hover(server: AvraeLanguageServer, params: types.HoverParams):
|
|
|
260
261
|
return None
|
|
261
262
|
|
|
262
263
|
|
|
264
|
+
@ls.feature(types.TEXT_DOCUMENT_CODE_ACTION)
|
|
265
|
+
def on_code_action(server: AvraeLanguageServer, params: types.CodeActionParams):
|
|
266
|
+
doc = server.workspace.get_text_document(params.text_document.uri)
|
|
267
|
+
return code_actions_for_document(doc.source, params, server.workspace_root)
|
|
268
|
+
|
|
269
|
+
|
|
263
270
|
@ls.command(RUN_ALIAS_COMMAND)
|
|
264
271
|
async def run_alias(server: AvraeLanguageServer, *args: Any):
|
|
265
272
|
payload = args[0] if args else {}
|
|
@@ -289,18 +296,20 @@ async def run_alias(server: AvraeLanguageServer, *args: Any):
|
|
|
289
296
|
server.state.context_builder.gvar_resolver,
|
|
290
297
|
args=alias_args,
|
|
291
298
|
)
|
|
292
|
-
|
|
299
|
+
preview = simulate_command(rendered.command)
|
|
293
300
|
|
|
294
301
|
response: dict[str, Any] = {
|
|
295
302
|
"stdout": rendered.stdout,
|
|
296
|
-
"result":
|
|
303
|
+
"result": preview.preview if preview.preview is not None else rendered.last_value,
|
|
297
304
|
"command": rendered.command,
|
|
298
|
-
"commandName": command_name,
|
|
305
|
+
"commandName": preview.command_name,
|
|
299
306
|
}
|
|
300
307
|
if rendered.error:
|
|
301
308
|
response["error"] = _format_runtime_error(rendered.error)
|
|
302
|
-
if validation_error:
|
|
303
|
-
response["validationError"] = validation_error
|
|
309
|
+
if preview.validation_error:
|
|
310
|
+
response["validationError"] = preview.validation_error
|
|
311
|
+
if preview.embed:
|
|
312
|
+
response["embed"] = preview.embed.to_dict()
|
|
304
313
|
if uri:
|
|
305
314
|
extra = []
|
|
306
315
|
if rendered.error:
|
avrae_ls/signature_help.py
CHANGED
|
@@ -157,15 +157,29 @@ def signature_help_for_code(code: str, line: int, character: int, sigs: Dict[str
|
|
|
157
157
|
return None
|
|
158
158
|
|
|
159
159
|
target_call: ast.Call | None = None
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
target_depth = -1
|
|
161
|
+
|
|
162
|
+
class Finder(ast.NodeVisitor):
|
|
163
|
+
def __init__(self):
|
|
164
|
+
self.stack: list[ast.AST] = []
|
|
165
|
+
|
|
166
|
+
def visit_Call(self, node: ast.Call):
|
|
167
|
+
nonlocal target_call, target_depth
|
|
162
168
|
if hasattr(node, "lineno") and hasattr(node, "col_offset"):
|
|
163
169
|
start = (node.lineno - 1, node.col_offset)
|
|
164
170
|
end_line = getattr(node, "end_lineno", node.lineno) - 1
|
|
165
171
|
end_col = getattr(node, "end_col_offset", node.col_offset)
|
|
166
172
|
if _pos_within((line, character), start, (end_line, end_col)):
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
depth = len(self.stack)
|
|
174
|
+
# Prefer the most nested call covering the cursor
|
|
175
|
+
if depth >= target_depth:
|
|
176
|
+
target_call = node
|
|
177
|
+
target_depth = depth
|
|
178
|
+
self.stack.append(node)
|
|
179
|
+
self.generic_visit(node)
|
|
180
|
+
self.stack.pop()
|
|
181
|
+
|
|
182
|
+
Finder().visit(tree)
|
|
169
183
|
|
|
170
184
|
if not target_call:
|
|
171
185
|
return None
|
|
@@ -184,7 +198,7 @@ def signature_help_for_code(code: str, line: int, character: int, sigs: Dict[str
|
|
|
184
198
|
documentation=fsig.doc,
|
|
185
199
|
parameters=[types.ParameterInformation(label=p) for p in fsig.params],
|
|
186
200
|
)
|
|
187
|
-
active_param =
|
|
201
|
+
active_param = _active_param_index(target_call, (line, character), fsig.params)
|
|
188
202
|
return types.SignatureHelp(signatures=[sig_info], active_signature=0, active_parameter=active_param)
|
|
189
203
|
|
|
190
204
|
|
|
@@ -199,3 +213,40 @@ def _pos_within(pos: Tuple[int, int], start: Tuple[int, int], end: Tuple[int, in
|
|
|
199
213
|
if line == el and col > ec:
|
|
200
214
|
return False
|
|
201
215
|
return True
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _active_param_index(call: ast.Call, cursor: Tuple[int, int], params: List[str]) -> int:
|
|
219
|
+
if not params:
|
|
220
|
+
return 0
|
|
221
|
+
|
|
222
|
+
# Build spans for positional args and keywords in source order.
|
|
223
|
+
spans: list[tuple[Tuple[int, int], Tuple[int, int], ast.AST]] = []
|
|
224
|
+
for arg in call.args:
|
|
225
|
+
spans.append((_node_start(arg), _node_end(arg), arg))
|
|
226
|
+
for kw in call.keywords:
|
|
227
|
+
spans.append((_node_start(kw), _node_end(kw), kw))
|
|
228
|
+
spans.sort(key=lambda s: (s[0][0], s[0][1]))
|
|
229
|
+
|
|
230
|
+
def _clamp(idx: int) -> int:
|
|
231
|
+
return max(0, min(idx, max(len(params) - 1, 0)))
|
|
232
|
+
|
|
233
|
+
for idx, (start, end, node) in enumerate(spans):
|
|
234
|
+
if _pos_within(cursor, start, end):
|
|
235
|
+
if isinstance(node, ast.keyword) and node.arg and node.arg in params:
|
|
236
|
+
return _clamp(params.index(node.arg))
|
|
237
|
+
return _clamp(idx)
|
|
238
|
+
|
|
239
|
+
# If cursor is after some args but not inside one, infer next argument slot.
|
|
240
|
+
before_count = sum(1 for start, _, _ in spans if start <= cursor)
|
|
241
|
+
return _clamp(before_count)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _node_start(node: ast.AST) -> Tuple[int, int]:
|
|
245
|
+
return (getattr(node, "lineno", 1) - 1, getattr(node, "col_offset", 0))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _node_end(node: ast.AST) -> Tuple[int, int]:
|
|
249
|
+
return (
|
|
250
|
+
getattr(node, "end_lineno", getattr(node, "lineno", 1)) - 1,
|
|
251
|
+
getattr(node, "end_col_offset", getattr(node, "col_offset", 0)),
|
|
252
|
+
)
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
avrae_ls/__init__.py,sha256=BmjrnksGkbG7TPqwbyQvgYj9uei8pFSFpfkRpaGVdJU,63
|
|
2
2
|
avrae_ls/__main__.py,sha256=ch287lWe11go5xHAE9OkVppixt0vRF401E4zTs2tqQ0,3557
|
|
3
|
-
avrae_ls/alias_preview.py,sha256=
|
|
3
|
+
avrae_ls/alias_preview.py,sha256=YFUiY36runNe6R0k-9YrbGff4l3evgEsoWbVc8WT5nU,11462
|
|
4
4
|
avrae_ls/api.py,sha256=_AHvIEIlz34YeWdZDpXd1EwgdQUGk8-nqNIEN4qdNR8,65093
|
|
5
5
|
avrae_ls/argparser.py,sha256=DRptXGyK4f0r7NsuV1Sg4apG-qihIClOX408jgk4HH4,13762
|
|
6
6
|
avrae_ls/argument_parsing.py,sha256=ezKl65VwuNEDxt6KlYwVQcpy1110UDvf4BqZqgZTcqk,2122
|
|
7
|
-
avrae_ls/
|
|
7
|
+
avrae_ls/code_actions.py,sha256=CYl3nMzCaE9k35p5fWAmzsTOVfcv2RrkA3SbtyCdcsI,9423
|
|
8
|
+
avrae_ls/codes.py,sha256=iPRPQ6i9DZheae4_ra1y29vCw3Y4SEu6Udf5WiZj_RY,136
|
|
9
|
+
avrae_ls/completions.py,sha256=CvwKp3f20RsPFPnjUv03xCbuaGKIyIF7VgVhYNbig4U,51180
|
|
8
10
|
avrae_ls/config.py,sha256=ULEfWbzIO05cllHBLUeGY_CHLpX6tHyCJYIEQgKSoXc,17383
|
|
9
11
|
avrae_ls/context.py,sha256=KcCKqzqJCG2xlEtou5HWz1OWISApGl8IfwOvVnNi9nc,8464
|
|
10
12
|
avrae_ls/cvars.py,sha256=0tcVbUHx_CKJ6aou3kEsKX37LRWAjkUWlqqIuSRFlXk,3197
|
|
11
|
-
avrae_ls/diagnostics.py,sha256=
|
|
13
|
+
avrae_ls/diagnostics.py,sha256=wq5sBjI11NyTNtnjoZTGm1vdcvOvW7OoPHbCDAbF308,24914
|
|
12
14
|
avrae_ls/dice.py,sha256=DY7V7L-EwAXaCgddgVe9xU1s9lVtiw5Zc2reipNgdTk,874
|
|
13
15
|
avrae_ls/parser.py,sha256=UQDwjupuJVU6KvoF4D8r3azPT7ksP_nTZsm5FUhGnWE,1395
|
|
14
16
|
avrae_ls/runtime.py,sha256=PDnjZXcU5DqM9jKSc4S0jHObBE7SWKte17PWWXGQ34M,23356
|
|
15
|
-
avrae_ls/server.py,sha256=
|
|
16
|
-
avrae_ls/signature_help.py,sha256=
|
|
17
|
+
avrae_ls/server.py,sha256=s3Cl6Ta0954FqBUh9pM326TwdUBLb7ZuyzR8vjQqs5c,15101
|
|
18
|
+
avrae_ls/signature_help.py,sha256=sVQncCeLNCD3UBVPZu9ZFw0b-8wq64n3qFPLtWSy_Cs,8827
|
|
17
19
|
avrae_ls/symbols.py,sha256=Lm5LH-F60JYNuAtSjRhHeyHEWAWxpnNb0w6KR0u1Zac,8422
|
|
18
|
-
avrae_ls-0.4.
|
|
20
|
+
avrae_ls-0.4.1.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
|
|
19
21
|
draconic/LICENSE,sha256=Fzvu32_DafLKKn2mzxhEdlmrKZzAsigDZ87O7uoVqZI,1067
|
|
20
22
|
draconic/__init__.py,sha256=YPH420Pcn_nTkfB62hJy_YqC5kpJdzSa78jP8n4z_xY,109
|
|
21
23
|
draconic/exceptions.py,sha256=siahnHIsumbaUhKBDSrw_DmLZ-0oZks8L5oytPH8hD4,3753
|
|
@@ -25,8 +27,8 @@ draconic/string.py,sha256=kGrRc6wNHRq1y5xw8Os-fBhfINDtIY2nBWQWkyLSfQI,2858
|
|
|
25
27
|
draconic/types.py,sha256=1Lsr6z8bW5agglGI4hLt_nPtYuZOIf_ueSpPDB4WDrs,13686
|
|
26
28
|
draconic/utils.py,sha256=D4vJ-txqS2-rlqsEpXAC46_j1sZX4UjY-9zIgElo96k,3122
|
|
27
29
|
draconic/versions.py,sha256=CUEsgUWjAmjez0432WwiBwZlIzWPIObwZUf8Yld18EE,84
|
|
28
|
-
avrae_ls-0.4.
|
|
29
|
-
avrae_ls-0.4.
|
|
30
|
-
avrae_ls-0.4.
|
|
31
|
-
avrae_ls-0.4.
|
|
32
|
-
avrae_ls-0.4.
|
|
30
|
+
avrae_ls-0.4.1.dist-info/METADATA,sha256=4kw6QJtVMqp_8pHBPra7sJXjchuCQdSOYiwj_dt7j1k,4713
|
|
31
|
+
avrae_ls-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
32
|
+
avrae_ls-0.4.1.dist-info/entry_points.txt,sha256=OtYXipMQzqmxpMoApgo0MeJYFmMbkbFN51Ibhpb8hF4,52
|
|
33
|
+
avrae_ls-0.4.1.dist-info/top_level.txt,sha256=TL68uzGHmB2R2ID32_s2zocmcNnpMJVQ6_4NBvyo8a4,18
|
|
34
|
+
avrae_ls-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|