Skip to content

editor: Fix LSP extension commands failing at end of file#52651

Merged
SomeoneToIgnore merged 3 commits intozed-industries:mainfrom
florian-trehaut:troubleshoot/switch-source-header-2026-03-29
Mar 30, 2026
Merged

editor: Fix LSP extension commands failing at end of file#52651
SomeoneToIgnore merged 3 commits intozed-industries:mainfrom
florian-trehaut:troubleshoot/switch-source-header-2026-03-29

Conversation

@florian-trehaut
Copy link
Copy Markdown
Contributor

@florian-trehaut florian-trehaut commented Mar 29, 2026

Context

Closes #51330

When the cursor is at the very end of a file, find_specific_language_server_in_selection in crates/editor/src/lsp_ext.rs silently skips the selection because the anchor's buffer_id is None. This causes editor: switch source header (clangd) and rust-analyzer extension commands (expand macro, open docs, open playground) to do nothing.

The fix falls back to the singleton buffer's ID when the anchor has no buffer_id.

How to Review

Single file change in crates/editor/src/lsp_ext.rs. The diff is small — pre-compute the singleton buffer ID, then use it as fallback in the filter_map closure. An integration test verifies the fix.

Self-Review Checklist

  • Reviewed own diff for quality, security, and reliability
  • No unsafe blocks
  • Tests pass (639 editor tests, 0 failures)
  • Manual testing: switch source header works at beginning, middle, and end of C++ file

Test Plan

  • New integration test: test_find_language_server_at_end_of_file — verifies find_specific_language_server_in_selection returns Some at both beginning and end of file
  • Confirmed the test fails without the fix (assertion on "should find language server at end of file")
  • Manual: open a C++ file with clangd, place cursor at very end, run switch source header — now correctly opens the header
  • Manual: verify it still works at beginning and middle of file (non-regression)
  • cargo test -p editor — 639 passed, 0 failed

Release Notes:

  • Fixed editor: switch source header and other LSP extension commands not working when the cursor is at the very end of a file.

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Mar 29, 2026
@zed-codeowner-coordinator zed-codeowner-coordinator bot requested a review from a team March 29, 2026 08:11
@zed-community-bot zed-community-bot bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Mar 29, 2026
@zed-codeowner-coordinator zed-codeowner-coordinator bot requested review from SomeoneToIgnore and as-cii and removed request for a team March 29, 2026 08:11
When the cursor is at the very end of a file, the selection anchor's
`buffer_id` can be `None`, causing `find_specific_language_server_in_selection`
to silently skip the selection. This affects `switch source header` (clangd)
and rust-analyzer commands (expand macro, open docs, open playground).

Fall back to the singleton buffer's ID when the anchor has no `buffer_id`.

Closes zed-industries#51330

Release Notes:

- Fixed `editor: switch source header` and other LSP extension commands not
  working when the cursor is at the very end of a file.
@florian-trehaut florian-trehaut force-pushed the troubleshoot/switch-source-header-2026-03-29 branch from b150dae to 71f3593 Compare March 29, 2026 08:42
Copy link
Copy Markdown
Contributor

@SomeoneToIgnore SomeoneToIgnore left a comment

Choose a reason for hiding this comment

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

Thank you for the fix, this is a very nice catch overall meaning there's a lot of places like this elsewhere.
The most important one most probably is the diagnostics-related code as there were reports of diagnostics not shown on the very edge of the file?

Either way, let's adjust the code for a proper multi buffer coordinate contract find_specific_language_server_in_selection exposes and merge this.

@florian-trehaut florian-trehaut marked this pull request as draft March 30, 2026 09:31
Use MultiBufferSnapshot::buffer_id_for_anchor to resolve buffer IDs,
which properly handles anchors with None buffer_id through
excerpt_containing. This replaces the custom resolve_anchor_to_buffer
helper with the existing multi-buffer API.

Merge all filtering logic into a single find_map to avoid borrow
checker issues with cx, using a scoped block to drop the multi-buffer
borrow before calling buffer.update.

Strengthen test assertions to verify the specific server ID, language,
buffer identity, and file presence.
@florian-trehaut florian-trehaut force-pushed the troubleshoot/switch-source-header-2026-03-29 branch from f4607eb to 92061af Compare March 30, 2026 10:06
@florian-trehaut florian-trehaut marked this pull request as ready for review March 30, 2026 10:06
@zed-codeowner-coordinator zed-codeowner-coordinator bot requested review from a team and Veykril and removed request for a team March 30, 2026 10:06
@florian-trehaut
Copy link
Copy Markdown
Contributor Author

Thanks for taking the time to review this! I've addressed all the feedback:

  • Replaced resolve_anchor_to_buffer with MultiBufferSnapshot::buffer_id_for_anchor, which already handles buffer_id: None through excerpt_containing internally
  • Merged everything into a single find_map with a scoped block to avoid borrow checker issues with cx
  • Kept the tail fallback
  • Strengthened test assertions: server ID, language, buffer identity, file presence, all in a shared closure

About the diagnostics code you mentioned, I'd be happy to look into that as a follow-up if you can point me to the relevant area.

Copy link
Copy Markdown
Contributor

@SomeoneToIgnore SomeoneToIgnore left a comment

Choose a reason for hiding this comment

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

Thank you, I have pushed another small fix on top to avoid extra snapshot-related creation costs.


I've rechecked the diagnostics-related idea of mine and that does not seem related to this particular issue: #22473

Yet, I think if you check for text_anchor.buffer_id field usages, some of them may come out wrong, e.g.

if Some(buffer_id) != cursor.text_anchor.buffer_id {
return None;
}
let buffer = self.buffer.read(cx).buffer(buffer_id)?;

or even

pub fn text_anchor_for_position<T: ToOffset>(
&self,
position: T,
cx: &App,
) -> Option<(Entity<Buffer>, language::Anchor)> {
let snapshot = self.read(cx);
let anchor = snapshot.anchor_before(position);
let buffer = self
.buffers
.get(&anchor.text_anchor.buffer_id?)?
.buffer
.clone();
Some((buffer, anchor.text_anchor))
}

are quite suspicious already.

@SomeoneToIgnore SomeoneToIgnore enabled auto-merge (squash) March 30, 2026 11:11
@SomeoneToIgnore SomeoneToIgnore merged commit 2b4c217 into zed-industries:main Mar 30, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The "editor: switch source header" command does not work when the cursor is positioned at the very end of the file

4 participants