python-delphi-lsp 2.3.0__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.0/python_delphi_lsp.egg-info → python_delphi_lsp-2.3.2}/PKG-INFO +38 -9
- {python_delphi_lsp-2.3.0 → 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.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_cache.py +144 -14
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_cli.py +128 -14
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_context.py +4 -2
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_templates.py +1 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_workspace.py +23 -2
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/project_discovery.py +94 -5
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/pyproject.toml +1 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2/python_delphi_lsp.egg-info}/PKG-INFO +38 -9
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/SOURCES.txt +1 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_cache.py +234 -1
- python_delphi_lsp-2.3.2/tests/test_agent_cli_errors.py +234 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_codebase.py +1 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_context.py +59 -12
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_worker.py +278 -6
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_workspace.py +57 -7
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_package_metadata.py +3 -3
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_project_discovery.py +93 -0
- python_delphi_lsp-2.3.0/delphi_lsp/_version.py +0 -1
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/.github/workflows/ci.yml +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/LICENSE +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/MANIFEST.in +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/__init__.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_layers.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_protocol.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/agent_relations.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/binary.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/comment_builder.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/consts.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/delphiast_lexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/delphiast_parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/delphiast_tokens.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/grammar.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/lark_builder.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/lark_tokens.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/lsp_server.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/navigation_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/nodes.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/parallel_outline.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/parser_backend.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/preprocessor.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/progress.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/project_indexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/semantic.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/semantic_builder.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/source_reader.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/workspace.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/delphi_lsp/writer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/opencode.json +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/entry_points.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/requires.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/python_delphi_lsp.egg-info/top_level.txt +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/audit_delphi_language_features.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/benchmark_github_corpus.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/benchmark_parallel_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_codebase_skill_test.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_opencode_test.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/build_github_performance_corpus.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/check_ornith_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/generate_progress_pdf.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/generate_release_evidence.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/prepare_ornith_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/run_opencode_lsp_probe.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/run_openrouter_github_e2e.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/start_ornith_vllm.sh +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/scripts/summarize_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/setup.cfg +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/corpora.performance.lock.json +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/constset.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/properties.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/library_demo.dpr +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/package_demo.dpk +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/program_demo.dpr +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_advanced.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_attributes.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_basic.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_consumer.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_generics.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_inheritance.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_math.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_properties.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_sections.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_statements.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_types.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_unresolved.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/fixtures/unit_with.pas +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_protocol.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_agent_relations.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_bootstrap_vllm_codebase_skill.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_bootstrap_vllm_opencode.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_delphiast_lexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_delphiast_native_compatibility.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_delphiast_parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_delphiast_semantic_compatibility.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_diagnostics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_github_performance_corpus.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_language_feature_matrix.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_legacy_snippets.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_lsp_features.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_lsp_support.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_metrics.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_navigation_cache.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_opencode_config.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_opencode_probe.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_openrouter_github_e2e.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_ornith_cache_prepare.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_parallel_outline.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_parser.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_parser_backends.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_preprocessor.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_progress.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_project_indexer.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_release_evidence.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_semantic.py +0 -0
- {python_delphi_lsp-2.3.0 → python_delphi_lsp-2.3.2}/tests/test_vllm_start_script.py +0 -0
- {python_delphi_lsp-2.3.0 → 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"
|
|
@@ -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())
|