avrae-ls 0.3.1__py3-none-any.whl → 0.4.0__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/api.py +229 -229
- avrae_ls/argparser.py +16 -3
- avrae_ls/completions.py +4 -0
- avrae_ls/config.py +61 -2
- avrae_ls/context.py +62 -1
- avrae_ls/diagnostics.py +207 -2
- avrae_ls/parser.py +7 -2
- avrae_ls/runtime.py +94 -15
- avrae_ls/server.py +38 -1
- avrae_ls/symbols.py +149 -33
- avrae_ls-0.4.0.dist-info/METADATA +86 -0
- {avrae_ls-0.3.1.dist-info → avrae_ls-0.4.0.dist-info}/RECORD +16 -16
- avrae_ls-0.3.1.dist-info/METADATA +0 -47
- {avrae_ls-0.3.1.dist-info → avrae_ls-0.4.0.dist-info}/WHEEL +0 -0
- {avrae_ls-0.3.1.dist-info → avrae_ls-0.4.0.dist-info}/entry_points.txt +0 -0
- {avrae_ls-0.3.1.dist-info → avrae_ls-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {avrae_ls-0.3.1.dist-info → avrae_ls-0.4.0.dist-info}/top_level.txt +0 -0
avrae_ls/server.py
CHANGED
|
@@ -18,7 +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 .symbols import build_symbol_table, document_symbols, find_definition_range
|
|
21
|
+
from .symbols import build_symbol_table, document_symbols, find_definition_range, find_references, range_for_word
|
|
22
22
|
from .argument_parsing import apply_argument_parsing
|
|
23
23
|
|
|
24
24
|
__version__ = "0.1.0"
|
|
@@ -142,6 +142,43 @@ def on_definition(server: AvraeLanguageServer, params: types.DefinitionParams):
|
|
|
142
142
|
return types.Location(uri=params.text_document.uri, range=rng)
|
|
143
143
|
|
|
144
144
|
|
|
145
|
+
@ls.feature(types.TEXT_DOCUMENT_REFERENCES)
|
|
146
|
+
def on_references(server: AvraeLanguageServer, params: types.ReferenceParams):
|
|
147
|
+
doc = server.workspace.get_text_document(params.text_document.uri)
|
|
148
|
+
table = build_symbol_table(doc.source)
|
|
149
|
+
word = doc.word_at_position(params.position)
|
|
150
|
+
if not word or not table.lookup(word):
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
ranges = find_references(table, doc.source, word, include_declaration=params.context.include_declaration)
|
|
154
|
+
return [types.Location(uri=params.text_document.uri, range=rng) for rng in ranges]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@ls.feature(types.TEXT_DOCUMENT_PREPARE_RENAME)
|
|
158
|
+
def on_prepare_rename(server: AvraeLanguageServer, params: types.PrepareRenameParams):
|
|
159
|
+
doc = server.workspace.get_text_document(params.text_document.uri)
|
|
160
|
+
table = build_symbol_table(doc.source)
|
|
161
|
+
word = doc.word_at_position(params.position)
|
|
162
|
+
if not word or not table.lookup(word):
|
|
163
|
+
return None
|
|
164
|
+
return range_for_word(doc.source, params.position)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@ls.feature(types.TEXT_DOCUMENT_RENAME)
|
|
168
|
+
def on_rename(server: AvraeLanguageServer, params: types.RenameParams):
|
|
169
|
+
doc = server.workspace.get_text_document(params.text_document.uri)
|
|
170
|
+
table = build_symbol_table(doc.source)
|
|
171
|
+
word = doc.word_at_position(params.position)
|
|
172
|
+
if not word or not table.lookup(word) or not params.new_name:
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
ranges = find_references(table, doc.source, word, include_declaration=True)
|
|
176
|
+
if not ranges:
|
|
177
|
+
return None
|
|
178
|
+
edits = [types.TextEdit(range=rng, new_text=params.new_name) for rng in ranges]
|
|
179
|
+
return types.WorkspaceEdit(changes={params.text_document.uri: edits})
|
|
180
|
+
|
|
181
|
+
|
|
145
182
|
@ls.feature(types.WORKSPACE_SYMBOL)
|
|
146
183
|
def on_workspace_symbol(server: AvraeLanguageServer, params: types.WorkspaceSymbolParams):
|
|
147
184
|
symbols: list[types.SymbolInformation] = []
|
avrae_ls/symbols.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import ast
|
|
4
4
|
import logging
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from typing import Dict, List, Optional
|
|
6
|
+
from typing import Dict, Iterable, List, Optional
|
|
7
7
|
|
|
8
8
|
import draconic
|
|
9
9
|
from lsprotocol import types
|
|
@@ -40,10 +40,10 @@ def build_symbol_table(source: str) -> SymbolTable:
|
|
|
40
40
|
parsed_source = apply_argument_parsing(source)
|
|
41
41
|
blocks = find_draconic_blocks(parsed_source)
|
|
42
42
|
if not blocks:
|
|
43
|
-
entries.extend(_symbols_from_code(parsed_source, 0))
|
|
43
|
+
entries.extend(_symbols_from_code(parsed_source, 0, 0))
|
|
44
44
|
else:
|
|
45
45
|
for block in blocks:
|
|
46
|
-
entries.extend(_symbols_from_code(block.code, block.line_offset))
|
|
46
|
+
entries.extend(_symbols_from_code(block.code, block.line_offset, block.char_offset))
|
|
47
47
|
return SymbolTable(entries)
|
|
48
48
|
|
|
49
49
|
|
|
@@ -67,31 +67,72 @@ def find_definition_range(table: SymbolTable, name: str) -> types.Range | None:
|
|
|
67
67
|
return None
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
def find_references(
|
|
71
|
+
table: SymbolTable, source: str, name: str, include_declaration: bool = True
|
|
72
|
+
) -> List[types.Range]:
|
|
73
|
+
parsed_source = apply_argument_parsing(source)
|
|
74
|
+
ranges: list[types.Range] = []
|
|
75
|
+
entry = table.lookup(name)
|
|
76
|
+
include_stores = include_declaration and entry is None
|
|
77
|
+
if include_declaration:
|
|
78
|
+
if entry:
|
|
79
|
+
ranges.append(entry.selection_range)
|
|
80
|
+
|
|
81
|
+
blocks = find_draconic_blocks(parsed_source)
|
|
82
|
+
if not blocks:
|
|
83
|
+
ranges.extend(_references_from_code(parsed_source, name, 0, 0, include_stores))
|
|
84
|
+
else:
|
|
85
|
+
for block in blocks:
|
|
86
|
+
ranges.extend(
|
|
87
|
+
_references_from_code(block.code, name, block.line_offset, block.char_offset, include_stores)
|
|
88
|
+
)
|
|
89
|
+
return _dedupe_ranges(ranges)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def range_for_word(source: str, position: types.Position) -> types.Range | None:
|
|
93
|
+
lines = source.splitlines()
|
|
94
|
+
if position.line >= len(lines):
|
|
95
|
+
return None
|
|
96
|
+
line = lines[position.line]
|
|
97
|
+
if position.character > len(line):
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def _is_ident(ch: str) -> bool:
|
|
101
|
+
return ch.isalnum() or ch == "_"
|
|
102
|
+
|
|
103
|
+
start_idx = position.character
|
|
104
|
+
while start_idx > 0 and _is_ident(line[start_idx - 1]):
|
|
105
|
+
start_idx -= 1
|
|
106
|
+
|
|
107
|
+
end_idx = position.character
|
|
108
|
+
while end_idx < len(line) and _is_ident(line[end_idx]):
|
|
109
|
+
end_idx += 1
|
|
110
|
+
|
|
111
|
+
if start_idx == end_idx:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
return types.Range(
|
|
115
|
+
start=types.Position(line=position.line, character=start_idx),
|
|
116
|
+
end=types.Position(line=position.line, character=end_idx),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _symbols_from_code(code: str, line_offset: int, char_offset: int) -> List[SymbolEntry]:
|
|
121
|
+
body, offset_adjust = _parse_draconic(code)
|
|
122
|
+
if not body:
|
|
84
123
|
return []
|
|
85
124
|
|
|
125
|
+
local_offset = line_offset + offset_adjust
|
|
126
|
+
|
|
86
127
|
entries: list[SymbolEntry] = []
|
|
87
128
|
for node in body:
|
|
88
|
-
entry = _entry_from_node(node, local_offset)
|
|
129
|
+
entry = _entry_from_node(node, local_offset, char_offset)
|
|
89
130
|
if entry:
|
|
90
131
|
entries.append(entry)
|
|
91
132
|
return entries
|
|
92
133
|
|
|
93
134
|
|
|
94
|
-
def _entry_from_node(node: ast.AST, line_offset: int = 0) -> SymbolEntry | None:
|
|
135
|
+
def _entry_from_node(node: ast.AST, line_offset: int = 0, char_offset: int = 0) -> SymbolEntry | None:
|
|
95
136
|
if isinstance(node, ast.FunctionDef):
|
|
96
137
|
kind = types.SymbolKind.Function
|
|
97
138
|
name = node.name
|
|
@@ -103,6 +144,7 @@ def _entry_from_node(node: ast.AST, line_offset: int = 0) -> SymbolEntry | None:
|
|
|
103
144
|
if isinstance(target, ast.Name):
|
|
104
145
|
kind = types.SymbolKind.Variable
|
|
105
146
|
name = target.id
|
|
147
|
+
node = target
|
|
106
148
|
else:
|
|
107
149
|
return None
|
|
108
150
|
else:
|
|
@@ -110,37 +152,99 @@ def _entry_from_node(node: ast.AST, line_offset: int = 0) -> SymbolEntry | None:
|
|
|
110
152
|
|
|
111
153
|
rng = _range_from_positions(
|
|
112
154
|
getattr(node, "lineno", 1),
|
|
113
|
-
getattr(node, "col_offset", 0)
|
|
155
|
+
getattr(node, "col_offset", 0),
|
|
114
156
|
getattr(node, "end_lineno", None),
|
|
115
157
|
getattr(node, "end_col_offset", None),
|
|
116
158
|
)
|
|
117
|
-
rng = _shift_range(rng, line_offset)
|
|
159
|
+
rng = _shift_range(rng, line_offset, char_offset)
|
|
118
160
|
return SymbolEntry(name=name, kind=kind, range=rng, selection_range=rng)
|
|
119
161
|
|
|
120
162
|
|
|
163
|
+
class _ReferenceCollector(ast.NodeVisitor):
|
|
164
|
+
def __init__(self, target: str, include_stores: bool):
|
|
165
|
+
super().__init__()
|
|
166
|
+
self._target = target
|
|
167
|
+
self._include_stores = include_stores
|
|
168
|
+
self.ranges: list[types.Range] = []
|
|
169
|
+
|
|
170
|
+
def visit_Name(self, node: ast.Name): # type: ignore[override]
|
|
171
|
+
if node.id == self._target:
|
|
172
|
+
if isinstance(node.ctx, ast.Store) and not self._include_stores:
|
|
173
|
+
return
|
|
174
|
+
rng = _range_from_positions(
|
|
175
|
+
getattr(node, "lineno", 1),
|
|
176
|
+
getattr(node, "col_offset", 0),
|
|
177
|
+
getattr(node, "end_lineno", None),
|
|
178
|
+
getattr(node, "end_col_offset", None),
|
|
179
|
+
)
|
|
180
|
+
self.ranges.append(rng)
|
|
181
|
+
self.generic_visit(node)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _references_from_code(
|
|
185
|
+
code: str,
|
|
186
|
+
name: str,
|
|
187
|
+
line_offset: int,
|
|
188
|
+
char_offset: int,
|
|
189
|
+
include_stores: bool,
|
|
190
|
+
) -> List[types.Range]:
|
|
191
|
+
body, offset_adjust = _parse_draconic(code)
|
|
192
|
+
if not body:
|
|
193
|
+
return []
|
|
194
|
+
|
|
195
|
+
collector = _ReferenceCollector(name, include_stores)
|
|
196
|
+
for node in body:
|
|
197
|
+
collector.visit(node)
|
|
198
|
+
|
|
199
|
+
local_offset = line_offset + offset_adjust
|
|
200
|
+
return [_shift_range(rng, local_offset, char_offset) for rng in collector.ranges]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _parse_draconic(code: str) -> tuple[list[ast.AST], int]:
|
|
204
|
+
parser = draconic.DraconicInterpreter()
|
|
205
|
+
try:
|
|
206
|
+
return parser.parse(code), 0
|
|
207
|
+
except draconic.DraconicSyntaxError:
|
|
208
|
+
wrapped, added = _wrap_draconic(code)
|
|
209
|
+
try:
|
|
210
|
+
return parser.parse(wrapped), -added
|
|
211
|
+
except draconic.DraconicSyntaxError:
|
|
212
|
+
return [], 0
|
|
213
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
214
|
+
log.debug("Symbol extraction failed: %s", exc)
|
|
215
|
+
return [], 0
|
|
216
|
+
|
|
217
|
+
|
|
121
218
|
def _range_from_positions(
|
|
122
219
|
lineno: int | None,
|
|
123
220
|
col_offset: int | None,
|
|
124
221
|
end_lineno: int | None,
|
|
125
222
|
end_col_offset: int | None,
|
|
126
223
|
) -> types.Range:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
224
|
+
start_line = max((lineno or 1) - 1, 0)
|
|
225
|
+
start_char = max(col_offset or 0, 0)
|
|
226
|
+
end_line = max(((end_lineno or lineno or 1) - 1), 0)
|
|
227
|
+
raw_end_char = end_col_offset if end_col_offset is not None else col_offset
|
|
228
|
+
end_char = max(raw_end_char or start_char, start_char)
|
|
229
|
+
if end_char <= start_char:
|
|
230
|
+
end_char = start_char + 1
|
|
231
|
+
return types.Range(
|
|
232
|
+
start=types.Position(line=start_line, character=start_char),
|
|
233
|
+
end=types.Position(line=end_line, character=end_char),
|
|
134
234
|
)
|
|
135
|
-
return types.Range(start=start, end=end)
|
|
136
235
|
|
|
137
236
|
|
|
138
|
-
def _shift_range(rng: types.Range, line_offset: int) -> types.Range:
|
|
237
|
+
def _shift_range(rng: types.Range, line_offset: int, char_offset: int = 0) -> types.Range:
|
|
238
|
+
start_char = rng.start.character + (char_offset if rng.start.line == 0 else 0)
|
|
239
|
+
end_char = rng.end.character + (char_offset if rng.end.line == 0 else 0)
|
|
139
240
|
if line_offset == 0:
|
|
140
|
-
return
|
|
241
|
+
return types.Range(
|
|
242
|
+
start=types.Position(line=rng.start.line, character=start_char),
|
|
243
|
+
end=types.Position(line=rng.end.line, character=end_char),
|
|
244
|
+
)
|
|
141
245
|
return types.Range(
|
|
142
|
-
start=types.Position(line=max(rng.start.line + line_offset, 0), character=
|
|
143
|
-
end=types.Position(line=max(rng.end.line + line_offset, 0), character=
|
|
246
|
+
start=types.Position(line=max(rng.start.line + line_offset, 0), character=max(start_char, 0)),
|
|
247
|
+
end=types.Position(line=max(rng.end.line + line_offset, 0), character=max(end_char, 0)),
|
|
144
248
|
)
|
|
145
249
|
|
|
146
250
|
|
|
@@ -148,3 +252,15 @@ def _wrap_draconic(code: str) -> tuple[str, int]:
|
|
|
148
252
|
indented = "\n".join(f" {line}" for line in code.splitlines())
|
|
149
253
|
wrapped = f"def __alias_main__():\n{indented}\n__alias_main__()"
|
|
150
254
|
return wrapped, 1
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _dedupe_ranges(ranges: Iterable[types.Range]) -> List[types.Range]:
|
|
258
|
+
seen = set()
|
|
259
|
+
unique: list[types.Range] = []
|
|
260
|
+
for rng in ranges:
|
|
261
|
+
key = (rng.start.line, rng.start.character, rng.end.line, rng.end.character)
|
|
262
|
+
if key in seen:
|
|
263
|
+
continue
|
|
264
|
+
seen.add(key)
|
|
265
|
+
unique.append(rng)
|
|
266
|
+
return unique
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: avrae-ls
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Language server for Avrae draconic aliases
|
|
5
|
+
Author: 1drturtle
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pygls>=1.3.1
|
|
10
|
+
Requires-Dist: lsprotocol>=2023.0.1
|
|
11
|
+
Requires-Dist: httpx>=0.27
|
|
12
|
+
Requires-Dist: d20>=1.1.2
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
16
|
+
Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Avrae Draconic Alias Language Server
|
|
21
|
+
|
|
22
|
+
Language Server Protocol (LSP) implementation targeting Avrae-style draconic aliases. It provides syntax/semantic diagnostics, a mocked execution command, and a thin configuration layer driven by a workspace `.avraels.json` file. Credit to Avrae team for all code yoinked!
|
|
23
|
+
|
|
24
|
+
## Install (released package)
|
|
25
|
+
|
|
26
|
+
- CLI/server via `uv tool` (preferred): `uv tool install avrae-ls` then `avrae-ls --help` to see stdio/TCP options (same as `python -m avrae_ls`). The VS Code extension uses this invocation by default. The draconic interpreter is vendored, so no Git deps are needed.
|
|
27
|
+
|
|
28
|
+
## VS Code extension (released)
|
|
29
|
+
|
|
30
|
+
- 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.
|
|
31
|
+
- Open your alias workspace; commands like `Avrae: Show Alias Preview` and `Avrae: Run Alias` will be available.
|
|
32
|
+
|
|
33
|
+
## Developing locally
|
|
34
|
+
|
|
35
|
+
- Prereqs: [uv](https://github.com/astral-sh/uv) and Node.js.
|
|
36
|
+
- Install deps: `uv sync --all-extras` then `make vscode-deps`.
|
|
37
|
+
- Build everything locally: `make package` (wheel + VSIX in `dist/`).
|
|
38
|
+
- Run tests/lint: `make check`.
|
|
39
|
+
- Run via uv tool from source: `uv tool install --from . avrae-ls`.
|
|
40
|
+
- Run diagnostics for a single file (stdout + stderr logs): `avrae-ls --analyze path/to/alias.txt --log-level DEBUG`.
|
|
41
|
+
|
|
42
|
+
## How to test
|
|
43
|
+
|
|
44
|
+
- Quick check (ruff + pytest): `make check` (uses `uv run ruff` and `uv run pytest` under the hood).
|
|
45
|
+
- Lint only: `make lint` or `uv run ruff check src tests`.
|
|
46
|
+
- Tests only (with coverage): `make test` or `uv run pytest tests --cov=src`.
|
|
47
|
+
- CLI smoke test without installing: `uv run python -m avrae_ls --analyze path/to/alias.txt`.
|
|
48
|
+
|
|
49
|
+
## Runtime differences (mock vs. live Avrae)
|
|
50
|
+
|
|
51
|
+
- Mock execution never writes back to Avrae: cvar/uvar/gvar mutations only live for the current run and reset before the next.
|
|
52
|
+
- Network is limited to gvar fetches (when `enableGvarFetch` is true) and `verify_signature`; other Avrae/Discord calls are replaced with mocked context data from `.avraels.json`.
|
|
53
|
+
- `get_gvar`/`using` values are pulled from local var files first; remote fetches go to `https://api.avrae.io/customizations/gvars/<id>` (or your `avraeService.baseUrl`) using `avraeService.token` and are cached for the session.
|
|
54
|
+
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`, respects `verifySignatureTimeout`/`verifySignatureRetries`, reuses the last successful response per signature, and includes `avraeService.token` if present.
|
|
55
|
+
|
|
56
|
+
## Troubleshooting gvar fetch / verify_signature
|
|
57
|
+
|
|
58
|
+
- `get_gvar` returns `None` or `using(...)` raises `ModuleNotFoundError`: ensure the workspace `.avraels.json` sets `enableGvarFetch: true`, includes a valid `avraeService.token`, or seed the gvar in a var file referenced by `varFiles`.
|
|
59
|
+
- HTTP 401/403/404 from fetch/verify calls: check the token (401/403) and the gvar/signature id (404). Override `avraeService.baseUrl` if you mirror the API.
|
|
60
|
+
- Slow or flaky calls: tune `verifySignatureTimeout` / `verifySignatureRetries`, or disable remote fetches by flipping `enableGvarFetch` off to rely purely on local vars.
|
|
61
|
+
|
|
62
|
+
## Other editors (stdio)
|
|
63
|
+
|
|
64
|
+
- Any client can launch the server with stdio: `avrae-ls --stdio` (flag accepted for client compatibility) or `python -m avrae_ls`. The server will also auto-discover `.avraels.json` in parent folders.
|
|
65
|
+
- Neovim (nvim-lspconfig example):
|
|
66
|
+
```lua
|
|
67
|
+
require("lspconfig").avraels.setup({
|
|
68
|
+
cmd = { "avrae-ls", "--stdio" },
|
|
69
|
+
filetypes = { "avrae" },
|
|
70
|
+
root_dir = require("lspconfig.util").root_pattern(".avraels.json", ".git"),
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
- Emacs (lsp-mode snippet):
|
|
74
|
+
```elisp
|
|
75
|
+
(lsp-register-client
|
|
76
|
+
(make-lsp-client
|
|
77
|
+
:new-connection (lsp-stdio-connection '("avrae-ls" "--stdio"))
|
|
78
|
+
:major-modes '(fundamental-mode) ;; bind to your Avrae alias mode
|
|
79
|
+
:server-id 'avrae-ls))
|
|
80
|
+
```
|
|
81
|
+
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
82
|
+
|
|
83
|
+
## Releasing (maintainers)
|
|
84
|
+
|
|
85
|
+
1. Bump `pyproject.toml` / `package.json`
|
|
86
|
+
2. Create Github release
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
avrae_ls/__init__.py,sha256=BmjrnksGkbG7TPqwbyQvgYj9uei8pFSFpfkRpaGVdJU,63
|
|
2
2
|
avrae_ls/__main__.py,sha256=ch287lWe11go5xHAE9OkVppixt0vRF401E4zTs2tqQ0,3557
|
|
3
3
|
avrae_ls/alias_preview.py,sha256=Wy_ZNRq73ojPiLFf6P6fJ7rG7DeFFsFLkLlhFrAjkKI,6117
|
|
4
|
-
avrae_ls/api.py,sha256=
|
|
5
|
-
avrae_ls/argparser.py,sha256
|
|
4
|
+
avrae_ls/api.py,sha256=_AHvIEIlz34YeWdZDpXd1EwgdQUGk8-nqNIEN4qdNR8,65093
|
|
5
|
+
avrae_ls/argparser.py,sha256=DRptXGyK4f0r7NsuV1Sg4apG-qihIClOX408jgk4HH4,13762
|
|
6
6
|
avrae_ls/argument_parsing.py,sha256=ezKl65VwuNEDxt6KlYwVQcpy1110UDvf4BqZqgZTcqk,2122
|
|
7
|
-
avrae_ls/completions.py,sha256=
|
|
8
|
-
avrae_ls/config.py,sha256=
|
|
9
|
-
avrae_ls/context.py,sha256=
|
|
7
|
+
avrae_ls/completions.py,sha256=ht0J7JQX5QS8cz85itfozLBP9GPr1BjmNyttFEmJN-0,34514
|
|
8
|
+
avrae_ls/config.py,sha256=ULEfWbzIO05cllHBLUeGY_CHLpX6tHyCJYIEQgKSoXc,17383
|
|
9
|
+
avrae_ls/context.py,sha256=KcCKqzqJCG2xlEtou5HWz1OWISApGl8IfwOvVnNi9nc,8464
|
|
10
10
|
avrae_ls/cvars.py,sha256=0tcVbUHx_CKJ6aou3kEsKX37LRWAjkUWlqqIuSRFlXk,3197
|
|
11
|
-
avrae_ls/diagnostics.py,sha256=
|
|
11
|
+
avrae_ls/diagnostics.py,sha256=2wn9Gu5vDGWt32zap9rhduv4q4B9c_X3baI2_yDHGYg,23074
|
|
12
12
|
avrae_ls/dice.py,sha256=DY7V7L-EwAXaCgddgVe9xU1s9lVtiw5Zc2reipNgdTk,874
|
|
13
|
-
avrae_ls/parser.py,sha256=
|
|
14
|
-
avrae_ls/runtime.py,sha256=
|
|
15
|
-
avrae_ls/server.py,sha256=
|
|
13
|
+
avrae_ls/parser.py,sha256=UQDwjupuJVU6KvoF4D8r3azPT7ksP_nTZsm5FUhGnWE,1395
|
|
14
|
+
avrae_ls/runtime.py,sha256=PDnjZXcU5DqM9jKSc4S0jHObBE7SWKte17PWWXGQ34M,23356
|
|
15
|
+
avrae_ls/server.py,sha256=jKypzveP2iRZjEdK0xJxfcn6QfbjrDe03XnXNhsga9Q,14709
|
|
16
16
|
avrae_ls/signature_help.py,sha256=JheaEzINV4FO72t5U0AJfL2ZX15y3-gcA6xk3M1jHcY,6980
|
|
17
|
-
avrae_ls/symbols.py,sha256=
|
|
18
|
-
avrae_ls-0.
|
|
17
|
+
avrae_ls/symbols.py,sha256=Lm5LH-F60JYNuAtSjRhHeyHEWAWxpnNb0w6KR0u1Zac,8422
|
|
18
|
+
avrae_ls-0.4.0.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
|
|
19
19
|
draconic/LICENSE,sha256=Fzvu32_DafLKKn2mzxhEdlmrKZzAsigDZ87O7uoVqZI,1067
|
|
20
20
|
draconic/__init__.py,sha256=YPH420Pcn_nTkfB62hJy_YqC5kpJdzSa78jP8n4z_xY,109
|
|
21
21
|
draconic/exceptions.py,sha256=siahnHIsumbaUhKBDSrw_DmLZ-0oZks8L5oytPH8hD4,3753
|
|
@@ -25,8 +25,8 @@ draconic/string.py,sha256=kGrRc6wNHRq1y5xw8Os-fBhfINDtIY2nBWQWkyLSfQI,2858
|
|
|
25
25
|
draconic/types.py,sha256=1Lsr6z8bW5agglGI4hLt_nPtYuZOIf_ueSpPDB4WDrs,13686
|
|
26
26
|
draconic/utils.py,sha256=D4vJ-txqS2-rlqsEpXAC46_j1sZX4UjY-9zIgElo96k,3122
|
|
27
27
|
draconic/versions.py,sha256=CUEsgUWjAmjez0432WwiBwZlIzWPIObwZUf8Yld18EE,84
|
|
28
|
-
avrae_ls-0.
|
|
29
|
-
avrae_ls-0.
|
|
30
|
-
avrae_ls-0.
|
|
31
|
-
avrae_ls-0.
|
|
32
|
-
avrae_ls-0.
|
|
28
|
+
avrae_ls-0.4.0.dist-info/METADATA,sha256=z-bp7Y2zrilkbNmLujQYnHOW6rRlAyPMdQpJBDUB4cU,4713
|
|
29
|
+
avrae_ls-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
avrae_ls-0.4.0.dist-info/entry_points.txt,sha256=OtYXipMQzqmxpMoApgo0MeJYFmMbkbFN51Ibhpb8hF4,52
|
|
31
|
+
avrae_ls-0.4.0.dist-info/top_level.txt,sha256=TL68uzGHmB2R2ID32_s2zocmcNnpMJVQ6_4NBvyo8a4,18
|
|
32
|
+
avrae_ls-0.4.0.dist-info/RECORD,,
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: avrae-ls
|
|
3
|
-
Version: 0.3.1
|
|
4
|
-
Summary: Language server for Avrae draconic aliases
|
|
5
|
-
Author: 1drturtle
|
|
6
|
-
Requires-Python: >=3.11
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Requires-Dist: pygls>=1.3.1
|
|
10
|
-
Requires-Dist: lsprotocol>=2023.0.1
|
|
11
|
-
Requires-Dist: httpx>=0.27
|
|
12
|
-
Requires-Dist: d20>=1.1.2
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
16
|
-
Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
|
|
17
|
-
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
18
|
-
Dynamic: license-file
|
|
19
|
-
|
|
20
|
-
# Avrae Draconic Alias Language Server
|
|
21
|
-
|
|
22
|
-
Language Server Protocol (LSP) implementation targeting Avrae-style draconic aliases. It provides syntax/semantic diagnostics, a mocked execution command, and a thin configuration layer driven by a workspace `.avraels.json` file. Credit to Avrae team for all code yoinked!
|
|
23
|
-
|
|
24
|
-
## Install (released package)
|
|
25
|
-
|
|
26
|
-
- CLI/server via `uv tool` (preferred): `uv tool install avrae-ls` then `avrae-ls --help` to see stdio/TCP options (same as `python -m avrae_ls`). The VS Code extension uses this invocation by default. The draconic interpreter is vendored, so no Git deps are needed.
|
|
27
|
-
|
|
28
|
-
## VS Code extension (released)
|
|
29
|
-
|
|
30
|
-
- 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.
|
|
31
|
-
- Open your alias workspace; commands like `Avrae: Show Alias Preview` and `Avrae: Run Alias` will be available.
|
|
32
|
-
|
|
33
|
-
## Developing locally
|
|
34
|
-
|
|
35
|
-
- Prereqs: [uv](https://github.com/astral-sh/uv) and Node.js.
|
|
36
|
-
- Install deps: `uv sync --all-extras` then `make vscode-deps`.
|
|
37
|
-
- Build everything locally: `make package` (wheel + VSIX in `dist/`).
|
|
38
|
-
- Run tests/lint: `make check`.
|
|
39
|
-
- Run via uv tool from source: `uv tool install --from . avrae-ls`.
|
|
40
|
-
- Run diagnostics for a single file (stdout + stderr logs): `avrae-ls --analyze path/to/alias.txt --log-level DEBUG`.
|
|
41
|
-
|
|
42
|
-
## Releasing (maintainers)
|
|
43
|
-
|
|
44
|
-
1. Bump `pyproject.toml` version.
|
|
45
|
-
2. `make release` (clean, build, upload to PyPI).
|
|
46
|
-
3. Build and attach the VSIX to the GitHub release (`make vsix`).
|
|
47
|
-
4. Tag and push.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|