Draft
Conversation
This adds the first full LSP implementation for jrsonnet. It introduces a new `jrsonnet-lsp` binary and a dedicated crate stack for document management, scope resolution, import graphing, type inference, diagnostics/checking, request handlers, and server orchestration. Implemented features include incremental sync, definition, hover, completion, references, rename, inlay hints, code lens, semantic tokens, formatting, workspace symbols, async request execution, and async diagnostics with dependency-aware cache invalidation. Type inference now includes flow typing driven by stdlib predicates. Flow typing means we narrow a variable's type inside control-flow branches based on checks such as `std.isString(x)` and `std.isNumber(x)`. Example: ```jsonnet if std.isString(x) then x + "!" else x ``` In the `then` branch, `x` is narrowed to `string`. Example: ```jsonnet if std.isNumber(x) then x + 1 else x ``` In the `then` branch, `x` is narrowed to `number`. Predicates marked partial (for example `std.isInteger`) only narrow the `true` branch; the `false` branch is not treated as the strict complement. There were some changes to other crates required: - The AST now models field/index/slice/call as explicit expression nodes (`ExprField`, `ExprIndex`, `ExprSlice`, `ExprCall`), giving stable node boundaries and ranges used by hover, go-to-definition, completion, and semantic token classification. - The Rowan parser now exposes the Rowan green-tree types and richer syntax error types, allowing parse results/errors to be reused safely across async diagnostics and structural tests. - `jrsonnet-fmt` is refactored into context/macros/printable modules, with explicit style options and snapshot coverage, so `textDocument/formatting`, which calls this, can be deterministic and regression-tested. - Evaluator/Tanka/import resolution plumbing is updated so eval diagnostics and execute-command evaluation follow the same path-resolution behavior as the language server. - Workspace dependency and compatibility updates (`lsp-types` bump, lockfile refresh, and required clippy/FFI cleanups in touched crates) keep the expanded workspace compiling cleanly under the stricter lint profile used for this work. LSP documentation is added under `docs/lsp/`.`
Add textDocument/didSave handling in the server and advertise save support in textDocumentSync options. On save, refresh document state (when text is provided), invalidate dependent type cache entries, update import graph state, and reschedule diagnostics for the saved file and open importers. Add integration coverage for save-triggered diagnostics refresh and update architecture docs to describe the didSave path.
Wire into async command execution with argument parsing for URI, position, and optional includeDeclaration. Reuse the existing references path and return LSP Location arrays to command callers. Add integration coverage for command behavior (with and without declarations) and update LSP docs to mark the command as implemented.
Advertise range support in semantic token capabilities, route textDocument/semanticTokens/range requests in the server, and add a range-aware token generation path in handlers. Add unit and integration tests for range filtering and update LSP docs to reflect full + range semantic token support.
- finalize each TypeAnalysis by merging its LocalTyStore into the shared GlobalTyStore and remapping all recorded expression/document types before caching or reuse. - introduce GlobalTy in jrsonnet-lsp-types and switch type-cache/import-resolver boundaries to use it, so cross-file APIs cannot accept local Ty IDs at compile time. - harden TySubst::merge for cyclic local types by lowering unresolved local refs to Any during merge, preventing invalid global entries. - update cross-file cache tests/bench wiring for GlobalTy and add cycle regression coverage in subst tests.
- advertise and route textDocument/implementation in the server capability and request dispatcher. - implement async go-to-implementation resolution for local bindings and import-backed fields, with separate declaration vs implementation ranges when available. - load unopened imported files on demand so implementation lookup can traverse import chains without requiring all files to be open. - add integration coverage for local and import-field implementation lookups, plus regression coverage for import diagnostics + definition stability. - document implementation-provider behavior and handler flow in docs/lsp architecture/handlers docs.
- make URI handling fallible and propagate/skip invalid paths in request and diagnostics flows - replace panic-prone indexing/slicing with checked access across handlers, inference, check, scope, import, and type-store code - remove remaining panic-style server dispatch fallthroughs and simplify error handling paths - tighten type-store mutation paths (object merge/narrow, union/sum simplification, safe deref fallbacks) - keep behavior stable with targeted test runs across lsp-document, lsp-types, lsp-import, lsp-scope, lsp-inference, lsp-check, and lsp-handlers
- route definition/declaration/implementation through explicit target-based resolution - keep declaration semantics aligned with definition for declaration sites while preserving distinct implementation jumps - strengthen integration coverage with structural assertions and declaration checks for import-field navigation - update handler docs to describe dedicated declaration dispatch
Introduce distinct goto paths so declaration returns the nearest lexical binder while definition follows local alias chains to canonical origins. - add handler-level declaration vs definition modes and alias-chain resolution for local bindings/import aliases - route server declaration/implementation through lexical declaration handling, while keeping implementation expression jumps intact - add structural integration coverage for divergence cases (local alias and import-field alias)
Rewrite declaration/definition/implementation guidance in clearer language with practical intent and worked examples. - explain when each request should be used - add concrete alias/import examples with expected jump targets - align architecture summary wording with handler-level behavior
Cross-file reference search no longer requires the cursor to sit on the top-level definition token. When the cursor is on a local reference, we now resolve its definition first and treat it as cross-file-exportable if that definition is file-scope. This preserves existing behavior for definitions while making references from local usage sites return importer hits as expected.
Fix the failing type-at-position test for computed object field names by addressing the inference gap instead of weakening the assertion. Object inference previously skipped dynamic field-name expressions entirely, so no type information was recorded for tokens inside `[expr]` field keys. This caused `type_at_position` to fall back to less-specific ancestor types. The object inference passes now: - mark objects with dynamic fields as open (`has_unknown = true`) - infer dynamic key expressions in pass 2 so their expression ranges are recorded in analysis - preserve unknown-field state when merging super objects Also adds a regression test that dynamic-field objects are open. Together with the existing query test, this verifies the root cause and the behavior end-to-end.
Split expression inference implementation out of `expr/mod.rs` so the module file only declares submodules and re-exports public/internal entry points. The implementation previously lived directly in `mod.rs`, which made it harder to navigate and expanded diff noise for unrelated changes. This moves the core inference logic into `expr/core.rs` and leaves `mod.rs` as import/export wiring only. No behavior changes are intended. The refactor keeps existing visibility boundaries by re-exporting crate-internal items used by sibling modules and preserving external APIs. Also updates one test import in `expr/advanced.rs` to match how the test module references `Document` after the split.
Extend inlay hint rendering so comprehension bindings can emit per-name type hints when the binding uses destructuring. Previously, the comprehension hint path only handled simple bindings (`for x in ...`). Destructured forms like `for [a, b] in ...` produced no binding hints even when the inferred element type was known. The comprehension pipeline now reuses the existing destructuring hint walker with the inferred iterator element type. This keeps behavior consistent with local/object-local destructuring hints and supports nested array/object patterns. This remains non-intrusive by default. Destructured comprehension hints appear only when `inlayHints.comprehensions = "all"` and `inlayHints.destructuring = "all"` are both enabled. Also adds unit coverage for both the enabled case and the config-gated disabled case, and documents the category interaction in `docs/lsp/README.md`.
Add focused end-to-end scenarios for inlay hints so configuration and category interactions are validated through the scenario runner, not only unit-level handlers. New coverage includes: - local/object-local mode filters (`all`, `variables`, `functions`) - comprehension + destructuring gating for destructured `for` bindings - function-parameter hints combined with call-argument hints, including named-argument skip behavior Each fixture keeps one concern per scenario so failures are easy to triage and configuration regressions are easier to localize.
Make formatting request options first-class across formatter, LSP, and scenario harness paths. What changed: - map request options for indentation (`insertSpaces`/`tabSize`) and trimming flags through the LSP formatting request path - move trailing-whitespace and final-newline trimming behavior into `jrsonnet-fmt` options so normalization lives in the formatter - keep `insertFinalNewline` handling at the LSP layer as a final request-specific post-step Also expands test coverage: - async formatting option unit tests in LSP - integration tests for optional formatting request flags - scenario runner support for formatting request option fields - new scenario fixtures covering option combinations, files with trailing input newlines, and tabs/spaces/tabSize behavior README formatting docs now describe these request option effects.
Drop README language that promised how the server applies `FormattingOptions` on formatting requests. This was over-specific for user-facing docs and implied a behavioral contract users should not need spelled out. The section now keeps the configuration field reference and removes per-request option mapping details.
Add end-to-end range formatting support across formatter, handlers, server wiring, and scenario/integration test harnesses. Core behavior: - add `jrsonnet-fmt::format_code_range` and `ByteRangeEdit` so range edit computation lives in the formatter crate - compute a minimal changed byte span from full formatted output and return edits only when changes are fully contained in the requested range - keep full-document formatting behavior unchanged LSP wiring: - advertise `documentRangeFormattingProvider` - route `textDocument/rangeFormatting` through request dispatch and async handler plumbing - apply the same request option merge path used by document formatting (`insertSpaces`/`tabSize`, trimming options) Scenario and test coverage: - extend scenario DSL with `requestRangeFormatting` and `expectRangeFormatting` - add focused runner fixtures for: - edits contained in requested range - no-op when formatter changes escape requested range - request option behavior for tabs/spaces and tab size - add integration tests for range formatting responses and lifecycle capability assertions - update missing-step coverage scenario to include range formatting Docs: - update architecture/handlers docs to include range formatting request routing and capability coverage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a language server to Rustanka. It supports all the things: typing, formatting, diagnostics, code actions, hovering, renaming, references, all the stuff.
I would say it is currently a little bit buggy. But do have a play with it:
cargo build --release --bin jrsonnet-lspand read the README for info on the config options. 😁