Skip to content

language server goodies#1242

Merged
sbillig merged 31 commits intoargotorg:masterfrom
micahscopes:lsp-features-feb-2026
Feb 17, 2026
Merged

language server goodies#1242
sbillig merged 31 commits intoargotorg:masterfrom
micahscopes:lsp-features-feb-2026

Conversation

@micahscopes
Copy link
Copy Markdown
Collaborator

@micahscopes micahscopes commented Feb 8, 2026

LSP features and crash fixes

New features

  • Call hierarchy (incoming and outgoing calls)
  • Type hierarchy (supertypes and subtypes)
  • Code lens (reference and implementation counts)
  • Selection range (expand/shrink selection to enclosing syntax)
  • Folding ranges
  • Go to declaration (distinguishes trait method signatures from impl bodies)
  • Codegen viewer (view MIR, Yul, Sonatina IR for a file)
  • Virtual files (builtin core/std libraries materialized as temp files for navigation)
  • LSIF and SCIP index generation (fe lsif, fe scip)

Crash fixes

The reported crash (Sean's SendError during formatting mid-edit) came from three parser panic paths:

  1. Parser scope leak in struct_.rs, error recovery scope entered but never left due to early ? return, corrupting the scope stack and causing
    ast::Root::cast().unwrap() to fail in HIR lowering.
  2. unreachable!() in generic params, error recovery landing on unexpected tokens. Changed to graceful return.
  3. todo!()/unimplemented!() in SyntaxKind::describe(), replaced with string returns.

Added catch_unwind around diagnostics so future parser issues degrade to missing diagnostics instead of crashing.

Other fixes

  • Text sync was reading content_changes[0] instead of .last() in full-sync mode
  • Stale diagnostics for deleted/renamed files were never cleared
  • Per-request logging moved from INFO to DEBUG
  • Out-of-range positions from the client no longer panic
  • Line offset calculation unified with LSIF/SCIP, fixing trailing newline edge cases
  • Folding ranges end on the closing brace line, not the empty line after
  • Channel sends in stream infrastructure return errors instead of panicking
  • showDocument fallback checks success field, not just RPC status

Tests

  • 7 integration tests using a mock LSP client with real server
  • Fuzz test with ~70 malformed inputs
  • Unit tests for each new feature
  • LSIF structural validator and SCIP round-trip tests

Semantic API additions for upcoming LSP features:
- Ingot::all_funcs() collects all functions across modules
- Func::containing_impl() finds parent impl block
- ImplTrait::implementing_adt() resolves implementing struct/enum
- CallSiteView + Body::call_sites() for iterating call expressions
…tion range

Four new LSP features:
- Call hierarchy: incoming/outgoing call navigation
- Type hierarchy: supertype (traits) and subtype (implementors) navigation
- Code lens: reference and implementation counts on symbols
- Selection range: AST-aware expand/shrink selection
Generates LSIF 0.4.0 index for code navigation (GitHub, Sourcegraph).
Emits definitions, references, hover content, and monikers for all
symbols in an ingot. Usage: fe lsif <path> [-o output]
Extract find_incoming_calls/find_outgoing_calls helpers from handlers
for testability. Tests cover prepare, incoming calls, outgoing calls,
and empty cases.
Extract find_supertypes/find_subtypes helpers from handlers. Tests
cover prepare on struct/trait, supertypes from trait impls, subtypes
from trait implementors, and empty cases.
Extract collect_lens_data helper. Tests cover reference counting on
structs/functions, trait implementation counting, and zero-reference
cases.
Extract build_selection_ranges_at helper. Tests verify nested ranges
on function names, inside bodies, nested items (method inside impl),
and cursor outside items.
Pre-emit document vertices for all modules, track via HashMap to
avoid emitting duplicate documents when multiple modules share a
file. Collect range IDs per document for proper contains edges.
Validate LSIF output structure: required fields, edge references,
vertex ordering, metadata, document deduplication, hover content,
range positions, monikers, and contains edges.
Eliminates dead code warnings for collect_lens_data and
build_selection_ranges_at, which are only used by tests.
Folding ranges uses scope graph traversal to fold multi-line
functions, structs, enums, traits, impls, and impl trait blocks.

Go-to-declaration navigates to the concrete declaration site,
complementing goto-definition which redirects impl trait methods
to their trait method declarations.
The LSP spec expects SymbolInformation.name to be just the identifier,
with the kind field indicating the symbol type. Removing prefixes like
"fn " and "struct " enables proper syntax highlighting in editors that
use label_for_symbol to wrap names in highlighted code snippets.
Collapse nested if statements using let-chains, use HashMap entry API
instead of contains_key+insert, iterate map values directly.
Extract the temp-directory + bidirectional URI mapping from BuiltinFiles
into a generic VirtualFiles abstraction. Add codegen output generation
(MIR, Yul, Sonatina IR) exposed via workspace/executeCommand, with code
lenses per file to trigger each view. The generated IR is materialized
into the VFS temp directory and opened in the editor via showDocument.
Adds fn/struct/enum/trait/type/const/mod/contract prefixes back to
symbol names so Zed's symbol browser can syntax-highlight them.
When a fe.toml exists at the repo root, containing_ingot() would claim
all .fe files as part of that ingot, even files like test_files/goto.fe
that aren't under src/. This led to a panic in module_tree_impl when the
ingot had no discoverable source files.

Fix: check that the file is actually under the ingot's src/ directory or
at the ingot root before claiming it. Files outside the source tree now
fall through to standalone ingot detection.

Also add a safety net in module_tree_impl: return an empty module tree
instead of panicking when no source files are found.
The code lenses were using custom fe.showReferences and
fe.showImplementations commands that were never registered, causing
"command not found" when clicked. Switch to the standard
editor.action.showReferences command with actual reference locations
as arguments, which both VS Code and Zed handle natively.
- Remove TracingLayer to fix feedback loop that crashed Zed (every
  window/logMessage notification was re-logged, flooding the client)
- Lower default log level from INFO to WARN for window/logMessage
- Use fe.showReferences instead of editor.action.showReferences so
  the VS Code extension can bridge JSON args to native VS Code types
- Show friendly warning instead of internal error when codegen fails
  due to type errors in the source file
- Add showDocument -> applyEdit fallback for codegen viewer in editors
  that don't support window/showDocument (Zed)
- Fix readonly file overwrite in VirtualFiles for codegen regeneration
@micahscopes micahscopes force-pushed the lsp-features-feb-2026 branch from 95cb3ef to b59b199 Compare February 11, 2026 00:58
Fix three issues causing the language server to get stuck or crash:

- Use content_changes.last() instead of [0] in didChange handler to
  correctly pick up the final file state under FULL sync mode
- Clear stale diagnostics for URIs not covered by the computed
  diagnostics map, preventing phantom errors after fixes
- Wrap mir_diagnostics_for_ingot in catch_unwind to prevent panics
  in MIR analysis (missing spans, invalid IDs) from killing the
  Backend actor and causing SendError on all subsequent requests

Add regression tests covering malformed mid-edit states: truncated
source, empty files, garbage content, partial generics, incomplete
function bodies, and the reported "mself" intermediate edit scenario.
Fix three parser panics that caused the language server to crash during
editing, leaving it in a permanently broken state (all subsequent LSP
requests fail with SendError):

1. parser/struct_.rs: ErrorScope leak when parsing fn inside struct def.
   Manual enter(ErrorScope)/parse(FuncScope)?/leave() pattern skipped
   leave() on error propagation via ?, corrupting the scope stack and
   producing a non-Root green tree. Fix: capture result before ?.

2. parser/param.rs: unreachable!() in GenericParamListScope when
   recovery lands on a list separator (e.g. `struct S<,>`).

3. parser/syntax_kind.rs: todo!() and unimplemented!() in describe()
   for Error and InvalidToken syntax kinds.

Add catch_unwind safety net around diagnostics_for_ingot in the LSP
handler so future panics in analysis passes degrade gracefully instead
of killing the backend actor.

Add mock LSP client test harness (mock_client_tests.rs) with 7
integration tests covering formatting during malformed states, keystroke
sequences, and concurrent diagnostics. Add fuzz test with ~70 malformed
inputs covering Sean's reported crash scenarios.
@micahscopes micahscopes force-pushed the lsp-features-feb-2026 branch from b59b199 to 067b7d1 Compare February 17, 2026 04:15
@micahscopes micahscopes force-pushed the lsp-features-feb-2026 branch from 067b7d1 to 4b9d428 Compare February 17, 2026 04:17
- Bounds-check line index in to_offset_from_position to prevent panics
  on stale document positions from the client
- Clamp byte offset to text length to avoid out-of-range TextSize
- Replace O(n^2) chars().nth().unwrap() with as_bytes() in item_info
  line-start scan, fixing both a performance issue and panic on non-ASCII
- Document type_hierarchy_provider gap in lsp-types 0.95.1
@micahscopes micahscopes marked this pull request as ready for review February 17, 2026 06:17
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 993752767f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/language-server/src/functionality/codegen_view.rs Outdated
Comment thread crates/language-server/src/functionality/call_hierarchy.rs
- Replace unreachable!() with warning on unknown FileChangeType
- Replace unwrap() on channel sends in lsp_streams with error
  propagation so a dropped receiver returns a proper LSP error
  instead of crashing
- Convert expect() to fallible ? in to_lsp_location_from_span so
  files without URLs don't crash the diagnostics path
- Unify calculate_line_offsets with the LSIF/SCIP byte-scanning
  implementation, fixing edge cases with trailing newlines
- Adjust folding range end line when spans include trailing newlines,
  so folds end on the closing brace line instead of the line after
…y gap

- Check the success field in the showDocument response, not just
  whether the RPC succeeded. Clients that return success:false now
  correctly fall through to the workspace/applyEdit path.
- Document that incoming call hierarchy doesn't yet scan contract
  init/recv bodies.
Copy link
Copy Markdown
Collaborator

@sbillig sbillig left a comment

Choose a reason for hiding this comment

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

👯

@sbillig sbillig merged commit 5b53677 into argotorg:master Feb 17, 2026
5 checks passed
@sbillig sbillig mentioned this pull request Feb 27, 2026
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.

2 participants