Skip to content

Fix LSP issues found by the new tests - #106

Merged
Rigidity merged 4 commits into
mainfrom
lsp-test-followups
Oct 12, 2026
Merged

Rigidity merged 4 commits into
mainfrom
lsp-test-followups

Conversation

@Rigidity

@Rigidity Rigidity commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #105. This fixes the behaviour that #105's snapshots recorded but left alone. Each fix is a separate commit, and the snapshot diffs show exactly what changed.

Changes

UTF-16 positions (2f76b93)

  • LSP positions count characters in UTF-16 code units. The server treated them as character counts and then used the result as a byte offset, so hover, definitions and diagnostics were off after any non-ASCII text on the line.
  • Added position.rs, which converts between byte offsets and LSP positions in both directions. It's used for incoming cursor positions, locations and diagnostics.
  • LineCol::new now treats its index as a byte offset, which is what callers pass. This also corrects CLI error columns after non-ASCII text.

Declaration keywords and builtin signatures (4e504af)

  • Hover now shows fn name<T>(params) -> Ret, const NAME: T and name: T for parameters, and adds inline where it applies. Previously everything was shown as let.
  • Builtins have no function type, so completions listed them as {unresolved}. Builtin::signatures() in rue-hir now gives a hand-written signature for each way a builtin can be called, matching the rules in compile_builtin. Hover lists all of them; the completion detail shows the first.

Generic arguments (f5957af)

  • substitute copied aliases and structs but kept their declared generic parameters, so List<Int> was printed as List. The generics are now substituted along with the inner type, and stringify includes them for named aliases and structs.
  • Arguments of an Apply type are now printed in the order of their parameters. Previously they followed HashMap order.
  • Hover on a declaration shows its generic parameters, e.g. type List<T> = nil | (T, List<T>) and struct Wrapper<T> { ... }.

Completion in empty type positions (b644e2c)

  • Completion previously suggested nothing until at least one character of the type had been typed. It now checks the token before the cursor in the syntax tree. Colons in parameters, let, const and struct fields count as type positions, as do ->, as, is, and the = of a type alias.
  • A colon in a struct initializer is not a type position, so no types are suggested there. It still suggests nothing at all, and expressions should really be offered (marked with a TODO).

Test plan

  • cargo test --workspace --all-features: 75 LSP tests, up from 53, plus new position and LineCol unit tests. The compiler's YAML snapshots are unchanged.
  • cargo clippy --workspace --all-features --all-targets (no warnings), cargo fmt --check, cargo machete, rue fmt --check

Note

Medium Risk
Touches position math used by all LSP features and type substitution/stringify shared beyond the server; regressions could misalign diagnostics or change displayed types.

Overview
Fixes several Rue LSP behaviors that broke on non-ASCII text, empty type positions, and richer type/symbol display.

UTF-16 positions: Adds position.rs to convert between byte offsets and LSP positions (UTF-16 code units). Hover, go-to-definition, references, diagnostics, and cursor mapping now use it. LineCol treats indices as byte offsets and counts columns in Unicode characters, aligning CLI columns with the same model.

Hover and completions: Hover distinguishes bindings (let), parameters, const/inline const, and fn/inline fn with full signatures (generics, spread params). Builtin::signatures() supplies completion details and multi-signature hover for builtins instead of {unresolved}.

Generic types: substitute now substitutes generic parameters on structs/aliases; stringify prints Name<Args> in parameter order for Apply types. Hover shows generic declarations (e.g. List<T>, Wrapper<T>).

Completions: When no type AST exists yet, syntax-aware expects_type offers types after ->, as, is, annotated colons, and type-alias =—but not struct initializer values.

Reviewed by Cursor Bugbot for commit f3ecb6c. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b644e2c. Configure here.

SymbolHoverKind::Binding {
type_name: self.type_name(scopes, self.symbol_type(scopes, symbol)),
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline let hover drops keyword

Low Severity

SymbolHoverKind::Binding has no inline flag, and symbol_hover_kind ignores BindingSymbol.inline. Hover therefore always renders let, even when the binding was declared inline let. Constants and functions in this change do surface inline.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b644e2c. Configure here.

@Rigidity
Rigidity added this pull request to stack #107 October 12, 2026 22:56
Base automatically changed from lsp-tests to main October 12, 2026 22:57
LSP positions count characters in UTF-16 code units, but the server treated
them as character counts and then used the result as a byte offset. Convert
between byte offsets and LSP positions properly, and make LineCol::new treat
its index as a byte offset, which is what callers pass.
Hover now renders functions as `fn name<T>(params) -> Ret`, constants as
`const`, parameters without `let`, and marks inline items. Builtins have
no function type, so they showed up as {unresolved}; add hand written
signatures for each way a builtin can be called and use them for hover
and completion details.
Substituting an alias or struct kept its declared generic parameters, so
List<Int> was displayed as List. Substitute the generics along with the
inner type, include them when stringifying named aliases and structs, and
order applied arguments by their parameters instead of HashMap order.

The LSP now uses this for type names, and hover shows the generic
parameters of declarations, e.g. `type List<T> = ...`.
Type completions came from the completion context of a type node, so
nothing was suggested before anything had been typed, e.g. after
`value: ` or `-> `. Look at the token before the cursor instead, and
treat colons in parameters, let statements, constants and struct fields,
return arrows, casts, guards and type alias definitions as type positions.
@Rigidity
Rigidity merged commit 245c107 into main Oct 12, 2026
2 checks passed
@Rigidity
Rigidity deleted the lsp-test-followups branch October 12, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant