python-delphi-lsp 2.3.0__py3-none-any.whl → 2.3.2__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.
- delphi_lsp/_version.py +1 -1
- delphi_lsp/agent_cache.py +144 -14
- delphi_lsp/agent_cli.py +128 -14
- delphi_lsp/agent_context.py +4 -2
- delphi_lsp/agent_templates.py +1 -1
- delphi_lsp/agent_workspace.py +23 -2
- delphi_lsp/project_discovery.py +94 -5
- {python_delphi_lsp-2.3.0.dist-info → python_delphi_lsp-2.3.2.dist-info}/METADATA +38 -9
- {python_delphi_lsp-2.3.0.dist-info → python_delphi_lsp-2.3.2.dist-info}/RECORD +13 -13
- {python_delphi_lsp-2.3.0.dist-info → python_delphi_lsp-2.3.2.dist-info}/WHEEL +1 -1
- {python_delphi_lsp-2.3.0.dist-info → python_delphi_lsp-2.3.2.dist-info}/entry_points.txt +0 -0
- {python_delphi_lsp-2.3.0.dist-info → python_delphi_lsp-2.3.2.dist-info}/licenses/LICENSE +0 -0
- {python_delphi_lsp-2.3.0.dist-info → python_delphi_lsp-2.3.2.dist-info}/top_level.txt +0 -0
delphi_lsp/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.3.
|
|
1
|
+
__version__ = "2.3.2"
|
delphi_lsp/agent_cache.py
CHANGED
|
@@ -45,6 +45,88 @@ _WATCHED_SUFFIXES = frozenset(
|
|
|
45
45
|
)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def current_process_rss_bytes() -> int:
|
|
49
|
+
"""Return the current resident set size, or zero when unavailable."""
|
|
50
|
+
try:
|
|
51
|
+
if sys.platform == "darwin":
|
|
52
|
+
return _darwin_process_rss_bytes()
|
|
53
|
+
if sys.platform.startswith("linux"):
|
|
54
|
+
statm = Path("/proc/self/statm").read_text(encoding="ascii").split()
|
|
55
|
+
return int(statm[1]) * int(os.sysconf("SC_PAGE_SIZE"))
|
|
56
|
+
if os.name == "nt":
|
|
57
|
+
return _windows_process_rss_bytes()
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _darwin_process_rss_bytes() -> int:
|
|
64
|
+
import ctypes
|
|
65
|
+
|
|
66
|
+
class TimeValue(ctypes.Structure):
|
|
67
|
+
_fields_ = [("seconds", ctypes.c_int), ("microseconds", ctypes.c_int)]
|
|
68
|
+
|
|
69
|
+
class MachTaskBasicInfo(ctypes.Structure):
|
|
70
|
+
_fields_ = [
|
|
71
|
+
("virtual_size", ctypes.c_uint64),
|
|
72
|
+
("resident_size", ctypes.c_uint64),
|
|
73
|
+
("resident_size_max", ctypes.c_uint64),
|
|
74
|
+
("user_time", TimeValue),
|
|
75
|
+
("system_time", TimeValue),
|
|
76
|
+
("policy", ctypes.c_int),
|
|
77
|
+
("suspend_count", ctypes.c_int),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
library = ctypes.CDLL(None)
|
|
81
|
+
library.mach_task_self.restype = ctypes.c_uint
|
|
82
|
+
library.task_info.argtypes = (
|
|
83
|
+
ctypes.c_uint,
|
|
84
|
+
ctypes.c_int,
|
|
85
|
+
ctypes.c_void_p,
|
|
86
|
+
ctypes.POINTER(ctypes.c_uint),
|
|
87
|
+
)
|
|
88
|
+
library.task_info.restype = ctypes.c_int
|
|
89
|
+
info = MachTaskBasicInfo()
|
|
90
|
+
count = ctypes.c_uint(ctypes.sizeof(info) // ctypes.sizeof(ctypes.c_int))
|
|
91
|
+
result = library.task_info(
|
|
92
|
+
library.mach_task_self(),
|
|
93
|
+
20,
|
|
94
|
+
ctypes.byref(info),
|
|
95
|
+
ctypes.byref(count),
|
|
96
|
+
)
|
|
97
|
+
return int(info.resident_size) if result == 0 else 0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _windows_process_rss_bytes() -> int:
|
|
101
|
+
import ctypes
|
|
102
|
+
from ctypes import wintypes
|
|
103
|
+
|
|
104
|
+
class ProcessMemoryCounters(ctypes.Structure):
|
|
105
|
+
_fields_ = [
|
|
106
|
+
("cb", wintypes.DWORD),
|
|
107
|
+
("PageFaultCount", wintypes.DWORD),
|
|
108
|
+
("PeakWorkingSetSize", ctypes.c_size_t),
|
|
109
|
+
("WorkingSetSize", ctypes.c_size_t),
|
|
110
|
+
("QuotaPeakPagedPoolUsage", ctypes.c_size_t),
|
|
111
|
+
("QuotaPagedPoolUsage", ctypes.c_size_t),
|
|
112
|
+
("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t),
|
|
113
|
+
("QuotaNonPagedPoolUsage", ctypes.c_size_t),
|
|
114
|
+
("PagefileUsage", ctypes.c_size_t),
|
|
115
|
+
("PeakPagefileUsage", ctypes.c_size_t),
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
counters = ProcessMemoryCounters()
|
|
119
|
+
counters.cb = ctypes.sizeof(counters)
|
|
120
|
+
process = ctypes.windll.kernel32.GetCurrentProcess()
|
|
121
|
+
if not ctypes.windll.psapi.GetProcessMemoryInfo(
|
|
122
|
+
process,
|
|
123
|
+
ctypes.byref(counters),
|
|
124
|
+
counters.cb,
|
|
125
|
+
):
|
|
126
|
+
return 0
|
|
127
|
+
return int(counters.WorkingSetSize)
|
|
128
|
+
|
|
129
|
+
|
|
48
130
|
def estimate_deep_size(value: object) -> int:
|
|
49
131
|
"""Estimate the memory retained by an owned object graph.
|
|
50
132
|
|
|
@@ -292,6 +374,11 @@ def _safe_navigation_cache_path(root: str | Path, *, create: bool = False) -> Pa
|
|
|
292
374
|
return result
|
|
293
375
|
|
|
294
376
|
|
|
377
|
+
def navigation_cache_path(root: str | Path, *, create: bool = False) -> Path:
|
|
378
|
+
"""Return the validated persistent navigation-cache directory."""
|
|
379
|
+
return _safe_navigation_cache_path(root, create=create)
|
|
380
|
+
|
|
381
|
+
|
|
295
382
|
def _metadata_mapping(metadata: CacheMetadata) -> dict[str, object]:
|
|
296
383
|
return {field.name: getattr(metadata, field.name) for field in fields(metadata)}
|
|
297
384
|
|
|
@@ -552,16 +639,32 @@ def _read_startup_tail(diagnostics: object, *, max_bytes: int = _STARTUP_DIAGNOS
|
|
|
552
639
|
return b""
|
|
553
640
|
|
|
554
641
|
|
|
642
|
+
def _daemon_process_options() -> dict[str, object]:
|
|
643
|
+
options: dict[str, object] = {
|
|
644
|
+
"stdin": subprocess.DEVNULL,
|
|
645
|
+
"stdout": subprocess.DEVNULL,
|
|
646
|
+
}
|
|
647
|
+
if os.name == "nt":
|
|
648
|
+
options["creationflags"] = (
|
|
649
|
+
subprocess.CREATE_NO_WINDOW
|
|
650
|
+
| subprocess.CREATE_NEW_PROCESS_GROUP
|
|
651
|
+
)
|
|
652
|
+
else:
|
|
653
|
+
options["start_new_session"] = True
|
|
654
|
+
return options
|
|
655
|
+
|
|
656
|
+
|
|
555
657
|
class _CacheService:
|
|
556
658
|
def __init__(self, metadata: CacheMetadata) -> None:
|
|
557
659
|
self.metadata = metadata
|
|
660
|
+
self._rss_baseline_bytes = current_process_rss_bytes()
|
|
558
661
|
self.context = AgentContext.open(
|
|
559
662
|
metadata.root,
|
|
560
663
|
metadata.project_file or None,
|
|
561
664
|
workers=metadata.workers,
|
|
562
665
|
worker_memory_budget_bytes=metadata.max_memory_bytes,
|
|
563
666
|
revision_check_interval_seconds=_CACHE_REVISION_CHECK_INTERVAL_SECONDS,
|
|
564
|
-
navigation_cache_dir=
|
|
667
|
+
navigation_cache_dir=navigation_cache_path(
|
|
565
668
|
metadata.root,
|
|
566
669
|
create=True,
|
|
567
670
|
),
|
|
@@ -577,6 +680,15 @@ class _CacheService:
|
|
|
577
680
|
self.prewarm_seconds = 0.0
|
|
578
681
|
self.shutdown = threading.Event()
|
|
579
682
|
|
|
683
|
+
def _measure_retained_bytes(self) -> int:
|
|
684
|
+
process_rss = current_process_rss_bytes()
|
|
685
|
+
process_growth = (
|
|
686
|
+
max(0, process_rss - self._rss_baseline_bytes)
|
|
687
|
+
if process_rss > 0 and self._rss_baseline_bytes > 0
|
|
688
|
+
else 0
|
|
689
|
+
)
|
|
690
|
+
return max(self.context.estimated_cache_bytes, process_growth)
|
|
691
|
+
|
|
580
692
|
def prewarm(self) -> None:
|
|
581
693
|
started = time.monotonic()
|
|
582
694
|
try:
|
|
@@ -587,7 +699,7 @@ class _CacheService:
|
|
|
587
699
|
raise
|
|
588
700
|
self.cache_state = "ready"
|
|
589
701
|
self.last_budget = self.budget.enforce(
|
|
590
|
-
measure=
|
|
702
|
+
measure=self._measure_retained_bytes,
|
|
591
703
|
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
592
704
|
evict_navigation=self.context.evict_navigation_caches,
|
|
593
705
|
)
|
|
@@ -628,7 +740,7 @@ class _CacheService:
|
|
|
628
740
|
if before != after:
|
|
629
741
|
self.stats.invalidations += 1
|
|
630
742
|
self.last_budget = self.budget.enforce(
|
|
631
|
-
measure=
|
|
743
|
+
measure=self._measure_retained_bytes,
|
|
632
744
|
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
633
745
|
evict_navigation=self.context.evict_navigation_caches,
|
|
634
746
|
)
|
|
@@ -675,21 +787,34 @@ def _watch_filter(_change: object, path: str) -> bool:
|
|
|
675
787
|
return Path(path).suffix.casefold() in _WATCHED_SUFFIXES
|
|
676
788
|
|
|
677
789
|
|
|
678
|
-
def
|
|
790
|
+
def watch_workspace_changes(
|
|
791
|
+
root: str | Path,
|
|
792
|
+
*,
|
|
793
|
+
stop_event: threading.Event,
|
|
794
|
+
on_change: Callable[[], None],
|
|
795
|
+
) -> None:
|
|
679
796
|
try:
|
|
680
797
|
for changes in watch(
|
|
681
|
-
|
|
798
|
+
root,
|
|
682
799
|
watch_filter=_watch_filter,
|
|
683
|
-
stop_event=
|
|
800
|
+
stop_event=stop_event,
|
|
684
801
|
debounce=50,
|
|
685
802
|
step=20,
|
|
686
803
|
recursive=True,
|
|
687
804
|
raise_interrupt=False,
|
|
688
805
|
):
|
|
689
806
|
if changes:
|
|
690
|
-
|
|
807
|
+
on_change()
|
|
691
808
|
except (OSError, RuntimeError):
|
|
692
|
-
|
|
809
|
+
on_change()
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def _watch_workspace(service: _CacheService) -> None:
|
|
813
|
+
watch_workspace_changes(
|
|
814
|
+
service.metadata.root,
|
|
815
|
+
stop_event=service.shutdown,
|
|
816
|
+
on_change=service.context.invalidate_revision_cache,
|
|
817
|
+
)
|
|
693
818
|
|
|
694
819
|
|
|
695
820
|
def _serve_connection(connection: socket.socket, service: _CacheService) -> None:
|
|
@@ -811,11 +936,7 @@ def _start_cache_unlocked(
|
|
|
811
936
|
]
|
|
812
937
|
if project:
|
|
813
938
|
command.extend(("--project-file", project))
|
|
814
|
-
options
|
|
815
|
-
if os.name == "nt":
|
|
816
|
-
options["creationflags"] = subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
|
|
817
|
-
else:
|
|
818
|
-
options["start_new_session"] = True
|
|
939
|
+
options = _daemon_process_options()
|
|
819
940
|
with tempfile.TemporaryFile() as diagnostics:
|
|
820
941
|
options["stderr"] = diagnostics
|
|
821
942
|
process = subprocess.Popen(command, **options)
|
|
@@ -867,6 +988,8 @@ def start_cache(
|
|
|
867
988
|
) -> CacheMetadata:
|
|
868
989
|
if type(workers) is not int or not 0 <= workers <= 32:
|
|
869
990
|
raise ValueError("workers must be auto or an integer from 1 through 32.")
|
|
991
|
+
if type(idle_timeout) is not int or idle_timeout <= 0:
|
|
992
|
+
raise ValueError("idle_timeout must be greater than zero.")
|
|
870
993
|
if not math.isfinite(startup_timeout) or startup_timeout <= 0:
|
|
871
994
|
raise ValueError("startup_timeout must be greater than zero.")
|
|
872
995
|
with _start_lock(root, startup_timeout):
|
|
@@ -930,7 +1053,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
930
1053
|
serve.add_argument("--project-file", default="")
|
|
931
1054
|
serve.add_argument("--max-memory", type=int, default=DEFAULT_MAX_MEMORY_BYTES)
|
|
932
1055
|
serve.add_argument("--workers", type=int, default=0)
|
|
933
|
-
serve.add_argument("--idle-timeout", type=
|
|
1056
|
+
serve.add_argument("--idle-timeout", type=_positive_integer, default=DEFAULT_IDLE_TIMEOUT)
|
|
934
1057
|
args = parser.parse_args(argv)
|
|
935
1058
|
if args.command == "serve":
|
|
936
1059
|
run_cache_daemon(
|
|
@@ -943,5 +1066,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
943
1066
|
return 0
|
|
944
1067
|
|
|
945
1068
|
|
|
1069
|
+
def _positive_integer(value: str) -> int:
|
|
1070
|
+
parsed = int(value)
|
|
1071
|
+
if parsed <= 0:
|
|
1072
|
+
raise argparse.ArgumentTypeError("value must be greater than zero")
|
|
1073
|
+
return parsed
|
|
1074
|
+
|
|
1075
|
+
|
|
946
1076
|
if __name__ == "__main__":
|
|
947
1077
|
raise SystemExit(main())
|
delphi_lsp/agent_cli.py
CHANGED
|
@@ -5,6 +5,7 @@ import json
|
|
|
5
5
|
import math
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
+
import threading
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import BinaryIO, TextIO
|
|
10
11
|
|
|
@@ -15,15 +16,17 @@ from .agent_cache import (
|
|
|
15
16
|
DEFAULT_MAX_MEMORY_BYTES,
|
|
16
17
|
DEFAULT_STARTUP_TIMEOUT,
|
|
17
18
|
parse_memory_size,
|
|
19
|
+
navigation_cache_path,
|
|
18
20
|
query_cache,
|
|
19
21
|
run_cache_daemon,
|
|
20
22
|
start_cache,
|
|
21
23
|
stop_cache,
|
|
24
|
+
watch_workspace_changes,
|
|
22
25
|
)
|
|
23
26
|
from .agent_layers import build_codebase_index, layer_payload, render_layer
|
|
24
27
|
from .agent_protocol import AgentProtocolError, SUPPORTED_ACTIONS, SUPPORTED_DETAILS, SUPPORTED_RELATIONS
|
|
25
28
|
from .agent_templates import install_opencode_support, install_skill
|
|
26
|
-
from .parallel_outline import parse_worker_setting
|
|
29
|
+
from .parallel_outline import ParallelOutlineError, parse_worker_setting
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
_MAX_WORKER_RECORD_BYTES = 1024 * 1024
|
|
@@ -32,6 +35,14 @@ _INVALID_ENCODING_MESSAGE = "Invalid UTF-8 request."
|
|
|
32
35
|
_REQUEST_TOO_LARGE_MESSAGE = "Request exceeds the 1 MiB limit."
|
|
33
36
|
_INTERNAL_ERROR_MESSAGE = "Internal request error."
|
|
34
37
|
_SOURCE_UNAVAILABLE_MESSAGE = "Selected source is unavailable."
|
|
38
|
+
_WORKER_REVISION_CHECK_INTERVAL_SECONDS = 30.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _CliError(RuntimeError):
|
|
42
|
+
def __init__(self, code: str, message: str) -> None:
|
|
43
|
+
self.code = code
|
|
44
|
+
self.message = message
|
|
45
|
+
super().__init__(message)
|
|
35
46
|
|
|
36
47
|
|
|
37
48
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -120,7 +131,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
120
131
|
cache_serve.add_argument("--project-file", type=Path)
|
|
121
132
|
cache_serve.add_argument("--max-memory", type=parse_memory_size, required=True)
|
|
122
133
|
cache_serve.add_argument("--workers", type=parse_worker_setting, required=True)
|
|
123
|
-
cache_serve.add_argument("--idle-timeout", type=
|
|
134
|
+
cache_serve.add_argument("--idle-timeout", type=_positive_integer, required=True)
|
|
124
135
|
cache_serve.set_defaults(func=_cache_serve)
|
|
125
136
|
|
|
126
137
|
query = subcommands.add_parser("query", help="Send an ergonomic request to a running cache daemon.")
|
|
@@ -131,8 +142,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
131
142
|
query.add_argument("--detail", choices=SUPPORTED_DETAILS, default="summary")
|
|
132
143
|
query.add_argument("--relation", choices=SUPPORTED_RELATIONS)
|
|
133
144
|
query.add_argument("--cursor", default="")
|
|
134
|
-
query.add_argument("--max-items", type=
|
|
135
|
-
query.add_argument("--max-chars", type=
|
|
145
|
+
query.add_argument("--max-items", type=_max_items, default=12)
|
|
146
|
+
query.add_argument("--max-chars", type=_max_chars, default=12000)
|
|
136
147
|
query.set_defaults(func=_query)
|
|
137
148
|
|
|
138
149
|
return parser
|
|
@@ -140,10 +151,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
140
151
|
|
|
141
152
|
def _add_cache_start_arguments(parser: argparse.ArgumentParser) -> None:
|
|
142
153
|
parser.add_argument("--root", type=Path, default=Path("."))
|
|
143
|
-
parser.add_argument(
|
|
154
|
+
parser.add_argument(
|
|
155
|
+
"--project-file",
|
|
156
|
+
type=Path,
|
|
157
|
+
help=(
|
|
158
|
+
"Optionally select a .dproj, .dpr, or .dpk file, relative to "
|
|
159
|
+
"--root if needed."
|
|
160
|
+
),
|
|
161
|
+
)
|
|
144
162
|
parser.add_argument("--max-memory", type=parse_memory_size, default=DEFAULT_MAX_MEMORY_BYTES)
|
|
145
163
|
parser.add_argument("--workers", type=parse_worker_setting, default=0)
|
|
146
|
-
parser.add_argument("--idle-timeout", type=
|
|
164
|
+
parser.add_argument("--idle-timeout", type=_positive_integer, default=DEFAULT_IDLE_TIMEOUT)
|
|
147
165
|
parser.add_argument("--startup-timeout", type=_positive_float, default=DEFAULT_STARTUP_TIMEOUT)
|
|
148
166
|
|
|
149
167
|
|
|
@@ -154,6 +172,27 @@ def _positive_float(value: str) -> float:
|
|
|
154
172
|
return parsed
|
|
155
173
|
|
|
156
174
|
|
|
175
|
+
def _positive_integer(value: str) -> int:
|
|
176
|
+
parsed = int(value)
|
|
177
|
+
if parsed <= 0:
|
|
178
|
+
raise argparse.ArgumentTypeError("value must be greater than zero")
|
|
179
|
+
return parsed
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _max_items(value: str) -> int:
|
|
183
|
+
parsed = int(value)
|
|
184
|
+
if not 1 <= parsed <= 50:
|
|
185
|
+
raise argparse.ArgumentTypeError("value must be between 1 and 50")
|
|
186
|
+
return parsed
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _max_chars(value: str) -> int:
|
|
190
|
+
parsed = int(value)
|
|
191
|
+
if not 256 <= parsed <= 40000:
|
|
192
|
+
raise argparse.ArgumentTypeError("value must be between 256 and 40000")
|
|
193
|
+
return parsed
|
|
194
|
+
|
|
195
|
+
|
|
157
196
|
def main(argv: list[str] | None = None) -> int:
|
|
158
197
|
parser = build_parser()
|
|
159
198
|
args = parser.parse_args(argv)
|
|
@@ -161,9 +200,23 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
161
200
|
result = args.func(args)
|
|
162
201
|
if not getattr(sys.stdout, "closed", False):
|
|
163
202
|
sys.stdout.flush()
|
|
203
|
+
except _CliError as error:
|
|
204
|
+
sys.stderr.write(f"cli_error:{error.code}: {error.message}\n")
|
|
205
|
+
sys.stderr.flush()
|
|
206
|
+
return 1
|
|
207
|
+
except CacheClientError as error:
|
|
208
|
+
return _cache_error(error)
|
|
209
|
+
except ParallelOutlineError as error:
|
|
210
|
+
sys.stderr.write(f"cli_error:parallel_failed: {error}\n")
|
|
211
|
+
sys.stderr.flush()
|
|
212
|
+
return 1
|
|
164
213
|
except BrokenPipeError:
|
|
165
214
|
_discard_broken_stdout()
|
|
166
215
|
os._exit(1)
|
|
216
|
+
except OSError as error:
|
|
217
|
+
sys.stderr.write(f"cli_error:io_error: {error}\n")
|
|
218
|
+
sys.stderr.flush()
|
|
219
|
+
return 1
|
|
167
220
|
return result if isinstance(result, int) else 0
|
|
168
221
|
|
|
169
222
|
|
|
@@ -189,6 +242,7 @@ def _discard_broken_stdout() -> None:
|
|
|
189
242
|
|
|
190
243
|
|
|
191
244
|
def _view(args: argparse.Namespace) -> None:
|
|
245
|
+
args.root = _workspace_root(args.root)
|
|
192
246
|
index = build_codebase_index(
|
|
193
247
|
args.root,
|
|
194
248
|
project_file=args.project_file,
|
|
@@ -199,6 +253,7 @@ def _view(args: argparse.Namespace) -> None:
|
|
|
199
253
|
|
|
200
254
|
|
|
201
255
|
def _index(args: argparse.Namespace) -> None:
|
|
256
|
+
args.root = _workspace_root(args.root)
|
|
202
257
|
index = build_codebase_index(
|
|
203
258
|
args.root,
|
|
204
259
|
project_file=args.project_file,
|
|
@@ -216,33 +271,72 @@ def _index(args: argparse.Namespace) -> None:
|
|
|
216
271
|
|
|
217
272
|
|
|
218
273
|
def _skill_install(args: argparse.Namespace) -> None:
|
|
219
|
-
|
|
274
|
+
try:
|
|
275
|
+
skill_path = install_skill(args.target, force=args.force)
|
|
276
|
+
except FileExistsError as error:
|
|
277
|
+
raise _CliError("install_conflict", str(error)) from None
|
|
220
278
|
print(skill_path)
|
|
221
279
|
|
|
222
280
|
|
|
223
281
|
def _opencode_install(args: argparse.Namespace) -> None:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
282
|
+
try:
|
|
283
|
+
skill_path, plugin_path, agent_path = install_opencode_support(
|
|
284
|
+
args.target,
|
|
285
|
+
python_executable=args.python,
|
|
286
|
+
force=args.force,
|
|
287
|
+
write_config=args.write_config,
|
|
288
|
+
)
|
|
289
|
+
except FileExistsError as error:
|
|
290
|
+
raise _CliError("install_conflict", str(error)) from None
|
|
230
291
|
print(skill_path)
|
|
231
292
|
print(plugin_path)
|
|
232
293
|
print(agent_path)
|
|
233
294
|
|
|
234
295
|
|
|
235
296
|
def _worker(args: argparse.Namespace) -> None:
|
|
236
|
-
|
|
297
|
+
args.root = _workspace_root(args.root)
|
|
298
|
+
context = _open_worker_context(args.root, args.project_file, args.workers)
|
|
299
|
+
watcher_stop = threading.Event()
|
|
300
|
+
watcher = threading.Thread(
|
|
301
|
+
target=watch_workspace_changes,
|
|
302
|
+
kwargs={
|
|
303
|
+
"root": args.root,
|
|
304
|
+
"stop_event": watcher_stop,
|
|
305
|
+
"on_change": context.invalidate_revision_cache,
|
|
306
|
+
},
|
|
307
|
+
name="delphi-worker-watcher",
|
|
308
|
+
daemon=True,
|
|
309
|
+
)
|
|
310
|
+
watcher.start()
|
|
237
311
|
try:
|
|
238
312
|
_serve_worker(context, sys.stdin.buffer, sys.stdout.buffer, sys.stderr)
|
|
239
313
|
finally:
|
|
314
|
+
watcher_stop.set()
|
|
315
|
+
watcher.join(timeout=2.0)
|
|
240
316
|
try:
|
|
241
317
|
sys.stdout.close()
|
|
242
318
|
except (BrokenPipeError, OSError) as error:
|
|
243
319
|
raise BrokenPipeError from error
|
|
244
320
|
|
|
245
321
|
|
|
322
|
+
def _open_worker_context(
|
|
323
|
+
root: Path,
|
|
324
|
+
project_file: Path | None,
|
|
325
|
+
workers: int,
|
|
326
|
+
) -> AgentContext:
|
|
327
|
+
try:
|
|
328
|
+
cache_dir: Path | None = navigation_cache_path(root, create=True)
|
|
329
|
+
except (CacheClientError, OSError):
|
|
330
|
+
cache_dir = None
|
|
331
|
+
return AgentContext.open(
|
|
332
|
+
root,
|
|
333
|
+
project_file,
|
|
334
|
+
workers=workers,
|
|
335
|
+
revision_check_interval_seconds=_WORKER_REVISION_CHECK_INTERVAL_SECONDS,
|
|
336
|
+
navigation_cache_dir=cache_dir,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
246
340
|
def _write_json(payload: object) -> None:
|
|
247
341
|
sys.stdout.write(json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n")
|
|
248
342
|
|
|
@@ -260,6 +354,7 @@ def _cache_error(error: CacheClientError) -> int:
|
|
|
260
354
|
|
|
261
355
|
|
|
262
356
|
def _cache_start(args: argparse.Namespace) -> int:
|
|
357
|
+
args.root = _workspace_root(args.root)
|
|
263
358
|
try:
|
|
264
359
|
start_cache(
|
|
265
360
|
args.root,
|
|
@@ -278,6 +373,7 @@ def _cache_start(args: argparse.Namespace) -> int:
|
|
|
278
373
|
|
|
279
374
|
|
|
280
375
|
def _cache_status(args: argparse.Namespace) -> int:
|
|
376
|
+
args.root = _workspace_root(args.root)
|
|
281
377
|
try:
|
|
282
378
|
response = query_cache(args.root, {"action": "status"})
|
|
283
379
|
except CacheClientError as error:
|
|
@@ -295,6 +391,7 @@ def _cache_status(args: argparse.Namespace) -> int:
|
|
|
295
391
|
|
|
296
392
|
|
|
297
393
|
def _cache_stop(args: argparse.Namespace) -> int:
|
|
394
|
+
args.root = _workspace_root(args.root)
|
|
298
395
|
try:
|
|
299
396
|
response = query_cache(args.root, {"action": "status"})
|
|
300
397
|
except CacheClientError as error:
|
|
@@ -316,6 +413,7 @@ def _cache_stop(args: argparse.Namespace) -> int:
|
|
|
316
413
|
|
|
317
414
|
|
|
318
415
|
def _cache_serve(args: argparse.Namespace) -> int:
|
|
416
|
+
args.root = _workspace_root(args.root)
|
|
319
417
|
run_cache_daemon(
|
|
320
418
|
args.root,
|
|
321
419
|
project_file=str(args.project_file or ""),
|
|
@@ -327,6 +425,7 @@ def _cache_serve(args: argparse.Namespace) -> int:
|
|
|
327
425
|
|
|
328
426
|
|
|
329
427
|
def _query(args: argparse.Namespace) -> int:
|
|
428
|
+
args.root = _workspace_root(args.root)
|
|
330
429
|
request: dict[str, object] = {"action": args.action}
|
|
331
430
|
if args.value:
|
|
332
431
|
if args.action in {"find", "metrics"}:
|
|
@@ -350,6 +449,21 @@ def _query(args: argparse.Namespace) -> int:
|
|
|
350
449
|
return 0
|
|
351
450
|
|
|
352
451
|
|
|
452
|
+
def _workspace_root(value: str | Path) -> Path:
|
|
453
|
+
root = Path(value).expanduser()
|
|
454
|
+
if not root.exists():
|
|
455
|
+
raise _CliError(
|
|
456
|
+
"workspace_not_found",
|
|
457
|
+
f"Workspace root does not exist: {root}",
|
|
458
|
+
)
|
|
459
|
+
if not root.is_dir():
|
|
460
|
+
raise _CliError(
|
|
461
|
+
"workspace_not_directory",
|
|
462
|
+
f"Workspace root is not a directory: {root}",
|
|
463
|
+
)
|
|
464
|
+
return root.resolve()
|
|
465
|
+
|
|
466
|
+
|
|
353
467
|
def _serve_worker(context: AgentContext, input_stream: BinaryIO, output_stream: BinaryIO, error_stream: TextIO) -> None:
|
|
354
468
|
discarding_oversize_record = False
|
|
355
469
|
while True:
|
delphi_lsp/agent_context.py
CHANGED
|
@@ -658,7 +658,7 @@ class AgentContext:
|
|
|
658
658
|
return revision
|
|
659
659
|
|
|
660
660
|
def invalidate_revision_cache(self) -> None:
|
|
661
|
-
self._last_revision_check_at =
|
|
661
|
+
self._last_revision_check_at = float("-inf")
|
|
662
662
|
|
|
663
663
|
def handle(self, request: AgentRequest | Mapping[str, object]) -> AgentResponse:
|
|
664
664
|
parsed = _validated_request(request)
|
|
@@ -853,6 +853,7 @@ class AgentContext:
|
|
|
853
853
|
)
|
|
854
854
|
elif request.project_id:
|
|
855
855
|
self._focus = Focus(project_id=self._workspace.active_project_id)
|
|
856
|
+
self._require_registry(revision)
|
|
856
857
|
return self._response(request, revision, [self._focus.to_mapping()])
|
|
857
858
|
|
|
858
859
|
def _handle_metrics(self, request: AgentRequest, revision: str) -> AgentResponse:
|
|
@@ -992,7 +993,8 @@ class AgentContext:
|
|
|
992
993
|
if not project_id:
|
|
993
994
|
raise AgentProtocolError(
|
|
994
995
|
"project_required",
|
|
995
|
-
"
|
|
996
|
+
"Multiple projects were found. Run 'query open', then select one "
|
|
997
|
+
"with 'query focus --project-id PROJECT_ID'.",
|
|
996
998
|
)
|
|
997
999
|
return project_id
|
|
998
1000
|
|
delphi_lsp/agent_templates.py
CHANGED
|
@@ -220,7 +220,7 @@ Inspect Delphi/Object Pascal only through `delphi_codebase`; never raw bash/read
|
|
|
220
220
|
|
|
221
221
|
## Protocol v2 workflow
|
|
222
222
|
|
|
223
|
-
1. Call `open`.
|
|
223
|
+
1. Call `open`. A multi-project repository is active as one workspace by default; select a concrete project with `focus(project_id)` only when project-specific compiler context is needed.
|
|
224
224
|
2. Call `find` with a narrow query, then `focus(target_id)` for the returned target.
|
|
225
225
|
3. Inspect focused details in this order as needed: `summary`, `declaration`, `members`, `context`, `body`, `implementations`.
|
|
226
226
|
4. Trace relations with `references`, `callers`, `callees`, `uses`, `used_by`, `inherits`, or `implements`.
|
delphi_lsp/agent_workspace.py
CHANGED
|
@@ -112,6 +112,21 @@ class AgentWorkspace:
|
|
|
112
112
|
)
|
|
113
113
|
projects: list[AgentProject] = []
|
|
114
114
|
project_paths: dict[str, Path | None] = {}
|
|
115
|
+
default_project_id = ""
|
|
116
|
+
if (
|
|
117
|
+
resolved_project_file is None
|
|
118
|
+
and len(discovery.project_files) > 1
|
|
119
|
+
):
|
|
120
|
+
default_project_id = make_target_id("project", "", "workspace")
|
|
121
|
+
projects.append(
|
|
122
|
+
AgentProject(
|
|
123
|
+
project_id=default_project_id,
|
|
124
|
+
name="Workspace",
|
|
125
|
+
path=".",
|
|
126
|
+
kind="workspace",
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
project_paths[default_project_id] = None
|
|
115
130
|
if discovery.project_files:
|
|
116
131
|
for value in discovery.project_files:
|
|
117
132
|
path = Path(value)
|
|
@@ -140,7 +155,9 @@ class AgentWorkspace:
|
|
|
140
155
|
project_paths[project_id] = None
|
|
141
156
|
|
|
142
157
|
workspace = cls(root_path, discovery, tuple(projects), project_paths)
|
|
143
|
-
if
|
|
158
|
+
if default_project_id:
|
|
159
|
+
workspace.select_project(default_project_id)
|
|
160
|
+
elif len(projects) == 1:
|
|
144
161
|
workspace.select_project(projects[0].project_id)
|
|
145
162
|
return workspace
|
|
146
163
|
|
|
@@ -305,7 +322,11 @@ class AgentWorkspace:
|
|
|
305
322
|
project_path = self._project_paths[project_id]
|
|
306
323
|
cached = self._project_cache.get(project_id)
|
|
307
324
|
if project_path is None:
|
|
308
|
-
discovery =
|
|
325
|
+
discovery = (
|
|
326
|
+
self._discovery
|
|
327
|
+
if cached is None and self._discovery.source_files
|
|
328
|
+
else discover_workspace_sources(self._root)
|
|
329
|
+
)
|
|
309
330
|
else:
|
|
310
331
|
discovery = discover_delphi_project(
|
|
311
332
|
self._root,
|
delphi_lsp/project_discovery.py
CHANGED
|
@@ -75,7 +75,11 @@ def discover_delphi_project(
|
|
|
75
75
|
on_progress: ProgressCallback | None = None,
|
|
76
76
|
) -> DelphiProjectDiscovery:
|
|
77
77
|
root_path = Path(root).expanduser().resolve()
|
|
78
|
-
project_path = Path(project_file).expanduser()
|
|
78
|
+
project_path = Path(project_file).expanduser() if project_file is not None else None
|
|
79
|
+
if project_path is not None:
|
|
80
|
+
if not project_path.is_absolute():
|
|
81
|
+
project_path = root_path / project_path
|
|
82
|
+
project_path = project_path.resolve()
|
|
79
83
|
discovery = DelphiProjectDiscovery(root=str(root_path))
|
|
80
84
|
_emit_progress(on_progress, "discovery", str(root_path), 0, 0, None, "project discovery started")
|
|
81
85
|
|
|
@@ -142,7 +146,21 @@ def discover_delphi_project(
|
|
|
142
146
|
for value in defines:
|
|
143
147
|
add_define(value)
|
|
144
148
|
|
|
145
|
-
|
|
149
|
+
explicit_dproj: Path | None = None
|
|
150
|
+
if (
|
|
151
|
+
project_path is not None
|
|
152
|
+
and project_path.suffix.casefold() == ".dproj"
|
|
153
|
+
):
|
|
154
|
+
explicit_dproj = project_path
|
|
155
|
+
project_path = _project_entry_from_dproj(
|
|
156
|
+
explicit_dproj,
|
|
157
|
+
discovery,
|
|
158
|
+
)
|
|
159
|
+
candidates = (
|
|
160
|
+
[]
|
|
161
|
+
if explicit_dproj is not None and project_path is None
|
|
162
|
+
else _project_candidates(root_path, project_path)
|
|
163
|
+
)
|
|
146
164
|
for project in candidates:
|
|
147
165
|
key = str(project).casefold()
|
|
148
166
|
if key not in seen_projects:
|
|
@@ -156,8 +174,13 @@ def discover_delphi_project(
|
|
|
156
174
|
seen_search,
|
|
157
175
|
discovery.search_path_origins,
|
|
158
176
|
)
|
|
159
|
-
|
|
160
|
-
if
|
|
177
|
+
dproj_candidates = [project.with_suffix(".dproj")]
|
|
178
|
+
if explicit_dproj is not None:
|
|
179
|
+
dproj_candidates.insert(0, explicit_dproj)
|
|
180
|
+
for dproj in dproj_candidates:
|
|
181
|
+
config_key = str(dproj).casefold()
|
|
182
|
+
if config_key in seen_configs or not dproj.exists():
|
|
183
|
+
continue
|
|
161
184
|
_read_dproj(
|
|
162
185
|
dproj,
|
|
163
186
|
discovery,
|
|
@@ -169,7 +192,7 @@ def discover_delphi_project(
|
|
|
169
192
|
discovery.include_path_origins,
|
|
170
193
|
add_define,
|
|
171
194
|
)
|
|
172
|
-
seen_configs.add(
|
|
195
|
+
seen_configs.add(config_key)
|
|
173
196
|
discovery.config_files.append(str(dproj))
|
|
174
197
|
for cfg in (project.with_suffix(".cfg"), project.with_suffix(".dof")):
|
|
175
198
|
if cfg.exists():
|
|
@@ -479,6 +502,72 @@ def _main_source_from_dproj(path: Path) -> str | None:
|
|
|
479
502
|
return None
|
|
480
503
|
|
|
481
504
|
|
|
505
|
+
def _project_entry_from_dproj(
|
|
506
|
+
path: Path,
|
|
507
|
+
discovery: DelphiProjectDiscovery,
|
|
508
|
+
) -> Path | None:
|
|
509
|
+
try:
|
|
510
|
+
root = ET.parse(path).getroot()
|
|
511
|
+
except ET.ParseError as exc:
|
|
512
|
+
discovery.problems.append(
|
|
513
|
+
DiscoveryProblem(
|
|
514
|
+
"cant_read_project",
|
|
515
|
+
f"Could not parse Delphi project file: {exc}",
|
|
516
|
+
str(path),
|
|
517
|
+
)
|
|
518
|
+
)
|
|
519
|
+
return None
|
|
520
|
+
except OSError as exc:
|
|
521
|
+
discovery.problems.append(
|
|
522
|
+
DiscoveryProblem(
|
|
523
|
+
"cant_read_project",
|
|
524
|
+
f"Could not read Delphi project file: {exc}",
|
|
525
|
+
str(path),
|
|
526
|
+
)
|
|
527
|
+
)
|
|
528
|
+
return None
|
|
529
|
+
|
|
530
|
+
main_source = next(
|
|
531
|
+
(
|
|
532
|
+
(element.text or "").strip()
|
|
533
|
+
for element in root.iter()
|
|
534
|
+
if _xml_local_name(element.tag) == "MainSource"
|
|
535
|
+
and (element.text or "").strip()
|
|
536
|
+
),
|
|
537
|
+
"",
|
|
538
|
+
)
|
|
539
|
+
if not main_source:
|
|
540
|
+
discovery.problems.append(
|
|
541
|
+
DiscoveryProblem(
|
|
542
|
+
"cant_read_project",
|
|
543
|
+
"Delphi project file does not define MainSource.",
|
|
544
|
+
str(path),
|
|
545
|
+
)
|
|
546
|
+
)
|
|
547
|
+
return None
|
|
548
|
+
|
|
549
|
+
entry = (path.parent / main_source).resolve()
|
|
550
|
+
if entry.suffix.casefold() not in PROJECT_EXTENSIONS:
|
|
551
|
+
discovery.problems.append(
|
|
552
|
+
DiscoveryProblem(
|
|
553
|
+
"cant_read_project",
|
|
554
|
+
"MainSource must reference a .dpr or .dpk entry file.",
|
|
555
|
+
str(path),
|
|
556
|
+
)
|
|
557
|
+
)
|
|
558
|
+
return None
|
|
559
|
+
if not entry.is_file():
|
|
560
|
+
discovery.problems.append(
|
|
561
|
+
DiscoveryProblem(
|
|
562
|
+
"cant_read_project",
|
|
563
|
+
f"MainSource does not exist: {entry}",
|
|
564
|
+
str(path),
|
|
565
|
+
)
|
|
566
|
+
)
|
|
567
|
+
return None
|
|
568
|
+
return entry
|
|
569
|
+
|
|
570
|
+
|
|
482
571
|
def _scan_sources(
|
|
483
572
|
root: Path,
|
|
484
573
|
discovery: DelphiProjectDiscovery,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-delphi-lsp
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: Python Delphi/Object Pascal parser, semantic indexer, and language server.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -42,7 +42,7 @@ Dynamic: license-file
|
|
|
42
42
|
|
|
43
43
|
`python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
|
|
44
44
|
indexes, serves LSP, and provides bounded codebase navigation for agents.
|
|
45
|
-
Version 2.3.
|
|
45
|
+
Version 2.3.2 is authored by Dark Light and supports Windows, macOS, and Linux.
|
|
46
46
|
|
|
47
47
|
## Install and quick start
|
|
48
48
|
|
|
@@ -166,7 +166,8 @@ available without returning the complete file as agent context.
|
|
|
166
166
|
Auto-discovery reads `.dpr`, `.dpk`, `.dproj`, `.cfg`, and `.dof` files.
|
|
167
167
|
Its resolution order is:
|
|
168
168
|
|
|
169
|
-
1. An explicit
|
|
169
|
+
1. An explicit `.dproj`, `.dpr`, or `.dpk` selection takes precedence. An
|
|
170
|
+
explicit `.dproj` resolves its `MainSource`.
|
|
170
171
|
2. Otherwise, `.dpr` and `.dpk` candidates are considered.
|
|
171
172
|
3. `MainSource` in a `.dproj` contributes its entry project.
|
|
172
173
|
4. A selected entry associates same-stem `.dproj`, `.cfg`, and `.dof`.
|
|
@@ -175,11 +176,13 @@ Its resolution order is:
|
|
|
175
176
|
6. Direct `Unit in 'path/Unit.pas'` references contribute their parent
|
|
176
177
|
directory to unit search paths.
|
|
177
178
|
|
|
178
|
-
A single discovered project is selected automatically. With
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
A single discovered project is selected automatically. With multiple project
|
|
180
|
+
entries, a synthetic repository workspace containing every supported source is
|
|
181
|
+
selected automatically while the concrete projects remain available. With no
|
|
182
|
+
project entry, the same workspace model is used. Scans skip build and cache
|
|
183
|
+
directories such as `build`, `dist`, environments, VCS folders, `node_modules`,
|
|
184
|
+
and tool caches. Missing paths and invalid metadata become problems; paths are
|
|
185
|
+
not guessed.
|
|
183
186
|
|
|
184
187
|
## Agent CLI and Interface/Protocol v2
|
|
185
188
|
|
|
@@ -197,7 +200,7 @@ delphi-lsp-agent view --root PATH [--project-file FILE] --layer LAYER
|
|
|
197
200
|
delphi-lsp-agent index --root PATH [--project-file FILE] [--out FILE]
|
|
198
201
|
[--workers auto|N]
|
|
199
202
|
delphi-lsp-agent query --root PATH ACTION [VALUE]
|
|
200
|
-
[--project-id
|
|
203
|
+
[--project-id ID] [--detail summary|declaration|members|context|body|implementations]
|
|
201
204
|
[--relation references|callers|callees|uses|used_by|inherits|implements]
|
|
202
205
|
[--cursor TEXT] [--max-items INT] [--max-chars INT]
|
|
203
206
|
delphi-lsp-agent skill install [--target PATH] [--force]
|
|
@@ -229,6 +232,32 @@ delphi-lsp-agent query --root PATH metrics UNIT_QUERY
|
|
|
229
232
|
delphi-lsp-agent cache status --root PATH --format json
|
|
230
233
|
```
|
|
231
234
|
|
|
235
|
+
The repository root is sufficient even when it contains many projects:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
delphi-lsp-agent cache start --root PATH
|
|
239
|
+
delphi-lsp-agent query --root PATH find TCustomer
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
This prewarms one bounded repository navigation index and requires no project
|
|
243
|
+
selection. A `.dproj` is optional. Pass one when its project-specific compiler
|
|
244
|
+
settings and `MainSource` should define the index:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
delphi-lsp-agent cache start --root PATH --project-file relative/path/Main.dproj
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
An explicit `.dpr` or `.dpk` remains supported. To switch an already running
|
|
251
|
+
repository cache to one of its concrete project views, list the projects and
|
|
252
|
+
focus one by its returned `project_id`:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
delphi-lsp-agent query --root PATH open
|
|
256
|
+
delphi-lsp-agent query --root PATH focus --project-id PROJECT_ID
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Selecting a project this way also prewarms its navigation cache.
|
|
260
|
+
|
|
232
261
|
`inspect` uses the currently focused target, so call `focus TARGET_ID` before
|
|
233
262
|
`inspect` unless a previous request already selected it.
|
|
234
263
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
delphi_lsp/__init__.py,sha256=mq3tF0NrXDgbkJXDuf5HhW6sOnmP7DQxs3zl8fGoAmo,3867
|
|
2
|
-
delphi_lsp/_version.py,sha256=
|
|
3
|
-
delphi_lsp/agent_cache.py,sha256=
|
|
4
|
-
delphi_lsp/agent_cli.py,sha256=
|
|
5
|
-
delphi_lsp/agent_context.py,sha256=
|
|
2
|
+
delphi_lsp/_version.py,sha256=J4CRnpR3v72FGOMp8gqSua_XWZpAfXuqgVWiQFB-gTY,22
|
|
3
|
+
delphi_lsp/agent_cache.py,sha256=Fz7iPA-q2i-SLMmy6FBFTeUJ7Jdd8idXY2maRnhajbY,39204
|
|
4
|
+
delphi_lsp/agent_cli.py,sha256=6cb-cmRmjAyoTNh_CrR-VFUt-sFRNDiVFAmQIkX1DLc,19797
|
|
5
|
+
delphi_lsp/agent_context.py,sha256=9mDtYjL3LJRlwTEpUQeQIhcBkwDxQltQudKxVYdw8wk,97409
|
|
6
6
|
delphi_lsp/agent_layers.py,sha256=Waiwp47BuNxxwzM8EfGb0tvLSRn2udRHkTmuX2JE6cg,25658
|
|
7
7
|
delphi_lsp/agent_metrics.py,sha256=Zh96_qlaDzJIW33XiCucvUeD-Y0zaNr52MRAdKMv9Hg,5217
|
|
8
8
|
delphi_lsp/agent_protocol.py,sha256=RxUyWaL-ixYO3nvAY2Apkhs1c2r5oNEheUaw64Oizo4,12648
|
|
9
9
|
delphi_lsp/agent_relations.py,sha256=q01Mi44m-COs0gDRJa1bTcc1U5M5JsTkQBw4GX1HLPA,43381
|
|
10
|
-
delphi_lsp/agent_templates.py,sha256=
|
|
11
|
-
delphi_lsp/agent_workspace.py,sha256=
|
|
10
|
+
delphi_lsp/agent_templates.py,sha256=8KuLxBbl76mXE-MzNqWVB7OXxiHVu52q17cRsf4nHr4,21930
|
|
11
|
+
delphi_lsp/agent_workspace.py,sha256=si79CAn99nOodapE4ls0-EhClvl1FpLQ-bwWw0j_wf4,26385
|
|
12
12
|
delphi_lsp/binary.py,sha256=s-c0tstGtkbjxgyiS6vteqh5D40qmVEenoECtBCsDcU,8495
|
|
13
13
|
delphi_lsp/comment_builder.py,sha256=XvFW7WTbYy7yGN3MG40gtabTvhGj1ea1MYkySDbVMDA,832
|
|
14
14
|
delphi_lsp/consts.py,sha256=qM40iwxXmvEileWHJVLMYlQSkLoLyQaqM-Zscz3VJbs,5958
|
|
@@ -27,16 +27,16 @@ delphi_lsp/parser.py,sha256=qYY_eTeGgLYn30QyN4_ERjU9-TLFuD5u_CXAMYJdX5A,7721
|
|
|
27
27
|
delphi_lsp/parser_backend.py,sha256=_-veK3B4OreA5wkv5pm96-JTTZtNZUQ92JZMktp2tPU,1667
|
|
28
28
|
delphi_lsp/preprocessor.py,sha256=0WsY4DuYCSo8FOe8TK_8Vo2z8nFhh1nut1OQaifrvgI,32037
|
|
29
29
|
delphi_lsp/progress.py,sha256=FF3lqdDKLvq3b7cOhuSc3P0FDzVnX0miD8U8_3xt5iY,535
|
|
30
|
-
delphi_lsp/project_discovery.py,sha256=
|
|
30
|
+
delphi_lsp/project_discovery.py,sha256=xONO3Q7d2k-duNutn1vS3dknS8VmWR_guFhzZBS2XOQ,22852
|
|
31
31
|
delphi_lsp/project_indexer.py,sha256=kaDbQixp4x2Z9LnzSNhyjdsWfyEFUmE8DSE3cgdL1ZQ,14435
|
|
32
32
|
delphi_lsp/semantic.py,sha256=FZ9x_QsIS_u-1DlzVlYgRqH9gQZ5ySnDAD-xHwzblU8,9808
|
|
33
33
|
delphi_lsp/semantic_builder.py,sha256=ulA7xHJpltjc6cTo5wh9yTcYazuIykM-YLMXD1h-wzk,58680
|
|
34
34
|
delphi_lsp/source_reader.py,sha256=HWiw25sLVZ6s1GGpMl17uZ-o68HoxTEd3Z0__m1vsSE,520
|
|
35
35
|
delphi_lsp/workspace.py,sha256=TU__SujItRlW8hB2kuYC7ZJx1WQVOj7frYxvo7rDqeI,2137
|
|
36
36
|
delphi_lsp/writer.py,sha256=j-cnyiHFNOTFECL7Ehm-m43ye7ev7qFeCCscFMjrAOY,2503
|
|
37
|
-
python_delphi_lsp-2.3.
|
|
38
|
-
python_delphi_lsp-2.3.
|
|
39
|
-
python_delphi_lsp-2.3.
|
|
40
|
-
python_delphi_lsp-2.3.
|
|
41
|
-
python_delphi_lsp-2.3.
|
|
42
|
-
python_delphi_lsp-2.3.
|
|
37
|
+
python_delphi_lsp-2.3.2.dist-info/licenses/LICENSE,sha256=-rPda9qyJvHAhjCx3ZF-Efy07F4eAg4sFvg6ChOGPoU,16726
|
|
38
|
+
python_delphi_lsp-2.3.2.dist-info/METADATA,sha256=5t0-eIjD3lYNQBEkRC1GzDNrBdEpgaHS-LvQISTC8VI,22175
|
|
39
|
+
python_delphi_lsp-2.3.2.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
40
|
+
python_delphi_lsp-2.3.2.dist-info/entry_points.txt,sha256=rh_yGH7diowge_acQcST87A-ewH1QezEsAYC8T5ineY,103
|
|
41
|
+
python_delphi_lsp-2.3.2.dist-info/top_level.txt,sha256=0Zp0sjw5Qtic3EQX9zNeegURga8NuXk6c2e9PN-3OTs,11
|
|
42
|
+
python_delphi_lsp-2.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|