python-delphi-lsp 2.3.1__tar.gz → 2.3.2__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.
- {python_delphi_lsp-2.3.1/python_delphi_lsp.egg-info → python_delphi_lsp-2.3.2}/PKG-INFO +38 -9
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/README.md +37 -8
- python_delphi_lsp-2.3.2/delphi_lsp/_version.py +1 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_cache.py +16 -5
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_cli.py +8 -1
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_context.py +3 -1
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_templates.py +1 -1
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_workspace.py +23 -2
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/project_discovery.py +89 -4
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/pyproject.toml +1 -1
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2/python_delphi_lsp.egg-info}/PKG-INFO +38 -9
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_cache.py +108 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_cli_errors.py +12 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_codebase.py +1 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_context.py +22 -11
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_worker.py +50 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_workspace.py +57 -7
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_package_metadata.py +3 -3
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_project_discovery.py +73 -0
- python_delphi_lsp-2.3.1/delphi_lsp/_version.py +0 -1
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/.github/workflows/ci.yml +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/LICENSE +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/MANIFEST.in +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/__init__.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_layers.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_metrics.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_protocol.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_relations.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/binary.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/comment_builder.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/consts.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/delphiast_lexer.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/delphiast_parser.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/delphiast_tokens.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/grammar.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/lark_builder.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/lark_tokens.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/lsp_server.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/metrics.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/navigation_cache.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/nodes.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/parallel_outline.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/parser.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/parser_backend.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/preprocessor.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/progress.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/project_indexer.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/semantic.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/semantic_builder.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/source_reader.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/workspace.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/delphi_lsp/writer.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/opencode.json +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/SOURCES.txt +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/entry_points.txt +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/requires.txt +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/top_level.txt +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/audit_delphi_language_features.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/benchmark_github_corpus.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/benchmark_parallel_cache.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_codebase_skill_test.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_opencode_test.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/build_github_performance_corpus.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/check_ornith_cache.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/generate_progress_pdf.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/generate_release_evidence.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/prepare_ornith_cache.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/run_opencode_lsp_probe.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/run_openrouter_github_e2e.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/start_ornith_vllm.sh +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/scripts/summarize_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/setup.cfg +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/corpora.performance.lock.json +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/constset.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/properties.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/library_demo.dpr +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/package_demo.dpk +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/program_demo.dpr +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_advanced.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_attributes.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_basic.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_consumer.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_generics.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_inheritance.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_math.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_properties.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_sections.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_statements.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_types.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_unresolved.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_with.pas +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_metrics.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_protocol.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_agent_relations.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_bootstrap_vllm_codebase_skill.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_bootstrap_vllm_opencode.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_delphiast_lexer.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_delphiast_native_compatibility.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_delphiast_parser.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_delphiast_semantic_compatibility.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_diagnostics.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_github_performance_corpus.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_language_feature_matrix.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_legacy_snippets.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_lsp_features.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_lsp_support.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_metrics.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_navigation_cache.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_opencode_config.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_opencode_probe.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_openrouter_github_e2e.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_ornith_cache_prepare.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_parallel_outline.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_parser.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_parser_backends.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_preprocessor.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_progress.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_project_indexer.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_release_evidence.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_semantic.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_vllm_start_script.py +0 -0
- {python_delphi_lsp-2.3.1 → python_delphi_lsp-2.3.2}/tests/test_workspace.py +0 -0
|
@@ -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
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
|
|
4
4
|
indexes, serves LSP, and provides bounded codebase navigation for agents.
|
|
5
|
-
Version 2.3.
|
|
5
|
+
Version 2.3.2 is authored by Dark Light and supports Windows, macOS, and Linux.
|
|
6
6
|
|
|
7
7
|
## Install and quick start
|
|
8
8
|
|
|
@@ -126,7 +126,8 @@ available without returning the complete file as agent context.
|
|
|
126
126
|
Auto-discovery reads `.dpr`, `.dpk`, `.dproj`, `.cfg`, and `.dof` files.
|
|
127
127
|
Its resolution order is:
|
|
128
128
|
|
|
129
|
-
1. An explicit
|
|
129
|
+
1. An explicit `.dproj`, `.dpr`, or `.dpk` selection takes precedence. An
|
|
130
|
+
explicit `.dproj` resolves its `MainSource`.
|
|
130
131
|
2. Otherwise, `.dpr` and `.dpk` candidates are considered.
|
|
131
132
|
3. `MainSource` in a `.dproj` contributes its entry project.
|
|
132
133
|
4. A selected entry associates same-stem `.dproj`, `.cfg`, and `.dof`.
|
|
@@ -135,11 +136,13 @@ Its resolution order is:
|
|
|
135
136
|
6. Direct `Unit in 'path/Unit.pas'` references contribute their parent
|
|
136
137
|
directory to unit search paths.
|
|
137
138
|
|
|
138
|
-
A single discovered project is selected automatically. With
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
A single discovered project is selected automatically. With multiple project
|
|
140
|
+
entries, a synthetic repository workspace containing every supported source is
|
|
141
|
+
selected automatically while the concrete projects remain available. With no
|
|
142
|
+
project entry, the same workspace model is used. Scans skip build and cache
|
|
143
|
+
directories such as `build`, `dist`, environments, VCS folders, `node_modules`,
|
|
144
|
+
and tool caches. Missing paths and invalid metadata become problems; paths are
|
|
145
|
+
not guessed.
|
|
143
146
|
|
|
144
147
|
## Agent CLI and Interface/Protocol v2
|
|
145
148
|
|
|
@@ -157,7 +160,7 @@ delphi-lsp-agent view --root PATH [--project-file FILE] --layer LAYER
|
|
|
157
160
|
delphi-lsp-agent index --root PATH [--project-file FILE] [--out FILE]
|
|
158
161
|
[--workers auto|N]
|
|
159
162
|
delphi-lsp-agent query --root PATH ACTION [VALUE]
|
|
160
|
-
[--project-id
|
|
163
|
+
[--project-id ID] [--detail summary|declaration|members|context|body|implementations]
|
|
161
164
|
[--relation references|callers|callees|uses|used_by|inherits|implements]
|
|
162
165
|
[--cursor TEXT] [--max-items INT] [--max-chars INT]
|
|
163
166
|
delphi-lsp-agent skill install [--target PATH] [--force]
|
|
@@ -189,6 +192,32 @@ delphi-lsp-agent query --root PATH metrics UNIT_QUERY
|
|
|
189
192
|
delphi-lsp-agent cache status --root PATH --format json
|
|
190
193
|
```
|
|
191
194
|
|
|
195
|
+
The repository root is sufficient even when it contains many projects:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
delphi-lsp-agent cache start --root PATH
|
|
199
|
+
delphi-lsp-agent query --root PATH find TCustomer
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
This prewarms one bounded repository navigation index and requires no project
|
|
203
|
+
selection. A `.dproj` is optional. Pass one when its project-specific compiler
|
|
204
|
+
settings and `MainSource` should define the index:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
delphi-lsp-agent cache start --root PATH --project-file relative/path/Main.dproj
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
An explicit `.dpr` or `.dpk` remains supported. To switch an already running
|
|
211
|
+
repository cache to one of its concrete project views, list the projects and
|
|
212
|
+
focus one by its returned `project_id`:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
delphi-lsp-agent query --root PATH open
|
|
216
|
+
delphi-lsp-agent query --root PATH focus --project-id PROJECT_ID
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Selecting a project this way also prewarms its navigation cache.
|
|
220
|
+
|
|
192
221
|
`inspect` uses the currently focused target, so call `focus TARGET_ID` before
|
|
193
222
|
`inspect` unless a previous request already selected it.
|
|
194
223
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.2"
|
|
@@ -639,6 +639,21 @@ def _read_startup_tail(diagnostics: object, *, max_bytes: int = _STARTUP_DIAGNOS
|
|
|
639
639
|
return b""
|
|
640
640
|
|
|
641
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
|
+
|
|
642
657
|
class _CacheService:
|
|
643
658
|
def __init__(self, metadata: CacheMetadata) -> None:
|
|
644
659
|
self.metadata = metadata
|
|
@@ -921,11 +936,7 @@ def _start_cache_unlocked(
|
|
|
921
936
|
]
|
|
922
937
|
if project:
|
|
923
938
|
command.extend(("--project-file", project))
|
|
924
|
-
options
|
|
925
|
-
if os.name == "nt":
|
|
926
|
-
options["creationflags"] = subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
|
|
927
|
-
else:
|
|
928
|
-
options["start_new_session"] = True
|
|
939
|
+
options = _daemon_process_options()
|
|
929
940
|
with tempfile.TemporaryFile() as diagnostics:
|
|
930
941
|
options["stderr"] = diagnostics
|
|
931
942
|
process = subprocess.Popen(command, **options)
|
|
@@ -151,7 +151,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
151
151
|
|
|
152
152
|
def _add_cache_start_arguments(parser: argparse.ArgumentParser) -> None:
|
|
153
153
|
parser.add_argument("--root", type=Path, default=Path("."))
|
|
154
|
-
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
|
+
)
|
|
155
162
|
parser.add_argument("--max-memory", type=parse_memory_size, default=DEFAULT_MAX_MEMORY_BYTES)
|
|
156
163
|
parser.add_argument("--workers", type=parse_worker_setting, default=0)
|
|
157
164
|
parser.add_argument("--idle-timeout", type=_positive_integer, default=DEFAULT_IDLE_TIMEOUT)
|
|
@@ -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
|
|
|
@@ -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`.
|
|
@@ -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,
|
|
@@ -146,7 +146,21 @@ def discover_delphi_project(
|
|
|
146
146
|
for value in defines:
|
|
147
147
|
add_define(value)
|
|
148
148
|
|
|
149
|
-
|
|
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
|
+
)
|
|
150
164
|
for project in candidates:
|
|
151
165
|
key = str(project).casefold()
|
|
152
166
|
if key not in seen_projects:
|
|
@@ -160,8 +174,13 @@ def discover_delphi_project(
|
|
|
160
174
|
seen_search,
|
|
161
175
|
discovery.search_path_origins,
|
|
162
176
|
)
|
|
163
|
-
|
|
164
|
-
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
|
|
165
184
|
_read_dproj(
|
|
166
185
|
dproj,
|
|
167
186
|
discovery,
|
|
@@ -173,7 +192,7 @@ def discover_delphi_project(
|
|
|
173
192
|
discovery.include_path_origins,
|
|
174
193
|
add_define,
|
|
175
194
|
)
|
|
176
|
-
seen_configs.add(
|
|
195
|
+
seen_configs.add(config_key)
|
|
177
196
|
discovery.config_files.append(str(dproj))
|
|
178
197
|
for cfg in (project.with_suffix(".cfg"), project.with_suffix(".dof")):
|
|
179
198
|
if cfg.exists():
|
|
@@ -483,6 +502,72 @@ def _main_source_from_dproj(path: Path) -> str | None:
|
|
|
483
502
|
return None
|
|
484
503
|
|
|
485
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
|
+
|
|
486
571
|
def _scan_sources(
|
|
487
572
|
root: Path,
|
|
488
573
|
discovery: DelphiProjectDiscovery,
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-delphi-lsp"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.2"
|
|
8
8
|
description = "Python Delphi/Object Pascal parser, semantic indexer, and language server."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -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
|
|
|
@@ -332,6 +332,45 @@ def test_startup_timeout_replaces_the_old_ten_second_deadline(
|
|
|
332
332
|
assert process.killed is True
|
|
333
333
|
|
|
334
334
|
|
|
335
|
+
def test_windows_cache_daemon_starts_without_a_console_window(
|
|
336
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
337
|
+
) -> None:
|
|
338
|
+
from delphi_lsp import agent_cache
|
|
339
|
+
|
|
340
|
+
create_no_window = 0x08000000
|
|
341
|
+
create_new_process_group = 0x00000200
|
|
342
|
+
detached_process = 0x00000008
|
|
343
|
+
monkeypatch.setattr(agent_cache.os, "name", "nt")
|
|
344
|
+
monkeypatch.setattr(
|
|
345
|
+
agent_cache.subprocess,
|
|
346
|
+
"CREATE_NO_WINDOW",
|
|
347
|
+
create_no_window,
|
|
348
|
+
raising=False,
|
|
349
|
+
)
|
|
350
|
+
monkeypatch.setattr(
|
|
351
|
+
agent_cache.subprocess,
|
|
352
|
+
"CREATE_NEW_PROCESS_GROUP",
|
|
353
|
+
create_new_process_group,
|
|
354
|
+
raising=False,
|
|
355
|
+
)
|
|
356
|
+
monkeypatch.setattr(
|
|
357
|
+
agent_cache.subprocess,
|
|
358
|
+
"DETACHED_PROCESS",
|
|
359
|
+
detached_process,
|
|
360
|
+
raising=False,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
options = agent_cache._daemon_process_options()
|
|
364
|
+
|
|
365
|
+
assert options["creationflags"] == (
|
|
366
|
+
create_no_window | create_new_process_group
|
|
367
|
+
)
|
|
368
|
+
assert options["creationflags"] & detached_process == 0
|
|
369
|
+
assert options["stdin"] is agent_cache.subprocess.DEVNULL
|
|
370
|
+
assert options["stdout"] is agent_cache.subprocess.DEVNULL
|
|
371
|
+
assert "start_new_session" not in options
|
|
372
|
+
|
|
373
|
+
|
|
335
374
|
@pytest.mark.parametrize("startup_timeout", [0, -1, float("nan"), float("inf")])
|
|
336
375
|
def test_start_cache_rejects_non_positive_startup_timeout(
|
|
337
376
|
tmp_path: Path,
|
|
@@ -453,6 +492,72 @@ def test_prewarm_only_tolerates_project_selection_error(monkeypatch, tmp_path: P
|
|
|
453
492
|
service.prewarm()
|
|
454
493
|
|
|
455
494
|
|
|
495
|
+
def test_multiple_projects_prewarm_repository_navigation_cache(
|
|
496
|
+
tmp_path: Path,
|
|
497
|
+
) -> None:
|
|
498
|
+
from delphi_lsp.agent_cache import CacheMetadata, _CacheService
|
|
499
|
+
|
|
500
|
+
write_source(
|
|
501
|
+
tmp_path / "A.dpr",
|
|
502
|
+
"program A; uses AUnit in 'AUnit.pas'; begin end.",
|
|
503
|
+
)
|
|
504
|
+
write_source(
|
|
505
|
+
tmp_path / "B.dpr",
|
|
506
|
+
"program B; uses BUnit in 'BUnit.pas'; begin end.",
|
|
507
|
+
)
|
|
508
|
+
write_source(
|
|
509
|
+
tmp_path / "AUnit.pas",
|
|
510
|
+
"""
|
|
511
|
+
unit AUnit;
|
|
512
|
+
interface
|
|
513
|
+
type
|
|
514
|
+
TARepositoryCache = class
|
|
515
|
+
end;
|
|
516
|
+
implementation
|
|
517
|
+
end.
|
|
518
|
+
""",
|
|
519
|
+
)
|
|
520
|
+
write_source(
|
|
521
|
+
tmp_path / "BUnit.pas",
|
|
522
|
+
"""
|
|
523
|
+
unit BUnit;
|
|
524
|
+
interface
|
|
525
|
+
type
|
|
526
|
+
TBRepositoryCache = class
|
|
527
|
+
end;
|
|
528
|
+
implementation
|
|
529
|
+
end.
|
|
530
|
+
""",
|
|
531
|
+
)
|
|
532
|
+
metadata = CacheMetadata(
|
|
533
|
+
2,
|
|
534
|
+
str(tmp_path.resolve()),
|
|
535
|
+
os.getpid(),
|
|
536
|
+
1,
|
|
537
|
+
"x" * 32,
|
|
538
|
+
"test",
|
|
539
|
+
"",
|
|
540
|
+
512 * 1024**2,
|
|
541
|
+
0,
|
|
542
|
+
10,
|
|
543
|
+
time.time(),
|
|
544
|
+
)
|
|
545
|
+
service = _CacheService(metadata)
|
|
546
|
+
|
|
547
|
+
service.prewarm()
|
|
548
|
+
|
|
549
|
+
assert service.context.navigation_cache_is_warm
|
|
550
|
+
assert service.cache_state == "warm"
|
|
551
|
+
assert service.status()["current_bytes"] > 0
|
|
552
|
+
result = service.request(
|
|
553
|
+
{"action": "find", "query": "RepositoryCache"}
|
|
554
|
+
).payload["result"]
|
|
555
|
+
assert {item["name"] for item in result} == {
|
|
556
|
+
"TARepositoryCache",
|
|
557
|
+
"TBRepositoryCache",
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
456
561
|
def test_cache_service_uses_constant_time_accounting_instead_of_deep_graph_walk(
|
|
457
562
|
monkeypatch: pytest.MonkeyPatch,
|
|
458
563
|
tmp_path: Path,
|
|
@@ -735,6 +840,7 @@ def test_readme_documents_bounded_cache_daemon_commands_and_retention_contract()
|
|
|
735
840
|
"delphi-lsp-agent cache stop --root PATH",
|
|
736
841
|
"delphi-lsp-agent query --root PATH find TCustomer",
|
|
737
842
|
"delphi-lsp-agent query --root PATH focus TARGET_ID",
|
|
843
|
+
"delphi-lsp-agent query --root PATH focus --project-id PROJECT_ID",
|
|
738
844
|
"delphi-lsp-agent query --root PATH inspect",
|
|
739
845
|
"delphi-lsp-agent query --root PATH trace TARGET_ID --relation callers",
|
|
740
846
|
"delphi-lsp-agent query --root PATH metrics UNIT_QUERY",
|
|
@@ -755,6 +861,8 @@ def test_readme_documents_bounded_cache_daemon_commands_and_retention_contract()
|
|
|
755
861
|
assert "status JSON" in readme
|
|
756
862
|
assert "Protocol v2 JSON" in readme
|
|
757
863
|
assert "writes warnings to stderr" in readme
|
|
864
|
+
assert "A `.dproj` is optional" in readme
|
|
865
|
+
assert "Selecting a project this way also prewarms" in readme
|
|
758
866
|
assert "Eviction is ordered" in readme
|
|
759
867
|
assert "auxiliary caches are evicted first" in readme
|
|
760
868
|
assert "rebuilds the navigation state on demand" in readme
|
|
@@ -64,6 +64,18 @@ def test_query_cli_rejects_protocol_limits_before_contacting_cache(
|
|
|
64
64
|
parser.parse_args(["query", "open", option, value])
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
def test_cache_start_help_documents_optional_dproj(
|
|
68
|
+
capsys: pytest.CaptureFixture[str],
|
|
69
|
+
) -> None:
|
|
70
|
+
parser = agent_cli.build_parser()
|
|
71
|
+
|
|
72
|
+
with pytest.raises(SystemExit) as stopped:
|
|
73
|
+
parser.parse_args(["cache", "start", "--help"])
|
|
74
|
+
|
|
75
|
+
assert stopped.value.code == 0
|
|
76
|
+
assert ".dproj" in capsys.readouterr().out
|
|
77
|
+
|
|
78
|
+
|
|
67
79
|
@pytest.mark.parametrize("value", [0, -1, True])
|
|
68
80
|
def test_start_cache_rejects_non_positive_idle_timeout(
|
|
69
81
|
tmp_path: Path,
|