Fix crash when LSP returns multiple nested definition ranges (#52701)
This fixes a crash when navigating to multiple definitions in a singleton buffer (https://github.com/zed-industries/zed/pull/51461). Release Notes: - N/A
This commit is contained in:
1 parent
326752f12a
commit
577c9909fa
2 files changed
+63
No files matched your search
@@ -18219,6 +18219,20 @@ impl Editor {
|
||||
for ranges in locations.values_mut() {
|
||||
ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
ranges.dedup();
|
||||
// Merge overlapping or contained ranges. After sorting by
|
||||
// (start, Reverse(end)), we can merge in a single pass:
|
||||
// if the next range starts before the current one ends,
|
||||
// extend the current range's end if needed.
|
||||
let mut i = 0;
|
||||
while i + 1 < ranges.len() {
|
||||
if ranges[i + 1].start <= ranges[i].end {
|
||||
let merged_end = ranges[i].end.max(ranges[i + 1].end);
|
||||
ranges[i].end = merged_end;
|
||||
ranges.remove(i + 1);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
let fits_in_one_excerpt = ranges
|
||||
.iter()
|
||||
.tuple_windows()
|
||||
|
||||
@@ -25403,6 +25403,55 @@ async fn test_goto_definition_far_ranges_open_multibuffer(cx: &mut TestAppContex
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_goto_definition_contained_ranges(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
definition_provider: Some(lsp::OneOf::Left(true)),
|
||||
..lsp::ServerCapabilities::default()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
// The LSP returns two single-line definitions on the same row where one
|
||||
// range contains the other. Both are on the same line so the
|
||||
// `fits_in_one_excerpt` check won't underflow, and the code reaches
|
||||
// `change_selections`.
|
||||
cx.set_state(
|
||||
&r#"fn caller() {
|
||||
let _ = ˇtarget();
|
||||
}
|
||||
fn target_outer() { fn target_inner() {} }
|
||||
"#
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
// Return two definitions on the same line: an outer range covering the
|
||||
// whole line and an inner range for just the inner function name.
|
||||
cx.set_request_handler::<lsp::request::GotoDefinition, _, _>(move |url, _, _| async move {
|
||||
Ok(Some(lsp::GotoDefinitionResponse::Array(vec![
|
||||
// Inner range: just "target_inner" (cols 23..35)
|
||||
lsp::Location {
|
||||
uri: url.clone(),
|
||||
range: lsp::Range::new(lsp::Position::new(3, 23), lsp::Position::new(3, 35)),
|
||||
},
|
||||
// Outer range: the whole line (cols 0..48)
|
||||
lsp::Location {
|
||||
uri: url,
|
||||
range: lsp::Range::new(lsp::Position::new(3, 0), lsp::Position::new(3, 48)),
|
||||
},
|
||||
])))
|
||||
});
|
||||
|
||||
let navigated = cx
|
||||
.update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
|
||||
.await
|
||||
.expect("Failed to navigate to definitions");
|
||||
assert_eq!(navigated, Navigated::Yes);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_find_all_references_editor_reuse(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
Reference in new issue
Block a user