avrae-ls 0.2.0__py3-none-any.whl → 0.3.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/__main__.py CHANGED
@@ -1,7 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import argparse
4
+ import asyncio
5
+ import logging
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Iterable
4
9
 
10
+ from lsprotocol import types
11
+
12
+ from .config import CONFIG_FILENAME, load_config
13
+ from .context import ContextBuilder
14
+ from .diagnostics import DiagnosticProvider
15
+ from .runtime import MockExecutor
5
16
  from .server import create_server
6
17
 
7
18
 
@@ -11,8 +22,17 @@ def main(argv: list[str] | None = None) -> None:
11
22
  parser.add_argument("--host", default="127.0.0.1", help="TCP host (when --tcp is set)")
12
23
  parser.add_argument("--port", type=int, default=2087, help="TCP port (when --tcp is set)")
13
24
  parser.add_argument("--stdio", action="store_true", help="Accept stdio flag for VS Code clients (ignored)")
25
+ parser.add_argument("--log-level", default="WARNING", help="Logging level (DEBUG, INFO, WARNING, ERROR)")
26
+ parser.add_argument("--analyze", metavar="FILE", help="Run diagnostics for a file and print them to stdout")
14
27
  args = parser.parse_args(argv)
15
28
 
29
+ _configure_logging(args.log_level)
30
+
31
+ if args.analyze:
32
+ if args.tcp:
33
+ parser.error("--analyze cannot be combined with --tcp")
34
+ sys.exit(_run_analysis(Path(args.analyze)))
35
+
16
36
  server = create_server()
17
37
  if args.tcp:
18
38
  server.start_tcp(args.host, args.port)
@@ -20,5 +40,69 @@ def main(argv: list[str] | None = None) -> None:
20
40
  server.start_io()
21
41
 
22
42
 
43
+ def _configure_logging(level: str) -> None:
44
+ numeric = getattr(logging, level.upper(), logging.WARNING)
45
+ if not isinstance(numeric, int):
46
+ numeric = logging.WARNING
47
+ logging.basicConfig(
48
+ level=numeric,
49
+ format="%(levelname)s %(name)s: %(message)s",
50
+ )
51
+
52
+
53
+ def _run_analysis(path: Path) -> int:
54
+ if not path.exists():
55
+ print(f"File not found: {path}", file=sys.stderr)
56
+ return 2
57
+
58
+ workspace_root = _discover_workspace_root(path)
59
+ log = logging.getLogger(__name__)
60
+ log.info("Analyzing %s (workspace root: %s)", path, workspace_root)
61
+
62
+ config, warnings = load_config(workspace_root)
63
+ for warning in warnings:
64
+ log.warning(warning)
65
+
66
+ builder = ContextBuilder(config)
67
+ ctx_data = builder.build()
68
+ executor = MockExecutor(config.service)
69
+ diagnostics = DiagnosticProvider(executor, config.diagnostics)
70
+
71
+ source = path.read_text()
72
+ results = asyncio.run(diagnostics.analyze(source, ctx_data, builder.gvar_resolver))
73
+ _print_diagnostics(path, results)
74
+ return 1 if results else 0
75
+
76
+
77
+ def _discover_workspace_root(target: Path) -> Path:
78
+ current = target if target.is_dir() else target.parent
79
+ for folder in [current, *current.parents]:
80
+ if (folder / CONFIG_FILENAME).exists():
81
+ return folder
82
+ return current
83
+
84
+
85
+ def _print_diagnostics(path: Path, diagnostics: Iterable[types.Diagnostic]) -> None:
86
+ diags = list(diagnostics)
87
+ if not diags:
88
+ print(f"{path}: no issues found")
89
+ return
90
+
91
+ for diag in diags:
92
+ start = diag.range.start
93
+ severity = _severity_label(diag.severity)
94
+ source = diag.source or "avrae-ls"
95
+ print(f"{path}:{start.line + 1}:{start.character + 1}: {severity} [{source}] {diag.message}")
96
+
97
+
98
+ def _severity_label(severity: types.DiagnosticSeverity | None) -> str:
99
+ if severity is None:
100
+ return "info"
101
+ try:
102
+ return types.DiagnosticSeverity(severity).name.lower()
103
+ except Exception:
104
+ return str(severity).lower()
105
+
106
+
23
107
  if __name__ == "__main__":
24
108
  main()