Fix Cmd+click navigating to file instead of definition (#49012)

## Summary

- Tighten `link_pattern_file_candidates` regex from `\(([^)]*)\)` to
`]\(([^)]*)\)` so only Markdown link syntax `[title](path)` triggers
path extraction from parentheses
- Prevents function call arguments like `do_work(file2)` from being
incorrectly resolved as file paths, which preempted LSP go-to-definition

Closes #48938

## Test plan

- [x] `cargo test -p editor hover_links` — all 12 tests pass
- [x] New unit tests verify: function calls don't extract arguments as
file candidates; Markdown links still extract correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Release Notes:

- Fixed Cmd+click navigating to file instead of definition in certain
cases

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Wuji Chen 2026-02-27 19:28:34 +08:00 committed by GitHub
parent 0fad478607
commit 96abd034a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -673,7 +673,7 @@ pub(crate) async fn find_file(
// (literally, [LinkTitle](link_file.txt)) as a candidate.
fn link_pattern_file_candidates(candidate: &str) -> Vec<(String, Range<usize>)> {
static MD_LINK_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"\(([^)]*)\)").expect("Failed to create REGEX"));
LazyLock::new(|| Regex::new(r"]\(([^)]*)\)").expect("Failed to create REGEX"));
let candidate_len = candidate.len();
@ -1444,14 +1444,26 @@ mod tests {
candidates,
vec!["LinkTitle](link\\ _file.txt)", "link\\ _file.txt",]
);
//
// Square brackets not strictly necessary
// Parentheses without preceding `]` should not extract inner content,
// to avoid matching function calls like `do_work(file2)` as file paths.
let candidates: Vec<String> = link_pattern_file_candidates("(link_file.txt)")
.into_iter()
.map(|(c, _)| c)
.collect();
assert_eq!(candidates, vec!["(link_file.txt)"]);
assert_eq!(candidates, vec!["(link_file.txt)", "link_file.txt",]);
let candidates: Vec<String> = link_pattern_file_candidates("do_work(file2);")
.into_iter()
.map(|(c, _)| c)
.collect();
assert_eq!(candidates, vec!["do_work(file2);"]);
// Markdown links should still extract the path
let candidates: Vec<String> = link_pattern_file_candidates("](readme.md)")
.into_iter()
.map(|(c, _)| c)
.collect();
assert_eq!(candidates, vec!["](readme.md)", "readme.md"]);
// No nesting
let candidates: Vec<String> =