fix(lsp): resolve links from anywhere in the link, including the title
CI / cargo fmt --check (push) Successful in 1m1s
CI / cargo clippy (push) Successful in 1m6s
CI / cargo test (push) Successful in 1m16s
CI / editor keymaps (push) Successful in 1m41s

goto-definition (and references/hover) reported "No locations found" when
the cursor was on the *description* of a piped link — `[[target|Title]]`
or `[desc](url)` — and only worked over the target/location part.

find_in_inlines descended into the link's description and returned its
inner Text node, so the nav handlers saw a Text (not a WikiLink/
ExternalLink) and bailed. A link is an atomic unit for navigation: stop
descending into descriptions so a hit anywhere in the link returns the
link node itself. Adds a navigation regression test for cursor-on-title.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:29:41 +00:00
parent 3e8c534859
commit fcce5621a0
2 changed files with 17 additions and 8 deletions
+5 -8
View File
@@ -106,14 +106,11 @@ fn find_in_inlines(inlines: &[InlineNode], line: u32, col: u32) -> Option<&Inlin
InlineNode::Superscript(s) => find_in_inlines(&s.children, line, col), InlineNode::Superscript(s) => find_in_inlines(&s.children, line, col),
InlineNode::Subscript(s) => find_in_inlines(&s.children, line, col), InlineNode::Subscript(s) => find_in_inlines(&s.children, line, col),
InlineNode::Color(c) => find_in_inlines(&c.children, line, col), InlineNode::Color(c) => find_in_inlines(&c.children, line, col),
InlineNode::WikiLink(w) => w // Links are an atomic unit for navigation: a cursor anywhere in
.description // `[[target|description]]` (or `[desc](url)`) should resolve the
.as_ref() // link itself. Don't descend into the description — returning its
.and_then(|d| find_in_inlines(d, line, col)), // inner Text node leaves goto-definition/references/hover with
InlineNode::ExternalLink(e) => e // nothing to act on ("No locations found" when on the title).
.description
.as_ref()
.and_then(|d| find_in_inlines(d, line, col)),
_ => None, _ => None,
}; };
return Some(deeper.unwrap_or(n)); return Some(deeper.unwrap_or(n));
+12
View File
@@ -201,6 +201,18 @@ fn find_inline_at_returns_wikilink_under_cursor() {
assert!(matches!(node, InlineNode::WikiLink(_))); assert!(matches!(node, InlineNode::WikiLink(_)));
} }
#[test]
fn find_inline_at_returns_wikilink_when_cursor_on_description() {
// Regression: pressing <CR> on the *title* part of a piped link used to
// descend into the description's Text node, so goto-definition saw a Text
// (not a WikiLink) and reported "No locations found".
let src = "see [[Other|My Title]] now\n";
let doc = parse(src);
// Byte col 16 is inside "Title" (the description), past the '|' at 11.
let node = find_inline_at(&doc, 0, 16).expect("wikilink at cursor");
assert!(matches!(node, InlineNode::WikiLink(_)));
}
#[test] #[test]
fn find_inline_at_returns_none_for_plain_text() { fn find_inline_at_returns_none_for_plain_text() {
let src = "just text\n"; let src = "just text\n";