avrae-ls 0.5.1__tar.gz → 0.6.1__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.5.1 → avrae_ls-0.6.1}/PKG-INFO +34 -4
- {avrae_ls-0.5.1 → avrae_ls-0.6.1}/README.md +32 -3
- {avrae_ls-0.5.1 → avrae_ls-0.6.1}/pyproject.toml +4 -1
- avrae_ls-0.6.1/src/avrae_ls/__init__.py +3 -0
- avrae_ls-0.6.1/src/avrae_ls/__main__.py +210 -0
- avrae_ls-0.6.1/src/avrae_ls/alias_preview.py +371 -0
- avrae_ls-0.6.1/src/avrae_ls/alias_tests.py +311 -0
- avrae_ls-0.6.1/src/avrae_ls/api.py +2015 -0
- avrae_ls-0.6.1/src/avrae_ls/argparser.py +430 -0
- avrae_ls-0.6.1/src/avrae_ls/argument_parsing.py +67 -0
- avrae_ls-0.6.1/src/avrae_ls/code_actions.py +282 -0
- avrae_ls-0.6.1/src/avrae_ls/codes.py +3 -0
- avrae_ls-0.6.1/src/avrae_ls/completions.py +1695 -0
- avrae_ls-0.6.1/src/avrae_ls/config.py +474 -0
- avrae_ls-0.6.1/src/avrae_ls/context.py +337 -0
- avrae_ls-0.6.1/src/avrae_ls/cvars.py +115 -0
- avrae_ls-0.6.1/src/avrae_ls/diagnostics.py +826 -0
- avrae_ls-0.6.1/src/avrae_ls/dice.py +33 -0
- avrae_ls-0.6.1/src/avrae_ls/parser.py +68 -0
- avrae_ls-0.6.1/src/avrae_ls/runtime.py +750 -0
- avrae_ls-0.6.1/src/avrae_ls/server.py +447 -0
- avrae_ls-0.6.1/src/avrae_ls/signature_help.py +248 -0
- avrae_ls-0.6.1/src/avrae_ls/symbols.py +274 -0
- avrae_ls-0.6.1/src/draconic/__init__.py +4 -0
- avrae_ls-0.6.1/src/draconic/exceptions.py +157 -0
- avrae_ls-0.6.1/src/draconic/helpers.py +236 -0
- avrae_ls-0.6.1/src/draconic/interpreter.py +1091 -0
- avrae_ls-0.6.1/src/draconic/string.py +100 -0
- avrae_ls-0.6.1/src/draconic/types.py +364 -0
- avrae_ls-0.6.1/src/draconic/utils.py +78 -0
- avrae_ls-0.6.1/src/draconic/versions.py +4 -0
- {avrae_ls-0.5.1 → avrae_ls-0.6.1}/.gitignore +0 -0
- {avrae_ls-0.5.1 → avrae_ls-0.6.1}/LICENSE +0 -0
- {avrae_ls-0.5.1 → avrae_ls-0.6.1}/src/draconic/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avrae-ls
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Language server for Avrae draconic aliases
|
|
5
5
|
Author: 1drturtle
|
|
6
6
|
License: MIT License
|
|
@@ -30,6 +30,7 @@ Requires-Dist: d20>=1.1.2
|
|
|
30
30
|
Requires-Dist: httpx>=0.27
|
|
31
31
|
Requires-Dist: lsprotocol>=2023.0.1
|
|
32
32
|
Requires-Dist: pygls>=1.3.1
|
|
33
|
+
Requires-Dist: pyyaml>=6.0
|
|
33
34
|
Provides-Extra: dev
|
|
34
35
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
35
36
|
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
@@ -66,18 +67,47 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
66
67
|
- Tests only (with coverage): `make test` or `uv run pytest tests --cov=src`.
|
|
67
68
|
- CLI smoke test without installing: `uv run python -m avrae_ls --analyze path/to/alias.txt`.
|
|
68
69
|
|
|
70
|
+
## Alias tests
|
|
71
|
+
|
|
72
|
+
- Create files ending with `.alias-test` (or `.aliastest`) next to your alias file. Each test starts with an invocation, followed by `---` and the expected result; you can stack multiple tests in one file by repeating this pattern (optional metadata after a second `---` per test).
|
|
73
|
+
```
|
|
74
|
+
!my-alias -b example args
|
|
75
|
+
---
|
|
76
|
+
expected text or number
|
|
77
|
+
```
|
|
78
|
+
- For embed aliases, put a YAML/JSON dictionary after the separator to compare against the embed preview (partial dictionaries are allowed).
|
|
79
|
+
```
|
|
80
|
+
!embedtest
|
|
81
|
+
---
|
|
82
|
+
title: Hello
|
|
83
|
+
description: World
|
|
84
|
+
```
|
|
85
|
+
- Embed fields lists can be partial: only the listed fields (in order) are matched; extra fields in the alias do not fail the test.
|
|
86
|
+
- Use regex expectations by wrapping strings in `/.../` (or `re:...`). You can also mix literals with regex segments (e.g., `Hello /world.*/`) so only the delimited part is treated as regex.
|
|
87
|
+
- Optional second `---` section can carry metadata:
|
|
88
|
+
```
|
|
89
|
+
name: critical-hit
|
|
90
|
+
vars:
|
|
91
|
+
cvars:
|
|
92
|
+
hp: 12
|
|
93
|
+
character:
|
|
94
|
+
name: Tester
|
|
95
|
+
```
|
|
96
|
+
`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
|
+
- Run them with `avrae-ls --run-tests [path]` (defaults to the current directory); non-zero exit codes indicate failures.
|
|
98
|
+
|
|
69
99
|
## Runtime differences (mock vs. live Avrae)
|
|
70
100
|
|
|
71
101
|
- Mock execution never writes back to Avrae: cvar/uvar/gvar mutations only live for the current run and reset before the next.
|
|
72
102
|
- 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`.
|
|
73
103
|
- `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.
|
|
74
|
-
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`,
|
|
104
|
+
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`, reuses the last successful response per signature, and includes `avraeService.token` if present.
|
|
75
105
|
|
|
76
106
|
## Troubleshooting gvar fetch / verify_signature
|
|
77
107
|
|
|
78
108
|
- `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`.
|
|
79
109
|
- 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.
|
|
80
|
-
- Slow or flaky calls:
|
|
110
|
+
- Slow or flaky calls: disable remote fetches by flipping `enableGvarFetch` off to rely purely on local vars.
|
|
81
111
|
|
|
82
112
|
## Other editors (stdio)
|
|
83
113
|
|
|
@@ -98,7 +128,7 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
98
128
|
:major-modes '(fundamental-mode) ;; bind to your Avrae alias mode
|
|
99
129
|
:server-id 'avrae-ls))
|
|
100
130
|
```
|
|
101
|
-
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
131
|
+
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, `Avrae: Refresh GVARs`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
102
132
|
|
|
103
133
|
## Releasing (maintainers)
|
|
104
134
|
|
|
@@ -27,18 +27,47 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
27
27
|
- Tests only (with coverage): `make test` or `uv run pytest tests --cov=src`.
|
|
28
28
|
- CLI smoke test without installing: `uv run python -m avrae_ls --analyze path/to/alias.txt`.
|
|
29
29
|
|
|
30
|
+
## Alias tests
|
|
31
|
+
|
|
32
|
+
- Create files ending with `.alias-test` (or `.aliastest`) next to your alias file. Each test starts with an invocation, followed by `---` and the expected result; you can stack multiple tests in one file by repeating this pattern (optional metadata after a second `---` per test).
|
|
33
|
+
```
|
|
34
|
+
!my-alias -b example args
|
|
35
|
+
---
|
|
36
|
+
expected text or number
|
|
37
|
+
```
|
|
38
|
+
- For embed aliases, put a YAML/JSON dictionary after the separator to compare against the embed preview (partial dictionaries are allowed).
|
|
39
|
+
```
|
|
40
|
+
!embedtest
|
|
41
|
+
---
|
|
42
|
+
title: Hello
|
|
43
|
+
description: World
|
|
44
|
+
```
|
|
45
|
+
- Embed fields lists can be partial: only the listed fields (in order) are matched; extra fields in the alias do not fail the test.
|
|
46
|
+
- Use regex expectations by wrapping strings in `/.../` (or `re:...`). You can also mix literals with regex segments (e.g., `Hello /world.*/`) so only the delimited part is treated as regex.
|
|
47
|
+
- Optional second `---` section can carry metadata:
|
|
48
|
+
```
|
|
49
|
+
name: critical-hit
|
|
50
|
+
vars:
|
|
51
|
+
cvars:
|
|
52
|
+
hp: 12
|
|
53
|
+
character:
|
|
54
|
+
name: Tester
|
|
55
|
+
```
|
|
56
|
+
`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
|
+
- Run them with `avrae-ls --run-tests [path]` (defaults to the current directory); non-zero exit codes indicate failures.
|
|
58
|
+
|
|
30
59
|
## Runtime differences (mock vs. live Avrae)
|
|
31
60
|
|
|
32
61
|
- Mock execution never writes back to Avrae: cvar/uvar/gvar mutations only live for the current run and reset before the next.
|
|
33
62
|
- 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`.
|
|
34
63
|
- `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.
|
|
35
|
-
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`,
|
|
64
|
+
- `signature()` returns a mock string (`mock-signature:<int>`). `verify_signature()` POSTs to `/bot/signature/verify`, reuses the last successful response per signature, and includes `avraeService.token` if present.
|
|
36
65
|
|
|
37
66
|
## Troubleshooting gvar fetch / verify_signature
|
|
38
67
|
|
|
39
68
|
- `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`.
|
|
40
69
|
- 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.
|
|
41
|
-
- Slow or flaky calls:
|
|
70
|
+
- Slow or flaky calls: disable remote fetches by flipping `enableGvarFetch` off to rely purely on local vars.
|
|
42
71
|
|
|
43
72
|
## Other editors (stdio)
|
|
44
73
|
|
|
@@ -59,7 +88,7 @@ Language Server Protocol (LSP) implementation targeting Avrae-style draconic ali
|
|
|
59
88
|
:major-modes '(fundamental-mode) ;; bind to your Avrae alias mode
|
|
60
89
|
:server-id 'avrae-ls))
|
|
61
90
|
```
|
|
62
|
-
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
91
|
+
- VS Code commands to mirror: `Avrae: Run Alias (Mock)`, `Avrae: Show Alias Preview`, `Avrae: Refresh GVARs`, and `Avrae: Reload Workspace Config` run against the same server binary.
|
|
63
92
|
|
|
64
93
|
## Releasing (maintainers)
|
|
65
94
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "avrae-ls"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.1"
|
|
8
8
|
description = "Language server for Avrae draconic aliases"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "1drturtle" }
|
|
@@ -17,6 +17,7 @@ dependencies = [
|
|
|
17
17
|
"lsprotocol>=2023.0.1",
|
|
18
18
|
"httpx>=0.27",
|
|
19
19
|
"d20>=1.1.2",
|
|
20
|
+
"pyyaml>=6.0",
|
|
20
21
|
]
|
|
21
22
|
|
|
22
23
|
[project.optional-dependencies]
|
|
@@ -35,6 +36,8 @@ packages = ["src/avrae_ls", "src/draconic"]
|
|
|
35
36
|
|
|
36
37
|
[tool.hatch.build]
|
|
37
38
|
include = [
|
|
39
|
+
"src/avrae_ls",
|
|
40
|
+
"src/draconic",
|
|
38
41
|
"src/draconic/LICENSE",
|
|
39
42
|
]
|
|
40
43
|
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Iterable
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
from lsprotocol import types
|
|
13
|
+
|
|
14
|
+
from .alias_tests import AliasTestError, AliasTestResult, discover_test_files, parse_alias_tests, run_alias_tests
|
|
15
|
+
from .config import CONFIG_FILENAME, load_config
|
|
16
|
+
from .context import ContextBuilder
|
|
17
|
+
from .diagnostics import DiagnosticProvider
|
|
18
|
+
from .runtime import MockExecutor
|
|
19
|
+
from .server import create_server, __version__
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv: list[str] | None = None) -> None:
|
|
23
|
+
parser = argparse.ArgumentParser(description="Avrae draconic alias language server")
|
|
24
|
+
parser.add_argument("--tcp", action="store_true", help="Run in TCP mode instead of stdio")
|
|
25
|
+
parser.add_argument("--host", default="127.0.0.1", help="TCP host (when --tcp is set)")
|
|
26
|
+
parser.add_argument("--port", type=int, default=2087, help="TCP port (when --tcp is set)")
|
|
27
|
+
parser.add_argument("--stdio", action="store_true", help="Accept stdio flag for VS Code clients (ignored)")
|
|
28
|
+
parser.add_argument("--log-level", default="WARNING", help="Logging level (DEBUG, INFO, WARNING, ERROR)")
|
|
29
|
+
parser.add_argument("--analyze", metavar="FILE", help="Run diagnostics for a file and print them to stdout")
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--run-tests",
|
|
32
|
+
metavar="PATH",
|
|
33
|
+
nargs="?",
|
|
34
|
+
const=".",
|
|
35
|
+
help="Run alias tests in PATH (defaults to current directory)",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument("--version", action="store_true", help="Print version and exit")
|
|
38
|
+
args = parser.parse_args(argv)
|
|
39
|
+
|
|
40
|
+
_configure_logging(args.log_level)
|
|
41
|
+
|
|
42
|
+
if args.version:
|
|
43
|
+
print(__version__)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
if args.run_tests is not None:
|
|
47
|
+
if args.tcp:
|
|
48
|
+
parser.error("--run-tests cannot be combined with --tcp")
|
|
49
|
+
if args.analyze:
|
|
50
|
+
parser.error("--run-tests cannot be combined with --analyze")
|
|
51
|
+
sys.exit(_run_alias_tests(Path(args.run_tests)))
|
|
52
|
+
|
|
53
|
+
if args.analyze:
|
|
54
|
+
if args.tcp:
|
|
55
|
+
parser.error("--analyze cannot be combined with --tcp")
|
|
56
|
+
sys.exit(_run_analysis(Path(args.analyze)))
|
|
57
|
+
|
|
58
|
+
server = create_server()
|
|
59
|
+
if args.tcp:
|
|
60
|
+
server.start_tcp(args.host, args.port)
|
|
61
|
+
else:
|
|
62
|
+
server.start_io()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _configure_logging(level: str) -> None:
|
|
66
|
+
numeric = getattr(logging, level.upper(), logging.WARNING)
|
|
67
|
+
if not isinstance(numeric, int):
|
|
68
|
+
numeric = logging.WARNING
|
|
69
|
+
logging.basicConfig(
|
|
70
|
+
level=numeric,
|
|
71
|
+
format="%(levelname)s %(name)s: %(message)s",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _run_analysis(path: Path) -> int:
|
|
76
|
+
if not path.exists():
|
|
77
|
+
print(f"File not found: {path}", file=sys.stderr)
|
|
78
|
+
return 2
|
|
79
|
+
|
|
80
|
+
workspace_root = _discover_workspace_root(path)
|
|
81
|
+
log = logging.getLogger(__name__)
|
|
82
|
+
log.info("Analyzing %s (workspace root: %s)", path, workspace_root)
|
|
83
|
+
|
|
84
|
+
config, warnings = load_config(workspace_root)
|
|
85
|
+
for warning in warnings:
|
|
86
|
+
log.warning(warning)
|
|
87
|
+
|
|
88
|
+
builder = ContextBuilder(config)
|
|
89
|
+
ctx_data = builder.build()
|
|
90
|
+
executor = MockExecutor(config.service)
|
|
91
|
+
diagnostics = DiagnosticProvider(executor, config.diagnostics)
|
|
92
|
+
|
|
93
|
+
source = path.read_text()
|
|
94
|
+
results = asyncio.run(diagnostics.analyze(source, ctx_data, builder.gvar_resolver))
|
|
95
|
+
_print_diagnostics(path, results)
|
|
96
|
+
return 1 if results else 0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _run_alias_tests(target: Path) -> int:
|
|
100
|
+
if not target.exists():
|
|
101
|
+
print(f"Test path not found: {target}", file=sys.stderr)
|
|
102
|
+
return 2
|
|
103
|
+
|
|
104
|
+
workspace_root = _discover_workspace_root(target)
|
|
105
|
+
log = logging.getLogger(__name__)
|
|
106
|
+
log.info("Running alias tests in %s (workspace root: %s)", target, workspace_root)
|
|
107
|
+
|
|
108
|
+
config, warnings = load_config(workspace_root)
|
|
109
|
+
for warning in warnings:
|
|
110
|
+
log.warning(warning)
|
|
111
|
+
|
|
112
|
+
builder = ContextBuilder(config)
|
|
113
|
+
executor = MockExecutor(config.service)
|
|
114
|
+
|
|
115
|
+
test_files = discover_test_files(target)
|
|
116
|
+
cases = []
|
|
117
|
+
parse_errors: list[str] = []
|
|
118
|
+
for test_file in test_files:
|
|
119
|
+
try:
|
|
120
|
+
cases.extend(parse_alias_tests(test_file))
|
|
121
|
+
except AliasTestError as exc:
|
|
122
|
+
parse_errors.append(str(exc))
|
|
123
|
+
|
|
124
|
+
if parse_errors:
|
|
125
|
+
for err in parse_errors:
|
|
126
|
+
print(err, file=sys.stderr)
|
|
127
|
+
if not cases:
|
|
128
|
+
print(f"No alias tests found under {target}")
|
|
129
|
+
return 1 if parse_errors else 0
|
|
130
|
+
|
|
131
|
+
results = asyncio.run(run_alias_tests(cases, builder, executor))
|
|
132
|
+
_print_test_results(results, workspace_root)
|
|
133
|
+
|
|
134
|
+
failures = [res for res in results if not res.passed]
|
|
135
|
+
return 1 if failures or parse_errors else 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _print_test_results(results: Iterable[AliasTestResult], workspace_root: Path) -> None:
|
|
139
|
+
total = len(results)
|
|
140
|
+
passed = 0
|
|
141
|
+
for res in results:
|
|
142
|
+
rel = _relative_to_workspace(res.case.path, workspace_root)
|
|
143
|
+
label = f"{rel} ({res.case.name})" if res.case.name else rel
|
|
144
|
+
status = "PASS" if res.passed else "FAIL"
|
|
145
|
+
print(f"[{status}] {label} (alias: {res.case.alias_name})")
|
|
146
|
+
if res.passed:
|
|
147
|
+
if res.stdout:
|
|
148
|
+
print(f" Stdout: {res.stdout.strip()}")
|
|
149
|
+
passed += 1
|
|
150
|
+
continue
|
|
151
|
+
if res.error:
|
|
152
|
+
print(f" Error: {res.error}")
|
|
153
|
+
if res.details:
|
|
154
|
+
print(f" {res.details}")
|
|
155
|
+
expected = _format_value(res.case.expected)
|
|
156
|
+
actual = _format_value(res.actual)
|
|
157
|
+
print(f" Expected: {expected}")
|
|
158
|
+
print(f" Actual: {actual}")
|
|
159
|
+
if res.stdout:
|
|
160
|
+
print(f" Stdout: {res.stdout.strip()}")
|
|
161
|
+
print(f"{passed}/{total} tests passed")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _relative_to_workspace(path: Path, workspace_root: Path) -> str:
|
|
165
|
+
try:
|
|
166
|
+
return str(path.relative_to(workspace_root))
|
|
167
|
+
except ValueError:
|
|
168
|
+
return str(path)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _format_value(value) -> str:
|
|
172
|
+
if value is None:
|
|
173
|
+
return "None"
|
|
174
|
+
if isinstance(value, (dict, list)):
|
|
175
|
+
return (yaml.safe_dump(value, sort_keys=False) or "").strip()
|
|
176
|
+
return str(value)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _discover_workspace_root(target: Path) -> Path:
|
|
180
|
+
current = target if target.is_dir() else target.parent
|
|
181
|
+
for folder in [current, *current.parents]:
|
|
182
|
+
if (folder / CONFIG_FILENAME).exists():
|
|
183
|
+
return folder
|
|
184
|
+
return current
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _print_diagnostics(path: Path, diagnostics: Iterable[types.Diagnostic]) -> None:
|
|
188
|
+
diags = list(diagnostics)
|
|
189
|
+
if not diags:
|
|
190
|
+
print(f"{path}: no issues found")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
for diag in diags:
|
|
194
|
+
start = diag.range.start
|
|
195
|
+
severity = _severity_label(diag.severity)
|
|
196
|
+
source = diag.source or "avrae-ls"
|
|
197
|
+
print(f"{path}:{start.line + 1}:{start.character + 1}: {severity} [{source}] {diag.message}")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _severity_label(severity: types.DiagnosticSeverity | None) -> str:
|
|
201
|
+
if severity is None:
|
|
202
|
+
return "info"
|
|
203
|
+
try:
|
|
204
|
+
return types.DiagnosticSeverity(severity).name.lower()
|
|
205
|
+
except Exception:
|
|
206
|
+
return str(severity).lower()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
main()
|