feat: add LSP server for editor integration (pygls)#323
Conversation
|
@Jonas-Werne Iterating over the plan now: https://github.com/reqstool/reqstool-client/blob/feat/314-lsp-server/docs/lsp-server-plan.md |
…nd multi-root workspace support (#314) Adds refined Q5 root project discovery algorithm, document symbols feature (Step 9), separate root_discovery.py module, workspace/didChangeWorkspaceFolders support, and YAML schema utility (Step 10). Signed-off-by: jimisola <jimisola@users.noreply.github.com>
Move pygls/lsprotocol to project.optional-dependencies instead of main dependencies. Users install with `pip install reqstool[lsp]` only when they need the LSP server; CI pipelines keep the lighter base install. Signed-off-by: jimisola <jimisola@users.noreply.github.com>
…annotation parser (#314) - Add pygls/lsprotocol as optional [lsp] extra in pyproject.toml - Add `reqstool lsp` subcommand with ImportError guard for missing extra - Create annotation_parser module detecting @Requirements/@svcs in Java/Python (source decorators) and TypeScript/JavaScript (JSDoc tags) - Add 33 unit tests for annotation parser covering both syntaxes, position lookup, completion context, and multi-line annotations Signed-off-by: jimisola <jimisola@users.noreply.github.com>
- ProjectState wraps build_database() pipeline for a single reqstool project with query helpers for requirements, SVCs, and MVRs - Root discovery finds root projects in workspace folders by parsing requirements.yml metadata and building a local reference graph - WorkspaceManager provides per-folder isolation with add/remove/rebuild operations and file-to-project resolution - Add 27 unit tests covering project lifecycle, root discovery algorithm, and workspace management Signed-off-by: jimisola <jimisola@users.noreply.github.com>
… command (#314) - ReqstoolLanguageServer (pygls subclass) with workspace manager integration - Lifecycle handlers: initialized, shutdown, didOpen/Change/Save/Close - Workspace folder change tracking (add/remove folders dynamically) - File watcher for static YAML files triggers project rebuild - reqstool.refresh command for manual rebuild of all projects - Diagnostic publishing placeholders for Step 6 Signed-off-by: jimisola <jimisola@users.noreply.github.com>
- Hover on @Requirements/@svcs annotations shows requirement/SVC details - Hover on YAML fields shows JSON Schema descriptions - YAML schema utilities: load schemas, resolve $ref, get field descriptions/enum values - Wire hover handler into LSP server Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
- Source code diagnostics: unknown requirement/SVC IDs, deprecated/obsolete lifecycle warnings - YAML diagnostics: parse errors and JSON Schema validation against reqstool schemas - Wire diagnostics into server on didOpen, didChange, didSave, and rebuild events Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
- Source completion: offer requirement/SVC IDs inside @Requirements/@svcs annotations - YAML completion: offer enum values for fields like significance, variant, categories - Wire completion handler into server with trigger characters Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
- Source → YAML: navigate from @Requirements/@svcs annotations to YAML definitions - YAML → YAML: navigate from requirement IDs to SVC references and vice versa - Wire definition handler into LSP server Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
- Outline view for requirements.yml, software_verification_cases.yml, manual_verification_results.yml - Shows requirement/SVC/MVR items with titles, significance, and cross-references as children - Wire document symbol handler into LSP server Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
_find_id_in_yaml only matched `id: <raw_id>` lines, so cross-reference navigation (REQ→SVC, SVC→MVR) returned empty results because IDs appear inside array fields like `requirement_ids: ["REQ_PASS"]`. Add _find_reference_in_yaml with a \b word-boundary pattern and strengthen the integration test to assert actual navigation results. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Add location_type and location_uri columns to urn_metadata table to record where each URN's data originated (local, git, maven, pypi). Carry resolved file paths through CombinedRawDataset so the LSP go-to-definition handler uses actual paths from reqstool_config.yml instead of hard-coded filenames. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Add regression-python fixture project with requirements, SVCs, MVRs, annotations, and test results for end-to-end LSP integration tests. Configure pytest-asyncio and add test client infrastructure. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
…#314) - Accept --stdio flag (passed by VS Code LSP client) in lsp subcommand - Add --tcp, --host, --port args for TCP transport support - Wrap start_server() call with exception handler in command.py - Wrap server.start_io()/start_tcp() with exception logging in server.py
…okens, codeAction LSP features (#314) - Add reqstool/details custom request for structured REQ/SVC/MVR data - Extend hover with "Open Details" command link - Add textDocument/codeLens (verification status above annotations) - Add textDocument/inlayHint (title inline after ID) - Add textDocument/references (find all usages across open docs + YAML) - Add workspace/symbol (quick-search REQ/SVC IDs) - Add textDocument/semanticTokens/full (color-code deprecated/obsolete) - Add textDocument/codeAction (quick fixes for unknown/deprecated IDs) - Add get_mvr() and get_yaml_paths() to ProjectState - Add _get() and _first_project() shared helpers to server.py Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
21e5991 to
7fc2a9c
Compare
… references (#314) - get_requirement_details: adds `references` (cross-refs) and `implementations` (annotation impls) - get_svc_details: adds `test_annotations` and `test_results` (automated test status per annotation) - RequirementsRepository: adds get_test_results_for_svc for targeted per-SVC test result lookup - ProjectState: exposes get_impl_annotations_for_req, get_test_annotations_for_svc, get_test_results_for_svc Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
…s LSP features (#314) - completion: filter DEPRECATED/OBSOLETE IDs from suggestions - codeLens: add ⚠/✕ lifecycle badge per ID; pass full ids list in command args - hover: show implementation count for requirements; tests passed/failed/missing and MVR counts for SVCs - details: add test_summary aggregate, enrich requirement_ids with title+lifecycle_state, add source_paths to all responses - semanticTokens: 4 distinct token types in lifecycle order — reqstoolDraft(0), reqstoolValid(1), reqstoolDeprecated(2), reqstoolObsolete(3) Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
- Add static lsp.adoc covering all LSP capabilities, commands, and annotation languages - Add reqstool-lsp.openrpc.json (OpenRPC 1.2.6) as formal spec for the custom reqstool/details method - Fix details response: return id+urn separately (urn = project URN only, not composite UrnId) - Update nav.adoc to include LSP Server page - Update CLAUDE.md with LSP documentation guidance Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Note: LSP variant changes neededPR #328 removes
Existing |
…314) Extract LSP try/except handling into Command.command_lsp() to bring main() from complexity 13 down to the allowed maximum of 10. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
|
There are some tests that fails |
Move pygls and lsprotocol from optional [lsp] extra into core dependencies so no reqstool[lsp] install is needed. Fix test_definition.py import of renamed public function find_id_in_yaml (was _find_id_in_yaml). Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
|
I suspect that this is due to previous changes and not the LSP stuff per se. I'll handle it as a separate issue (bug) and deal with that when working on #325 #326 #327
|
|
Actually it seems that this could be the cause:
|
Introduce a .reqstoolignore sentinel file convention for the LSP's workspace discovery. When _walk_dir encounters a directory containing .reqstoolignore it skips that directory entirely, preventing test fixture requirements.yml files from being loaded as root projects. Add .reqstoolignore to tests/resources/test_data/ so that the reqstool-client project's own test fixtures are not discovered when the project is opened in VS Code. Fixes: #323 Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
|
Thanks for testing, @Jonas-Werne! I believe that the issue you hit is now fixed. When the reqstool-client project itself is opened in VS Code, the LSP was discovering test fixture The fix introduces a Could you pull the latest |
Jonas-Werne
left a comment
There was a problem hiding this comment.
Tested it out initially I had the same issue noticed that we had missed to add a .reqstoolignore file. Added it and now it works 👍
My mistake. Thanks for the commit. So, we can merge it then. |
|
Now some tests fail, probably because I ignored the regression requirements. |
I'm working on them. |
…ation tests Placing .reqstoolignore inside tests/fixtures/reqstool-regression-python/ caused the LSP integration tests to fail: _walk_dir finds the file at depth=0 (the workspace root) and skips the entire directory, so the regression fixture project is never discovered and all LSP features return 'project not loaded'. Move the file up one level to tests/fixtures/ so it only blocks discovery when the full reqstool-client project is opened as a workspace. When the integration test uses reqstool-regression-python as the workspace root directly, _walk_dir starts at that directory (depth=0, no .reqstoolignore present) and correctly discovers requirements.yml. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
|
@Jonas-Werne Can you try again? Moved the .reqstoolignore one level. |
I'm making an additional change. The |
Patterns (one per line, # comments ignored) are matched with fnmatch against immediate child directory names. An empty or comments-only file has no effect; use ** to skip all children. Update tests/resources/test_data/.reqstoolignore and tests/fixtures/.reqstoolignore to use the explicit ** pattern with an explanatory comment instead of relying on an empty file. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
fe3d291 to
553a98f
Compare
@Jonas-Werne Please try again when you have the time. |
Jonas-Werne
left a comment
There was a problem hiding this comment.
Tried it again, Works as expected


Important
This branch is based on
feat/313-sqlite-storage(#321) but targetsmain. PR #321 must be merged first before this PR can be merged.Summary
Add a Python LSP server to reqstool-client using
pygls, backed by the SQLite pipeline from #313. The server provides editor-agnostic support for reqstool annotations in Java, Python, TypeScript, and JavaScript.Depends on: #321 (SQLite storage)
Implemented features
@Requirements/@SVCsannotations + YAML field descriptions from JSON schemas@Requirements/@SVCsannotations) + TypeScript/JavaScript (JSDoc@Requirements/@SVCstags).reqstoolignore: Place in any directory to exclude it from LSP workspace discovery (ecosystem-agnostic — works for Python, Java, JS/TS projects)Architecture
New CLI command
New files
src/reqstool/lsp/annotation_parser.pysrc/reqstool/lsp/project_state.pybuild_database()pipeline for a single reqstool projectsrc/reqstool/lsp/root_discovery.py.reqstoolignoresrc/reqstool/lsp/workspace_manager.pysrc/reqstool/lsp/server.pysrc/reqstool/lsp/yaml_schema.pysrc/reqstool/lsp/features/hover.pysrc/reqstool/lsp/features/diagnostics.pysrc/reqstool/lsp/features/completion.pysrc/reqstool/lsp/features/definition.pysrc/reqstool/lsp/features/document_symbols.pysrc/reqstool/lsp/features/codelens.pyTest coverage
198 LSP unit tests, 530 total unit tests — all passing.
Closes
Related
Test plan
.reqstoolignorebehaviour)