Skip to content

Commit 8d15ec7

Browse files
authored
vim: Fix mini delimiters in multibuffer (#41834)
- Update `vim::object::find_mini_delimiters` in order to filter out the ranges before calling `vim::object::cover_or_next`, ensuring that the provided ranges are converted from multibuffer space into buffer space. - Remove the `range_filter` from `vim::object::cover_or_next` was the `find_mini_delimiters` function is the only caller and no longer uses it Closes #41346 Release Notes: - Fixed a crash that could occur when using `vim::MiniQuotes` and `vim::MiniBrackets` in a multibuffer
1 parent ca5a4dc commit 8d15ec7

File tree

1 file changed

+98
-11
lines changed

1 file changed

+98
-11
lines changed

crates/vim/src/object.rs

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
124124
candidates: Option<I>,
125125
caret: DisplayPoint,
126126
map: &DisplaySnapshot,
127-
range_filter: Option<&dyn Fn(Range<usize>, Range<usize>) -> bool>,
128127
) -> Option<CandidateWithRanges> {
129128
let caret_offset = caret.to_offset(map, Bias::Left);
130129
let mut covering = vec![];
@@ -135,11 +134,6 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
135134
for (open_range, close_range) in ranges {
136135
let start_off = open_range.start;
137136
let end_off = close_range.end;
138-
if let Some(range_filter) = range_filter
139-
&& !range_filter(open_range.clone(), close_range.clone())
140-
{
141-
continue;
142-
}
143137
let candidate = CandidateWithRanges {
144138
candidate: CandidateRange {
145139
start: start_off.to_display_point(map),
@@ -214,16 +208,35 @@ fn find_mini_delimiters(
214208
let visible_line_range = get_visible_line_range(&line_range);
215209

216210
let snapshot = &map.buffer_snapshot();
217-
let excerpt = snapshot.excerpt_containing(offset..offset)?;
211+
let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
218212
let buffer = excerpt.buffer();
213+
let buffer_offset = excerpt.map_offset_to_buffer(offset);
219214

220215
let bracket_filter = |open: Range<usize>, close: Range<usize>| {
221216
is_valid_delimiter(buffer, open.start, close.start)
222217
};
223218

224219
// Try to find delimiters in visible range first
225-
let ranges = map.buffer_snapshot().bracket_ranges(visible_line_range);
226-
if let Some(candidate) = cover_or_next(ranges, display_point, map, Some(&bracket_filter)) {
220+
let ranges = map
221+
.buffer_snapshot()
222+
.bracket_ranges(visible_line_range)
223+
.map(|ranges| {
224+
ranges.filter_map(move |(open, close)| {
225+
// Convert the ranges from multibuffer space to buffer space as
226+
// that is what `is_valid_delimiter` expects, otherwise it might
227+
// panic as the values might be out of bounds.
228+
let buffer_open = excerpt.map_range_to_buffer(open.clone());
229+
let buffer_close = excerpt.map_range_to_buffer(close.clone());
230+
231+
if is_valid_delimiter(buffer, buffer_open.start, buffer_close.start) {
232+
Some((open, close))
233+
} else {
234+
None
235+
}
236+
})
237+
});
238+
239+
if let Some(candidate) = cover_or_next(ranges, display_point, map) {
227240
return Some(
228241
DelimiterRange {
229242
open: candidate.open_range,
@@ -234,8 +247,8 @@ fn find_mini_delimiters(
234247
}
235248

236249
// Fall back to innermost enclosing brackets
237-
let (open_bracket, close_bracket) =
238-
buffer.innermost_enclosing_bracket_ranges(offset..offset, Some(&bracket_filter))?;
250+
let (open_bracket, close_bracket) = buffer
251+
.innermost_enclosing_bracket_ranges(buffer_offset..buffer_offset, Some(&bracket_filter))?;
239252

240253
Some(
241254
DelimiterRange {
@@ -1736,8 +1749,10 @@ pub fn surrounding_markers(
17361749

17371750
#[cfg(test)]
17381751
mod test {
1752+
use editor::{Editor, EditorMode, MultiBuffer, test::editor_test_context::EditorTestContext};
17391753
use gpui::KeyBinding;
17401754
use indoc::indoc;
1755+
use text::Point;
17411756

17421757
use crate::{
17431758
object::{AnyBrackets, AnyQuotes, MiniBrackets},
@@ -3185,6 +3200,78 @@ mod test {
31853200
}
31863201
}
31873202

3203+
#[gpui::test]
3204+
async fn test_minibrackets_multibuffer(cx: &mut gpui::TestAppContext) {
3205+
// Initialize test context with the TypeScript language loaded, so we
3206+
// can actually get brackets definition.
3207+
let mut cx = VimTestContext::new(cx, true).await;
3208+
3209+
// Update `b` to `MiniBrackets` so we can later use it when simulating
3210+
// keystrokes.
3211+
cx.update(|_, cx| {
3212+
cx.bind_keys([KeyBinding::new("b", MiniBrackets, None)]);
3213+
});
3214+
3215+
let (editor, cx) = cx.add_window_view(|window, cx| {
3216+
let multi_buffer = MultiBuffer::build_multi(
3217+
[
3218+
("111\n222\n333\n444\n", vec![Point::row_range(0..2)]),
3219+
("111\na {bracket} example\n", vec![Point::row_range(0..2)]),
3220+
],
3221+
cx,
3222+
);
3223+
3224+
// In order for the brackets to actually be found, we need to update
3225+
// the language used for the second buffer. This is something that
3226+
// is handled automatically when simply using `VimTestContext::new`
3227+
// but, since this is being set manually, the language isn't
3228+
// automatically set.
3229+
let editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
3230+
let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
3231+
if let Some(buffer) = multi_buffer.read(cx).buffer(buffer_ids[1]) {
3232+
buffer.update(cx, |buffer, cx| {
3233+
buffer.set_language(Some(language::rust_lang()), cx);
3234+
})
3235+
};
3236+
3237+
editor
3238+
});
3239+
3240+
let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
3241+
3242+
cx.assert_excerpts_with_selections(indoc! {"
3243+
[EXCERPT]
3244+
ˇ111
3245+
222
3246+
[EXCERPT]
3247+
111
3248+
a {bracket} example
3249+
"
3250+
});
3251+
3252+
cx.simulate_keystrokes("j j j j f r");
3253+
cx.assert_excerpts_with_selections(indoc! {"
3254+
[EXCERPT]
3255+
111
3256+
222
3257+
[EXCERPT]
3258+
111
3259+
a {bˇracket} example
3260+
"
3261+
});
3262+
3263+
cx.simulate_keystrokes("d i b");
3264+
cx.assert_excerpts_with_selections(indoc! {"
3265+
[EXCERPT]
3266+
111
3267+
222
3268+
[EXCERPT]
3269+
111
3270+
a {ˇ} example
3271+
"
3272+
});
3273+
}
3274+
31883275
#[gpui::test]
31893276
async fn test_minibrackets_trailing_space(cx: &mut gpui::TestAppContext) {
31903277
let mut cx = NeovimBackedTestContext::new(cx).await;

0 commit comments

Comments
 (0)