python-delphi-lsp 3.1.2__py3-none-any.whl → 3.2.1__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/__init__.py +105 -89
- delphi_lsp/_version.py +1 -1
- delphi_lsp/agent_cache.py +95 -42
- delphi_lsp/agent_cli.py +33 -7
- delphi_lsp/agent_context.py +47 -18
- delphi_lsp/agent_cpg.py +3 -4
- delphi_lsp/agent_cpg_builder.py +0 -1
- delphi_lsp/agent_layers.py +17 -8
- delphi_lsp/agent_metrics.py +2 -4
- delphi_lsp/agent_protocol.py +2 -3
- delphi_lsp/agent_relations.py +52 -8
- delphi_lsp/agent_templates.py +1 -2
- delphi_lsp/agent_wiki.py +6 -6
- delphi_lsp/agent_workspace.py +9 -5
- delphi_lsp/binary.py +5 -5
- delphi_lsp/comment_builder.py +1 -2
- delphi_lsp/delphiast_lexer.py +3 -5
- delphi_lsp/delphiast_parser.py +384 -52
- delphi_lsp/grammar.py +0 -1
- delphi_lsp/lark_builder.py +5 -10
- delphi_lsp/lark_tokens.py +0 -1
- delphi_lsp/lexical_scanner.py +30 -0
- delphi_lsp/lsp_server.py +603 -110
- delphi_lsp/metrics.py +50 -15
- delphi_lsp/navigation_cache.py +3 -4
- delphi_lsp/nodes.py +19 -19
- delphi_lsp/parallel_outline.py +3 -4
- delphi_lsp/parser.py +101 -17
- delphi_lsp/preprocessor.py +232 -25
- delphi_lsp/progress.py +1 -1
- delphi_lsp/project_config.py +2 -1
- delphi_lsp/project_discovery.py +38 -21
- delphi_lsp/project_indexer.py +18 -18
- delphi_lsp/semantic.py +28 -17
- delphi_lsp/semantic_builder.py +29 -20
- delphi_lsp/workspace.py +1 -1
- {python_delphi_lsp-3.1.2.dist-info → python_delphi_lsp-3.2.1.dist-info}/METADATA +18 -4
- python_delphi_lsp-3.2.1.dist-info/RECORD +48 -0
- python_delphi_lsp-3.1.2.dist-info/RECORD +0 -47
- {python_delphi_lsp-3.1.2.dist-info → python_delphi_lsp-3.2.1.dist-info}/WHEEL +0 -0
- {python_delphi_lsp-3.1.2.dist-info → python_delphi_lsp-3.2.1.dist-info}/entry_points.txt +0 -0
- {python_delphi_lsp-3.1.2.dist-info → python_delphi_lsp-3.2.1.dist-info}/licenses/LICENSE +0 -0
- {python_delphi_lsp-3.1.2.dist-info → python_delphi_lsp-3.2.1.dist-info}/top_level.txt +0 -0
delphi_lsp/__init__.py
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
from ._version import __version__
|
|
2
2
|
from .binary import BinarySerializer, dumps, loads
|
|
3
|
+
from .comment_builder import build_comment_nodes
|
|
3
4
|
from .consts import AttributeName, SyntaxNodeType
|
|
5
|
+
from .grammar import GRAMMAR_RULES, build_grammar
|
|
6
|
+
from .lark_tokens import (
|
|
7
|
+
BASE_TERMINALS,
|
|
8
|
+
KEYWORD_TERMINALS,
|
|
9
|
+
KEYWORDS,
|
|
10
|
+
build_grammar_snippet,
|
|
11
|
+
)
|
|
12
|
+
from .lsp_server import LspWorkspaceState, create_server
|
|
13
|
+
from .metrics import (
|
|
14
|
+
CyclomaticMetrics,
|
|
15
|
+
HalsteadMetrics,
|
|
16
|
+
LineMetrics,
|
|
17
|
+
MetricProblem,
|
|
18
|
+
ProjectMetrics,
|
|
19
|
+
RoutineComplexity,
|
|
20
|
+
UnitMetrics,
|
|
21
|
+
analyze_project,
|
|
22
|
+
analyze_unit,
|
|
23
|
+
)
|
|
4
24
|
from .nodes import (
|
|
5
25
|
CommentNode,
|
|
6
26
|
CompoundSyntaxNode,
|
|
@@ -9,12 +29,31 @@ from .nodes import (
|
|
|
9
29
|
SyntaxTreeException,
|
|
10
30
|
ValuedSyntaxNode,
|
|
11
31
|
)
|
|
12
|
-
from .
|
|
13
|
-
from .preprocessor import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
from .parser import DelphiParseError, DelphiParser, ParseResult, StringTransform, parse
|
|
33
|
+
from .preprocessor import (
|
|
34
|
+
CommentInfo,
|
|
35
|
+
IncludeLoader,
|
|
36
|
+
PreprocessedSource,
|
|
37
|
+
Preprocessor,
|
|
38
|
+
PreprocessorOptions,
|
|
39
|
+
)
|
|
40
|
+
from .progress import ProgressCallback, ProgressEvent
|
|
41
|
+
from .project_discovery import (
|
|
42
|
+
DelphiProjectDiscovery,
|
|
43
|
+
DiscoveryProblem,
|
|
44
|
+
discover_delphi_project,
|
|
45
|
+
)
|
|
46
|
+
from .project_indexer import (
|
|
47
|
+
GetUnitSyntaxHook,
|
|
48
|
+
IncludeFileInfo,
|
|
49
|
+
ProjectIndexer,
|
|
50
|
+
ProjectIndexResult,
|
|
51
|
+
ProjectProblem,
|
|
52
|
+
ProjectProblemType,
|
|
53
|
+
UnitErrorInfo,
|
|
54
|
+
UnitInfo,
|
|
55
|
+
UnitParsedHook,
|
|
56
|
+
)
|
|
18
57
|
from .semantic import (
|
|
19
58
|
ArrayTypeRef,
|
|
20
59
|
ClassOfTypeRef,
|
|
@@ -27,136 +66,113 @@ from .semantic import (
|
|
|
27
66
|
NamedTypeRef,
|
|
28
67
|
PointerTypeRef,
|
|
29
68
|
ProcTypeRef,
|
|
69
|
+
RecordTypeRef,
|
|
30
70
|
ReferenceKind,
|
|
31
71
|
ReferenceTypeRef,
|
|
32
|
-
RecordTypeRef,
|
|
33
72
|
Scope,
|
|
34
73
|
ScopeKind,
|
|
74
|
+
SetTypeRef,
|
|
35
75
|
SourceRange,
|
|
36
76
|
SubrangeTypeRef,
|
|
37
|
-
SetTypeRef,
|
|
38
77
|
Symbol,
|
|
39
78
|
SymbolIndex,
|
|
40
79
|
SymbolKind,
|
|
41
80
|
SymbolReference,
|
|
42
|
-
TypeRef,
|
|
43
81
|
TypeParameterRef,
|
|
82
|
+
TypeRef,
|
|
44
83
|
UnknownTypeRef,
|
|
45
84
|
Visibility,
|
|
46
85
|
)
|
|
47
86
|
from .semantic_builder import SemanticBuilder, SemanticModel, SemanticProblem
|
|
48
87
|
from .workspace import WorkspaceSemanticResult, build_workspace_semantics
|
|
49
|
-
from .
|
|
50
|
-
from .project_discovery import DelphiProjectDiscovery, DiscoveryProblem, discover_delphi_project
|
|
51
|
-
from .progress import ProgressCallback, ProgressEvent
|
|
52
|
-
from .project_indexer import (
|
|
53
|
-
GetUnitSyntaxHook,
|
|
54
|
-
IncludeFileInfo,
|
|
55
|
-
ProjectIndexer,
|
|
56
|
-
ProjectIndexResult,
|
|
57
|
-
ProjectProblem,
|
|
58
|
-
ProjectProblemType,
|
|
59
|
-
UnitErrorInfo,
|
|
60
|
-
UnitInfo,
|
|
61
|
-
UnitParsedHook,
|
|
62
|
-
)
|
|
63
|
-
from .metrics import (
|
|
64
|
-
CyclomaticMetrics,
|
|
65
|
-
HalsteadMetrics,
|
|
66
|
-
LineMetrics,
|
|
67
|
-
MetricProblem,
|
|
68
|
-
ProjectMetrics,
|
|
69
|
-
RoutineComplexity,
|
|
70
|
-
UnitMetrics,
|
|
71
|
-
analyze_project,
|
|
72
|
-
analyze_unit,
|
|
73
|
-
)
|
|
88
|
+
from .writer import SyntaxTreeWriter
|
|
74
89
|
|
|
75
90
|
__all__ = [
|
|
76
|
-
'__version__',
|
|
77
|
-
'AttributeName',
|
|
78
|
-
'BinarySerializer',
|
|
79
|
-
'CommentNode',
|
|
80
|
-
'CompoundSyntaxNode',
|
|
81
|
-
'ParserException',
|
|
82
|
-
'SyntaxNode',
|
|
83
|
-
'SyntaxNodeType',
|
|
84
|
-
'SyntaxTreeException',
|
|
85
|
-
'SyntaxTreeWriter',
|
|
86
|
-
'ValuedSyntaxNode',
|
|
87
|
-
'PreprocessedSource',
|
|
88
|
-
'Preprocessor',
|
|
89
|
-
'PreprocessorOptions',
|
|
90
|
-
'IncludeLoader',
|
|
91
|
-
'CommentInfo',
|
|
92
|
-
'KEYWORDS',
|
|
93
|
-
'KEYWORD_TERMINALS',
|
|
94
91
|
'BASE_TERMINALS',
|
|
95
|
-
'build_grammar_snippet',
|
|
96
|
-
'build_grammar',
|
|
97
92
|
'GRAMMAR_RULES',
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'ParseResult',
|
|
101
|
-
'StringTransform',
|
|
102
|
-
'parse',
|
|
103
|
-
'Modifier',
|
|
93
|
+
'KEYWORDS',
|
|
94
|
+
'KEYWORD_TERMINALS',
|
|
104
95
|
'ArrayTypeRef',
|
|
96
|
+
'AttributeName',
|
|
97
|
+
'BinarySerializer',
|
|
105
98
|
'ClassOfTypeRef',
|
|
106
99
|
'ClassTypeRef',
|
|
100
|
+
'CommentInfo',
|
|
101
|
+
'CommentNode',
|
|
102
|
+
'CompoundSyntaxNode',
|
|
103
|
+
'CyclomaticMetrics',
|
|
104
|
+
'DelphiParseError',
|
|
105
|
+
'DelphiParser',
|
|
106
|
+
'DelphiProjectDiscovery',
|
|
107
|
+
'DiscoveryProblem',
|
|
107
108
|
'EnumTypeRef',
|
|
108
109
|
'FileTypeRef',
|
|
109
110
|
'GenericInstanceTypeRef',
|
|
111
|
+
'GetUnitSyntaxHook',
|
|
112
|
+
'HalsteadMetrics',
|
|
113
|
+
'IncludeFileInfo',
|
|
114
|
+
'IncludeLoader',
|
|
110
115
|
'InterfaceTypeRef',
|
|
116
|
+
'LineMetrics',
|
|
117
|
+
'LspWorkspaceState',
|
|
118
|
+
'MetricProblem',
|
|
119
|
+
'Modifier',
|
|
111
120
|
'NamedTypeRef',
|
|
121
|
+
'ParseResult',
|
|
122
|
+
'ParserException',
|
|
112
123
|
'PointerTypeRef',
|
|
124
|
+
'PreprocessedSource',
|
|
125
|
+
'Preprocessor',
|
|
126
|
+
'PreprocessorOptions',
|
|
113
127
|
'ProcTypeRef',
|
|
128
|
+
'ProgressCallback',
|
|
129
|
+
'ProgressEvent',
|
|
130
|
+
'ProjectIndexResult',
|
|
131
|
+
'ProjectIndexer',
|
|
132
|
+
'ProjectMetrics',
|
|
133
|
+
'ProjectProblem',
|
|
134
|
+
'ProjectProblemType',
|
|
135
|
+
'RecordTypeRef',
|
|
114
136
|
'ReferenceKind',
|
|
115
137
|
'ReferenceTypeRef',
|
|
116
|
-
'
|
|
138
|
+
'RoutineComplexity',
|
|
117
139
|
'Scope',
|
|
118
140
|
'ScopeKind',
|
|
141
|
+
'SemanticBuilder',
|
|
142
|
+
'SemanticModel',
|
|
143
|
+
'SemanticProblem',
|
|
144
|
+
'SetTypeRef',
|
|
119
145
|
'SourceRange',
|
|
146
|
+
'StringTransform',
|
|
120
147
|
'SubrangeTypeRef',
|
|
121
|
-
'SetTypeRef',
|
|
122
148
|
'Symbol',
|
|
123
149
|
'SymbolIndex',
|
|
124
150
|
'SymbolKind',
|
|
125
151
|
'SymbolReference',
|
|
126
|
-
'
|
|
152
|
+
'SyntaxNode',
|
|
153
|
+
'SyntaxNodeType',
|
|
154
|
+
'SyntaxTreeException',
|
|
155
|
+
'SyntaxTreeWriter',
|
|
127
156
|
'TypeParameterRef',
|
|
157
|
+
'TypeRef',
|
|
158
|
+
'UnitErrorInfo',
|
|
159
|
+
'UnitInfo',
|
|
160
|
+
'UnitMetrics',
|
|
161
|
+
'UnitParsedHook',
|
|
128
162
|
'UnknownTypeRef',
|
|
163
|
+
'ValuedSyntaxNode',
|
|
129
164
|
'Visibility',
|
|
130
|
-
'SemanticBuilder',
|
|
131
|
-
'SemanticModel',
|
|
132
|
-
'SemanticProblem',
|
|
133
165
|
'WorkspaceSemanticResult',
|
|
166
|
+
'__version__',
|
|
167
|
+
'analyze_project',
|
|
168
|
+
'analyze_unit',
|
|
169
|
+
'build_comment_nodes',
|
|
170
|
+
'build_grammar',
|
|
171
|
+
'build_grammar_snippet',
|
|
134
172
|
'build_workspace_semantics',
|
|
135
|
-
'LspWorkspaceState',
|
|
136
173
|
'create_server',
|
|
137
|
-
'DelphiProjectDiscovery',
|
|
138
|
-
'DiscoveryProblem',
|
|
139
174
|
'discover_delphi_project',
|
|
140
|
-
'ProgressCallback',
|
|
141
|
-
'ProgressEvent',
|
|
142
|
-
'ProjectIndexer',
|
|
143
|
-
'ProjectIndexResult',
|
|
144
|
-
'ProjectProblemType',
|
|
145
|
-
'ProjectProblem',
|
|
146
|
-
'UnitInfo',
|
|
147
|
-
'UnitErrorInfo',
|
|
148
|
-
'IncludeFileInfo',
|
|
149
|
-
'GetUnitSyntaxHook',
|
|
150
|
-
'UnitParsedHook',
|
|
151
|
-
'MetricProblem',
|
|
152
|
-
'LineMetrics',
|
|
153
|
-
'HalsteadMetrics',
|
|
154
|
-
'RoutineComplexity',
|
|
155
|
-
'CyclomaticMetrics',
|
|
156
|
-
'UnitMetrics',
|
|
157
|
-
'ProjectMetrics',
|
|
158
|
-
'analyze_unit',
|
|
159
|
-
'analyze_project',
|
|
160
175
|
'dumps',
|
|
161
176
|
'loads',
|
|
177
|
+
'parse',
|
|
162
178
|
]
|
delphi_lsp/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "3.1
|
|
1
|
+
__version__ = "3.2.1"
|
delphi_lsp/agent_cache.py
CHANGED
|
@@ -18,6 +18,7 @@ import time
|
|
|
18
18
|
from collections.abc import Callable, Collection, Mapping
|
|
19
19
|
from concurrent.futures import ThreadPoolExecutor
|
|
20
20
|
from dataclasses import dataclass, fields, is_dataclass, replace
|
|
21
|
+
from functools import lru_cache
|
|
21
22
|
from pathlib import Path
|
|
22
23
|
from types import ModuleType
|
|
23
24
|
|
|
@@ -40,6 +41,8 @@ _CONNECTION_TIMEOUT = 2.0
|
|
|
40
41
|
_PREAUTH_CONNECTION_TIMEOUT = 0.25
|
|
41
42
|
_CONNECTION_WORKERS = 8
|
|
42
43
|
_CLIENT_RESPONSE_TIMEOUT = 120.0
|
|
44
|
+
_MAX_CLIENT_RESPONSE_TIMEOUT = 600.0
|
|
45
|
+
_CLIENT_SECONDS_PER_SOURCE = 0.25
|
|
43
46
|
_MEMORY_SIZE = re.compile(r"^(?P<count>[1-9][0-9]*)(?P<suffix>[KMG]?)$", re.IGNORECASE)
|
|
44
47
|
_STARTUP_DIAGNOSTIC_BYTES = 16 * 1024
|
|
45
48
|
_STARTUP_TOKEN_RE = re.compile(r"(?i)(token\b[^\n\r]*?:?\s*['\"]?[A-Za-z0-9_-]+['\"]?|\b[a-zA-Z0-9_-]{32,})")
|
|
@@ -623,7 +626,7 @@ def _process_start_lock(root: str | Path):
|
|
|
623
626
|
def _client_exchange(metadata: CacheMetadata, request: dict[str, object]) -> CacheClientResponse:
|
|
624
627
|
try:
|
|
625
628
|
with socket.create_connection(("127.0.0.1", metadata.port), timeout=2) as connection:
|
|
626
|
-
connection.settimeout(
|
|
629
|
+
connection.settimeout(_client_response_timeout(metadata, request))
|
|
627
630
|
request_without_token = {key: value for key, value in request.items() if key != "token"}
|
|
628
631
|
connection.sendall(json.dumps({"token": metadata.token, **request_without_token}, separators=(",", ":")).encode("utf-8") + b"\n")
|
|
629
632
|
response = _read_line(connection)
|
|
@@ -646,6 +649,43 @@ def _client_exchange(metadata: CacheMetadata, request: dict[str, object]) -> Cac
|
|
|
646
649
|
return CacheClientResponse(payload, str(decoded.get("warning", "")))
|
|
647
650
|
|
|
648
651
|
|
|
652
|
+
def _client_response_timeout(
|
|
653
|
+
metadata: CacheMetadata,
|
|
654
|
+
request: Mapping[str, object],
|
|
655
|
+
) -> float:
|
|
656
|
+
if request.get("action") in {"status", "stop"}:
|
|
657
|
+
return _CLIENT_RESPONSE_TIMEOUT
|
|
658
|
+
return min(
|
|
659
|
+
_MAX_CLIENT_RESPONSE_TIMEOUT,
|
|
660
|
+
_CLIENT_RESPONSE_TIMEOUT
|
|
661
|
+
+ _workspace_source_count(metadata.root) * _CLIENT_SECONDS_PER_SOURCE,
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
@lru_cache(maxsize=64)
|
|
666
|
+
def _workspace_source_count(root: str) -> int:
|
|
667
|
+
count = 0
|
|
668
|
+
source_suffixes = {".pas", ".pp", ".inc", ".dpr", ".dpk"}
|
|
669
|
+
maximum_relevant_count = int(
|
|
670
|
+
(_MAX_CLIENT_RESPONSE_TIMEOUT - _CLIENT_RESPONSE_TIMEOUT)
|
|
671
|
+
/ _CLIENT_SECONDS_PER_SOURCE
|
|
672
|
+
)
|
|
673
|
+
for _current, directories, files in os.walk(Path(root).expanduser().resolve()):
|
|
674
|
+
directories[:] = [
|
|
675
|
+
directory
|
|
676
|
+
for directory in directories
|
|
677
|
+
if directory not in SKIP_DIRS
|
|
678
|
+
]
|
|
679
|
+
count += sum(
|
|
680
|
+
1
|
|
681
|
+
for file_name in files
|
|
682
|
+
if Path(file_name).suffix.casefold() in source_suffixes
|
|
683
|
+
)
|
|
684
|
+
if count >= maximum_relevant_count:
|
|
685
|
+
return maximum_relevant_count
|
|
686
|
+
return count
|
|
687
|
+
|
|
688
|
+
|
|
649
689
|
def _read_line(connection: socket.socket) -> bytes:
|
|
650
690
|
chunks = bytearray()
|
|
651
691
|
while len(chunks) <= _MAX_MESSAGE_BYTES:
|
|
@@ -768,14 +808,7 @@ class _CacheService:
|
|
|
768
808
|
if error.code != "project_required":
|
|
769
809
|
raise
|
|
770
810
|
self.cache_state = "ready"
|
|
771
|
-
self.
|
|
772
|
-
measure=self._measure_retained_bytes,
|
|
773
|
-
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
774
|
-
evict_navigation=self.context.evict_navigation_caches,
|
|
775
|
-
)
|
|
776
|
-
if self.last_budget.compacted:
|
|
777
|
-
self.stats.evictions += 1
|
|
778
|
-
self.cache_state = "compact"
|
|
811
|
+
self._enforce_budget()
|
|
779
812
|
self.prewarm_seconds = time.monotonic() - started
|
|
780
813
|
self.stats.parallel_fallbacks = context.parallel_stats.fallbacks
|
|
781
814
|
|
|
@@ -835,13 +868,16 @@ class _CacheService:
|
|
|
835
868
|
before = self.last_revision
|
|
836
869
|
was_warm = self.context.navigation_cache_is_warm
|
|
837
870
|
try:
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
871
|
+
try:
|
|
872
|
+
response = self.context.handle(request).to_mapping()
|
|
873
|
+
except AgentProtocolError as error:
|
|
874
|
+
raise CacheClientError(error.code, error.message) from None
|
|
875
|
+
except (OSError, UnicodeError):
|
|
876
|
+
raise CacheClientError("source_unavailable", "Source is unavailable.") from None
|
|
877
|
+
except Exception:
|
|
878
|
+
raise CacheClientError("internal_error", "Internal cache error.") from None
|
|
879
|
+
finally:
|
|
880
|
+
self._enforce_budget()
|
|
845
881
|
after = str(response["workspace_revision"])
|
|
846
882
|
self.last_revision = after
|
|
847
883
|
self.stats.requests += 1
|
|
@@ -852,18 +888,24 @@ class _CacheService:
|
|
|
852
888
|
self.stats.parallel_fallbacks += self.context.parallel_stats.fallbacks
|
|
853
889
|
if before != after:
|
|
854
890
|
self.stats.invalidations += 1
|
|
855
|
-
self.last_budget = self.budget.enforce(
|
|
856
|
-
measure=self._measure_retained_bytes,
|
|
857
|
-
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
858
|
-
evict_navigation=self.context.evict_navigation_caches,
|
|
859
|
-
)
|
|
860
|
-
if self.last_budget.compacted:
|
|
861
|
-
self.stats.evictions += 1
|
|
862
|
-
self.cache_state = "compact"
|
|
863
|
-
else:
|
|
864
|
-
self.cache_state = "warm" if self.context.navigation_cache_is_warm else "ready"
|
|
865
891
|
return CacheClientResponse(response, self._consume_warning())
|
|
866
892
|
|
|
893
|
+
def _enforce_budget(self) -> None:
|
|
894
|
+
self.last_budget = self.budget.enforce(
|
|
895
|
+
measure=self._measure_retained_bytes,
|
|
896
|
+
evict_auxiliary=self.context.evict_auxiliary_caches,
|
|
897
|
+
evict_navigation=self.context.evict_navigation_caches,
|
|
898
|
+
)
|
|
899
|
+
if self.last_budget.compacted:
|
|
900
|
+
self.stats.evictions += 1
|
|
901
|
+
self.cache_state = "compact"
|
|
902
|
+
else:
|
|
903
|
+
self.cache_state = (
|
|
904
|
+
"warm"
|
|
905
|
+
if self.context.navigation_cache_is_warm
|
|
906
|
+
else "ready"
|
|
907
|
+
)
|
|
908
|
+
|
|
867
909
|
def _consume_warning(self) -> str:
|
|
868
910
|
warning = cache_warning(self.last_budget, self.budget.max_bytes)
|
|
869
911
|
if self.last_budget.warning_triggered and not self.last_budget.warning_active:
|
|
@@ -874,6 +916,17 @@ class _CacheService:
|
|
|
874
916
|
now = time.monotonic()
|
|
875
917
|
idle = max(0.0, now - self.last_activity)
|
|
876
918
|
context = self._context
|
|
919
|
+
live_rss = current_process_rss_bytes()
|
|
920
|
+
current_bytes = (
|
|
921
|
+
live_rss
|
|
922
|
+
if live_rss > 0
|
|
923
|
+
else self.last_budget.retained_bytes
|
|
924
|
+
)
|
|
925
|
+
current_utilization = current_bytes * 100.0 / self.budget.max_bytes
|
|
926
|
+
peak_utilization = max(
|
|
927
|
+
self.last_budget.peak_utilization_percent,
|
|
928
|
+
current_utilization,
|
|
929
|
+
)
|
|
877
930
|
parallel_stats = (
|
|
878
931
|
context.parallel_stats
|
|
879
932
|
if context is not None
|
|
@@ -883,10 +936,11 @@ class _CacheService:
|
|
|
883
936
|
"pid": self.metadata.pid, "root": self.metadata.root, "project_file": self.metadata.project_file,
|
|
884
937
|
"version": self.metadata.version, "uptime": now - self.started, "idle_seconds": idle,
|
|
885
938
|
"last_activity_at": time.time() - idle,
|
|
886
|
-
"max_memory_bytes": self.budget.max_bytes, "current_bytes":
|
|
887
|
-
"current_utilization_percent":
|
|
888
|
-
"peak_utilization_percent":
|
|
889
|
-
"warning_active": self.
|
|
939
|
+
"max_memory_bytes": self.budget.max_bytes, "current_bytes": current_bytes,
|
|
940
|
+
"current_utilization_percent": current_utilization,
|
|
941
|
+
"peak_utilization_percent": peak_utilization,
|
|
942
|
+
"warning_active": current_utilization >= self.budget.warning_percent,
|
|
943
|
+
"warning_threshold_percent": WARNING_THRESHOLD_PERCENT,
|
|
890
944
|
"cache_state": self.cache_state, "requests": self.stats.requests, "warm_hits": self.stats.warm_hits,
|
|
891
945
|
"rebuilds": self.stats.rebuilds, "invalidations": self.stats.invalidations, "evictions": self.stats.evictions,
|
|
892
946
|
"workers_configured": "auto" if self.metadata.workers == 0 else self.metadata.workers,
|
|
@@ -1073,7 +1127,7 @@ def run_cache_daemon(
|
|
|
1073
1127
|
):
|
|
1074
1128
|
try:
|
|
1075
1129
|
connection, _ = listener.accept()
|
|
1076
|
-
except
|
|
1130
|
+
except TimeoutError:
|
|
1077
1131
|
continue
|
|
1078
1132
|
if not connection_slots.acquire(blocking=False):
|
|
1079
1133
|
connection.close()
|
|
@@ -1240,17 +1294,16 @@ def start_cache(
|
|
|
1240
1294
|
):
|
|
1241
1295
|
raise ValueError("max_disk_cache_bytes must be non-negative.")
|
|
1242
1296
|
load_project_path_config(root)
|
|
1243
|
-
with _process_start_lock(root):
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
)
|
|
1297
|
+
with _process_start_lock(root), _start_lock(root, startup_timeout):
|
|
1298
|
+
return _start_cache_unlocked(
|
|
1299
|
+
root,
|
|
1300
|
+
project_file=project_file,
|
|
1301
|
+
max_memory_bytes=max_memory_bytes,
|
|
1302
|
+
workers=workers,
|
|
1303
|
+
idle_timeout=idle_timeout,
|
|
1304
|
+
startup_timeout=startup_timeout,
|
|
1305
|
+
max_disk_cache_bytes=max_disk_cache_bytes,
|
|
1306
|
+
)
|
|
1254
1307
|
|
|
1255
1308
|
|
|
1256
1309
|
def query_cache(root: str | Path, request: dict[str, object]) -> CacheClientResponse:
|
delphi_lsp/agent_cli.py
CHANGED
|
@@ -10,37 +10,36 @@ import threading
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import BinaryIO, TextIO
|
|
12
12
|
|
|
13
|
-
from .agent_context import AgentContext
|
|
14
13
|
from .agent_cache import (
|
|
15
|
-
CacheClientError,
|
|
16
14
|
DEFAULT_IDLE_TIMEOUT,
|
|
17
15
|
DEFAULT_MAX_DISK_CACHE_BYTES,
|
|
18
16
|
DEFAULT_MAX_MEMORY_BYTES,
|
|
19
17
|
DEFAULT_STARTUP_TIMEOUT,
|
|
20
|
-
|
|
18
|
+
CacheClientError,
|
|
21
19
|
navigation_cache_path,
|
|
20
|
+
parse_memory_size,
|
|
22
21
|
query_cache,
|
|
23
22
|
run_cache_daemon,
|
|
24
23
|
start_cache,
|
|
25
24
|
stop_cache,
|
|
26
25
|
watch_workspace_changes,
|
|
27
26
|
)
|
|
27
|
+
from .agent_context import AgentContext
|
|
28
28
|
from .agent_layers import build_codebase_index, layer_payload, render_layer
|
|
29
29
|
from .agent_protocol import (
|
|
30
30
|
SCHEMA_VERSION,
|
|
31
|
-
AgentProtocolError,
|
|
32
31
|
SUPPORTED_ACTIONS,
|
|
33
32
|
SUPPORTED_DETAILS,
|
|
34
33
|
SUPPORTED_DIRECTIONS,
|
|
35
34
|
SUPPORTED_GRAPHS,
|
|
36
35
|
SUPPORTED_RELATIONS,
|
|
36
|
+
AgentProtocolError,
|
|
37
37
|
)
|
|
38
38
|
from .agent_templates import install_opencode_support, install_skill
|
|
39
39
|
from .agent_wiki import WikiExportError, WikiProgressEvent, export_okf_wiki
|
|
40
40
|
from .parallel_outline import ParallelOutlineError, parse_worker_setting
|
|
41
41
|
from .project_config import ProjectConfigError
|
|
42
42
|
|
|
43
|
-
|
|
44
43
|
_MAX_WORKER_RECORD_BYTES = 1024 * 1024
|
|
45
44
|
_INVALID_JSON_MESSAGE = "Invalid JSON request."
|
|
46
45
|
_INVALID_ENCODING_MESSAGE = "Invalid UTF-8 request."
|
|
@@ -94,6 +93,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
94
93
|
index.add_argument("--project-file", type=Path)
|
|
95
94
|
index.add_argument("--out", type=Path, default=Path(".delphi-lsp") / "agent-index" / "index.json")
|
|
96
95
|
index.add_argument("--workers", type=parse_worker_setting, default=0)
|
|
96
|
+
index.add_argument(
|
|
97
|
+
"--deep-projects",
|
|
98
|
+
action="store_true",
|
|
99
|
+
help="Deep-parse project dependencies for the projects layer.",
|
|
100
|
+
)
|
|
97
101
|
index.set_defaults(func=_index)
|
|
98
102
|
|
|
99
103
|
wiki = subcommands.add_parser("wiki", help="Export a portable Markdown knowledge wiki.")
|
|
@@ -312,10 +316,11 @@ def _discard_broken_stdout() -> None:
|
|
|
312
316
|
|
|
313
317
|
def _view(args: argparse.Namespace) -> None:
|
|
314
318
|
args.root = _workspace_root(args.root)
|
|
319
|
+
args.project_file = _project_file(args.root, args.project_file)
|
|
315
320
|
index = build_codebase_index(
|
|
316
321
|
args.root,
|
|
317
322
|
project_file=args.project_file,
|
|
318
|
-
index_projects=args.deep_projects,
|
|
323
|
+
index_projects=args.deep_projects or args.layer == "problems",
|
|
319
324
|
workers=args.workers,
|
|
320
325
|
)
|
|
321
326
|
sys.stdout.write(render_layer(index, args.layer, query=args.query, output_format=args.format))
|
|
@@ -323,14 +328,16 @@ def _view(args: argparse.Namespace) -> None:
|
|
|
323
328
|
|
|
324
329
|
def _index(args: argparse.Namespace) -> None:
|
|
325
330
|
args.root = _workspace_root(args.root)
|
|
331
|
+
args.project_file = _project_file(args.root, args.project_file)
|
|
326
332
|
output = _resolve_output_path(args.root, args.out)
|
|
327
333
|
index = build_codebase_index(
|
|
328
334
|
args.root,
|
|
329
335
|
project_file=args.project_file,
|
|
330
|
-
index_projects=
|
|
336
|
+
index_projects=args.deep_projects,
|
|
331
337
|
workers=args.workers,
|
|
332
338
|
)
|
|
333
339
|
payload = {
|
|
340
|
+
"deep_projects": bool(args.deep_projects),
|
|
334
341
|
"overview": layer_payload(index, "overview"),
|
|
335
342
|
"projects": layer_payload(index, "projects"),
|
|
336
343
|
"problems": layer_payload(index, "problems"),
|
|
@@ -342,6 +349,7 @@ def _index(args: argparse.Namespace) -> None:
|
|
|
342
349
|
|
|
343
350
|
def _wiki_export(args: argparse.Namespace) -> None:
|
|
344
351
|
args.root = _workspace_root(args.root)
|
|
352
|
+
args.project_file = _project_file(args.root, args.project_file)
|
|
345
353
|
output = _resolve_output_path(args.root, args.out)
|
|
346
354
|
renderer = None if args.quiet else _WikiProgressRenderer(sys.stderr)
|
|
347
355
|
try:
|
|
@@ -449,6 +457,7 @@ def _require_install_directory(target: str | Path) -> None:
|
|
|
449
457
|
|
|
450
458
|
def _worker(args: argparse.Namespace) -> None:
|
|
451
459
|
args.root = _workspace_root(args.root)
|
|
460
|
+
args.project_file = _project_file(args.root, args.project_file)
|
|
452
461
|
context = _open_worker_context(args.root, args.project_file, args.workers)
|
|
453
462
|
watcher_stop = threading.Event()
|
|
454
463
|
watcher = threading.Thread(
|
|
@@ -509,6 +518,7 @@ def _cache_error(error: CacheClientError) -> int:
|
|
|
509
518
|
|
|
510
519
|
def _cache_start(args: argparse.Namespace) -> int:
|
|
511
520
|
args.root = _workspace_root(args.root)
|
|
521
|
+
args.project_file = _project_file(args.root, args.project_file)
|
|
512
522
|
try:
|
|
513
523
|
start_cache(
|
|
514
524
|
args.root,
|
|
@@ -583,6 +593,7 @@ def _cache_clear(args: argparse.Namespace) -> int:
|
|
|
583
593
|
|
|
584
594
|
def _cache_serve(args: argparse.Namespace) -> int:
|
|
585
595
|
args.root = _workspace_root(args.root)
|
|
596
|
+
args.project_file = _project_file(args.root, args.project_file)
|
|
586
597
|
run_cache_daemon(
|
|
587
598
|
args.root,
|
|
588
599
|
project_file=str(args.project_file or ""),
|
|
@@ -658,6 +669,21 @@ def _workspace_root(value: str | Path) -> Path:
|
|
|
658
669
|
return root.resolve()
|
|
659
670
|
|
|
660
671
|
|
|
672
|
+
def _project_file(root: Path, value: Path | None) -> Path | None:
|
|
673
|
+
if value is None:
|
|
674
|
+
return None
|
|
675
|
+
project = value.expanduser()
|
|
676
|
+
if not project.is_absolute():
|
|
677
|
+
project = root / project
|
|
678
|
+
project = project.resolve()
|
|
679
|
+
if not project.is_file():
|
|
680
|
+
raise _CliError(
|
|
681
|
+
"project_file_not_found",
|
|
682
|
+
f"Explicit project file does not exist: {project}",
|
|
683
|
+
)
|
|
684
|
+
return project
|
|
685
|
+
|
|
686
|
+
|
|
661
687
|
def _resolve_output_path(root: Path, output: Path) -> Path:
|
|
662
688
|
return output if output.is_absolute() else root / output
|
|
663
689
|
|