avrae-ls 0.6.3__tar.gz → 0.7.0__tar.gz
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-0.6.3 → avrae_ls-0.7.0}/PKG-INFO +6 -1
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/README.md +5 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/pyproject.toml +1 -1
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/__main__.py +54 -5
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/alias_preview.py +38 -6
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/alias_tests.py +2 -0
- avrae_ls-0.7.0/src/avrae_ls/ast_utils.py +14 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/code_actions.py +6 -2
- avrae_ls-0.7.0/src/avrae_ls/completions.py +730 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/config.py +2 -5
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/context.py +65 -34
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/diagnostics.py +33 -60
- avrae_ls-0.7.0/src/avrae_ls/lsp_utils.py +41 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/parser.py +30 -3
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/server.py +85 -47
- avrae_ls-0.7.0/src/avrae_ls/source_context.py +30 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/symbols.py +27 -60
- avrae_ls-0.7.0/src/avrae_ls/type_inference.py +470 -0
- avrae_ls-0.7.0/src/avrae_ls/type_system.py +729 -0
- avrae_ls-0.6.3/src/avrae_ls/completions.py +0 -1695
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/.gitignore +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/LICENSE +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/__init__.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/api.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/argparser.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/argument_parsing.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/codes.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/cvars.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/dice.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/runtime.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/avrae_ls/signature_help.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/LICENSE +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/__init__.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/exceptions.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/helpers.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/interpreter.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/string.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/types.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/utils.py +0 -0
- {avrae_ls-0.6.3 → avrae_ls-0.7.0}/src/draconic/versions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avrae-ls
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Language server for Avrae draconic aliases
|
|
5
5
|
Author: 1drturtle
|
|
6
6
|
License: MIT License
|
|
@@ -50,6 +50,7 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
50
50
|
|
|
51
51
|
- Install from VSIX: download `avrae-ls-client.vsix` from the GitHub releases page, then in VS Code run “Extensions: Install from VSIX” and select the file.
|
|
52
52
|
- Open your alias workspace; commands like `Avrae: Show Alias Preview` and `Avrae: Run Alias` will be available.
|
|
53
|
+
- Files ending with `.alias-module` are treated as full-file draconic modules under the `avrae-module` language id (no `<drac2>` tags; mock run/preview commands stay tied to `.alias` files).
|
|
53
54
|
|
|
54
55
|
## Developing locally
|
|
55
56
|
|
|
@@ -96,6 +97,10 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
96
97
|
`name` is a label for reporting, `vars` are merged into cvars/uvars/svars/gvars, and `character` keys are deep-merged into the mock character.
|
|
97
98
|
- Run them with `avrae-ls --run-tests [path]` (defaults to the current directory); non-zero exit codes indicate failures.
|
|
98
99
|
|
|
100
|
+
## Config variable substitution
|
|
101
|
+
|
|
102
|
+
- `.avraels.json` values support environment variable substitution with `$NAME` or `${NAME}`. `workspaceRoot` and `workspaceFolder` are injected automatically. Missing variables are replaced with an empty string and logged as warnings.
|
|
103
|
+
|
|
99
104
|
## Runtime differences (mock vs. live Avrae)
|
|
100
105
|
|
|
101
106
|
- Mock execution never writes back to Avrae: cvar/uvar/gvar mutations only live for the current run and reset before the next.
|
|
@@ -10,6 +10,7 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
10
10
|
|
|
11
11
|
- Install from VSIX: download `avrae-ls-client.vsix` from the GitHub releases page, then in VS Code run “Extensions: Install from VSIX” and select the file.
|
|
12
12
|
- Open your alias workspace; commands like `Avrae: Show Alias Preview` and `Avrae: Run Alias` will be available.
|
|
13
|
+
- Files ending with `.alias-module` are treated as full-file draconic modules under the `avrae-module` language id (no `<drac2>` tags; mock run/preview commands stay tied to `.alias` files).
|
|
13
14
|
|
|
14
15
|
## Developing locally
|
|
15
16
|
|
|
@@ -56,6 +57,10 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
56
57
|
`name` is a label for reporting, `vars` are merged into cvars/uvars/svars/gvars, and `character` keys are deep-merged into the mock character.
|
|
57
58
|
- Run them with `avrae-ls --run-tests [path]` (defaults to the current directory); non-zero exit codes indicate failures.
|
|
58
59
|
|
|
60
|
+
## Config variable substitution
|
|
61
|
+
|
|
62
|
+
- `.avraels.json` values support environment variable substitution with `$NAME` or `${NAME}`. `workspaceRoot` and `workspaceFolder` are injected automatically. Missing variables are replaced with an empty string and logged as warnings.
|
|
63
|
+
|
|
59
64
|
## Runtime differences (mock vs. live Avrae)
|
|
60
65
|
|
|
61
66
|
- Mock execution never writes back to Avrae: cvar/uvar/gvar mutations only live for the current run and reset before the next.
|
|
@@ -42,6 +42,13 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
42
42
|
const=".",
|
|
43
43
|
help="Run alias tests in PATH (defaults to current directory)",
|
|
44
44
|
)
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"--silent-gvar-fetch",
|
|
47
|
+
action="store_true",
|
|
48
|
+
help="Silently ignore gvar fetch failures and treat them as None",
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument("--token", help="Avrae API token (overrides config)")
|
|
51
|
+
parser.add_argument("--base-url", help="Avrae API base URL (overrides config)")
|
|
45
52
|
parser.add_argument("--version", action="store_true", help="Print version and exit")
|
|
46
53
|
args = parser.parse_args(argv)
|
|
47
54
|
|
|
@@ -56,12 +63,26 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
56
63
|
parser.error("--run-tests cannot be combined with --tcp")
|
|
57
64
|
if args.analyze:
|
|
58
65
|
parser.error("--run-tests cannot be combined with --analyze")
|
|
59
|
-
sys.exit(
|
|
66
|
+
sys.exit(
|
|
67
|
+
_run_alias_tests(
|
|
68
|
+
Path(args.run_tests),
|
|
69
|
+
token_override=args.token,
|
|
70
|
+
base_url_override=args.base_url,
|
|
71
|
+
silent_gvar_fetch=args.silent_gvar_fetch,
|
|
72
|
+
)
|
|
73
|
+
)
|
|
60
74
|
|
|
61
75
|
if args.analyze:
|
|
62
76
|
if args.tcp:
|
|
63
77
|
parser.error("--analyze cannot be combined with --tcp")
|
|
64
|
-
sys.exit(
|
|
78
|
+
sys.exit(
|
|
79
|
+
_run_analysis(
|
|
80
|
+
Path(args.analyze),
|
|
81
|
+
token_override=args.token,
|
|
82
|
+
base_url_override=args.base_url,
|
|
83
|
+
silent_gvar_fetch=args.silent_gvar_fetch,
|
|
84
|
+
)
|
|
85
|
+
)
|
|
65
86
|
|
|
66
87
|
server = create_server()
|
|
67
88
|
if args.tcp:
|
|
@@ -80,7 +101,13 @@ def _configure_logging(level: str) -> None:
|
|
|
80
101
|
)
|
|
81
102
|
|
|
82
103
|
|
|
83
|
-
def _run_analysis(
|
|
104
|
+
def _run_analysis(
|
|
105
|
+
path: Path,
|
|
106
|
+
*,
|
|
107
|
+
token_override: str | None = None,
|
|
108
|
+
base_url_override: str | None = None,
|
|
109
|
+
silent_gvar_fetch: bool = False,
|
|
110
|
+
) -> int:
|
|
84
111
|
if not path.exists():
|
|
85
112
|
print(f"File not found: {path}", file=sys.stderr)
|
|
86
113
|
return 2
|
|
@@ -90,6 +117,12 @@ def _run_analysis(path: Path) -> int:
|
|
|
90
117
|
log.info("Analyzing %s (workspace root: %s)", path, workspace_root)
|
|
91
118
|
|
|
92
119
|
config, warnings = load_config(workspace_root, default_enable_gvar_fetch=True)
|
|
120
|
+
if token_override:
|
|
121
|
+
config.service.token = token_override
|
|
122
|
+
if base_url_override:
|
|
123
|
+
config.service.base_url = base_url_override
|
|
124
|
+
if silent_gvar_fetch:
|
|
125
|
+
config.silent_gvar_fetch = True
|
|
93
126
|
for warning in warnings:
|
|
94
127
|
log.warning(warning)
|
|
95
128
|
|
|
@@ -104,7 +137,13 @@ def _run_analysis(path: Path) -> int:
|
|
|
104
137
|
return 1 if results else 0
|
|
105
138
|
|
|
106
139
|
|
|
107
|
-
def _run_alias_tests(
|
|
140
|
+
def _run_alias_tests(
|
|
141
|
+
target: Path,
|
|
142
|
+
*,
|
|
143
|
+
token_override: str | None = None,
|
|
144
|
+
base_url_override: str | None = None,
|
|
145
|
+
silent_gvar_fetch: bool = False,
|
|
146
|
+
) -> int:
|
|
108
147
|
if not target.exists():
|
|
109
148
|
print(f"Test path not found: {target}", file=sys.stderr)
|
|
110
149
|
return 2
|
|
@@ -114,6 +153,12 @@ def _run_alias_tests(target: Path) -> int:
|
|
|
114
153
|
log.info("Running alias tests in %s (workspace root: %s)", target, workspace_root)
|
|
115
154
|
|
|
116
155
|
config, warnings = load_config(workspace_root, default_enable_gvar_fetch=True)
|
|
156
|
+
if token_override:
|
|
157
|
+
config.service.token = token_override
|
|
158
|
+
if base_url_override:
|
|
159
|
+
config.service.base_url = base_url_override
|
|
160
|
+
if silent_gvar_fetch:
|
|
161
|
+
config.silent_gvar_fetch = True
|
|
117
162
|
for warning in warnings:
|
|
118
163
|
log.warning(warning)
|
|
119
164
|
|
|
@@ -157,7 +202,11 @@ def _print_test_results(results: Iterable[AliasTestResult], workspace_root: Path
|
|
|
157
202
|
passed += 1
|
|
158
203
|
continue
|
|
159
204
|
if res.error:
|
|
160
|
-
|
|
205
|
+
if res.error_line is not None:
|
|
206
|
+
alias_name = res.case.alias_path.name
|
|
207
|
+
print(f" Error (line {res.error_line} {alias_name}): {res.error}")
|
|
208
|
+
else:
|
|
209
|
+
print(f" Error: {res.error}")
|
|
161
210
|
if res.details:
|
|
162
211
|
print(f" {res.details}")
|
|
163
212
|
expected_val, actual_val = _summarize_mismatch(res.case.expected, res.actual)
|
|
@@ -17,6 +17,7 @@ class RenderedAlias:
|
|
|
17
17
|
stdout: str
|
|
18
18
|
error: Optional[BaseException]
|
|
19
19
|
last_value: Any | None = None
|
|
20
|
+
error_line: int | None = None
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
@dataclass
|
|
@@ -58,7 +59,7 @@ class SimulatedCommand:
|
|
|
58
59
|
embed: EmbedPreview | None = None
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
def
|
|
62
|
+
def _strip_alias_header_with_offset(text: str) -> tuple[str, int]:
|
|
62
63
|
lines = text.splitlines()
|
|
63
64
|
if lines and lines[0].lstrip().startswith("!alias"):
|
|
64
65
|
first = lines[0].lstrip()
|
|
@@ -66,9 +67,32 @@ def _strip_alias_header(text: str) -> str:
|
|
|
66
67
|
remainder = parts[2] if len(parts) > 2 else ""
|
|
67
68
|
body = "\n".join(lines[1:])
|
|
68
69
|
if remainder:
|
|
69
|
-
return remainder + ("\n" + body if body else "")
|
|
70
|
-
return body
|
|
71
|
-
return text
|
|
70
|
+
return remainder + ("\n" + body if body else ""), 0
|
|
71
|
+
return body, 1
|
|
72
|
+
return text, 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _strip_alias_header(text: str) -> str:
|
|
76
|
+
body, _ = _strip_alias_header_with_offset(text)
|
|
77
|
+
return body
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _line_index_for_offset(text: str, offset: int) -> int:
|
|
81
|
+
return text.count("\n", 0, offset)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _error_line_for_match(
|
|
85
|
+
body: str, match: re.Match[str], error: BaseException, line_offset: int
|
|
86
|
+
) -> int | None:
|
|
87
|
+
base_line = _line_index_for_offset(body, match.start(1))
|
|
88
|
+
lineno = getattr(error, "lineno", None)
|
|
89
|
+
if isinstance(lineno, int) and lineno > 0 and getattr(error, "text", None) is not None:
|
|
90
|
+
line_index = base_line + (lineno - 1)
|
|
91
|
+
else:
|
|
92
|
+
code = match.group(1)
|
|
93
|
+
leading_newlines = len(code) - len(code.lstrip("\n"))
|
|
94
|
+
line_index = base_line + leading_newlines
|
|
95
|
+
return line_index + line_offset + 1
|
|
72
96
|
|
|
73
97
|
|
|
74
98
|
async def render_alias_command(
|
|
@@ -79,12 +103,13 @@ async def render_alias_command(
|
|
|
79
103
|
args: list[str] | None = None,
|
|
80
104
|
) -> RenderedAlias:
|
|
81
105
|
"""Replace <drac2> blocks with their evaluated values and return final command."""
|
|
82
|
-
body =
|
|
106
|
+
body, line_offset = _strip_alias_header_with_offset(text)
|
|
83
107
|
body = apply_argument_parsing(body, args)
|
|
84
108
|
stdout_parts: list[str] = []
|
|
85
109
|
parts: list[str] = []
|
|
86
110
|
last_value = None
|
|
87
111
|
error: BaseException | None = None
|
|
112
|
+
error_line: int | None = None
|
|
88
113
|
|
|
89
114
|
pos = 0
|
|
90
115
|
matches: list[tuple[str, re.Match[str]]] = []
|
|
@@ -109,6 +134,7 @@ async def render_alias_command(
|
|
|
109
134
|
stdout_parts.append(result.stdout)
|
|
110
135
|
if result.error:
|
|
111
136
|
error = result.error
|
|
137
|
+
error_line = _error_line_for_match(body, match, result.error, line_offset)
|
|
112
138
|
break
|
|
113
139
|
last_value = result.value
|
|
114
140
|
if result.value is not None:
|
|
@@ -124,7 +150,13 @@ async def render_alias_command(
|
|
|
124
150
|
parts.append(body[pos:])
|
|
125
151
|
|
|
126
152
|
final_command = "".join(parts)
|
|
127
|
-
return RenderedAlias(
|
|
153
|
+
return RenderedAlias(
|
|
154
|
+
command=final_command,
|
|
155
|
+
stdout="".join(stdout_parts),
|
|
156
|
+
error=error,
|
|
157
|
+
last_value=last_value,
|
|
158
|
+
error_line=error_line,
|
|
159
|
+
)
|
|
128
160
|
|
|
129
161
|
|
|
130
162
|
def validate_embed_payload(payload: str) -> Tuple[bool, str | None]:
|
|
@@ -42,6 +42,7 @@ class AliasTestResult:
|
|
|
42
42
|
embed: dict[str, Any] | None = None
|
|
43
43
|
error: str | None = None
|
|
44
44
|
details: str | None = None
|
|
45
|
+
error_line: int | None = None
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def discover_test_files(
|
|
@@ -159,6 +160,7 @@ async def run_alias_test(case: AliasTestCase, builder: ContextBuilder, executor:
|
|
|
159
160
|
actual=None,
|
|
160
161
|
stdout=rendered.stdout,
|
|
161
162
|
error=str(rendered.error),
|
|
163
|
+
error_line=rendered.error_line,
|
|
162
164
|
)
|
|
163
165
|
|
|
164
166
|
preview = simulate_command(rendered.command)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from typing import Iterable, List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def collect_target_names(targets: Iterable[ast.AST]) -> List[str]:
|
|
8
|
+
names: list[str] = []
|
|
9
|
+
for target in targets:
|
|
10
|
+
if isinstance(target, ast.Name):
|
|
11
|
+
names.append(target.id)
|
|
12
|
+
elif isinstance(target, (ast.Tuple, ast.List)):
|
|
13
|
+
names.extend(collect_target_names(target.elts))
|
|
14
|
+
return names
|
|
@@ -10,7 +10,8 @@ from typing import Iterable, List, Sequence
|
|
|
10
10
|
from lsprotocol import types
|
|
11
11
|
|
|
12
12
|
from .codes import MISSING_GVAR_CODE, UNDEFINED_NAME_CODE, UNSUPPORTED_IMPORT_CODE
|
|
13
|
-
from .parser import DraconicBlock
|
|
13
|
+
from .parser import DraconicBlock
|
|
14
|
+
from .source_context import build_source_context
|
|
14
15
|
|
|
15
16
|
log = logging.getLogger(__name__)
|
|
16
17
|
|
|
@@ -42,10 +43,13 @@ def code_actions_for_document(
|
|
|
42
43
|
source: str,
|
|
43
44
|
params: types.CodeActionParams,
|
|
44
45
|
workspace_root: Path,
|
|
46
|
+
*,
|
|
47
|
+
treat_as_module: bool = False,
|
|
45
48
|
) -> List[types.CodeAction]:
|
|
46
49
|
"""Collect code actions for a document without requiring a running server."""
|
|
47
50
|
actions: list[types.CodeAction] = []
|
|
48
|
-
|
|
51
|
+
source_ctx = build_source_context(source, treat_as_module, apply_args=False)
|
|
52
|
+
blocks = source_ctx.blocks
|
|
49
53
|
snippets = _load_snippets(workspace_root)
|
|
50
54
|
only_kinds = list(params.context.only or [])
|
|
51
55
|
|