Compare commits

...

4 Commits

Author SHA1 Message Date
dino
eebc03e0b5 test(vim): add test for minibrackets in multibuffer 2025-11-03 20:23:13 +00:00
dino
0a48bad901 refactor(vim): filter ranges before cover_or_next 2025-11-03 16:26:18 +00:00
dino
4b4dd36b4b fix(vim): fix issue with cover_or_next
The `vim::object::cover_or_next` function assumes that `range_filter`
also expects the ranges for a `MultiBufferSnapshot` instead of a single
buffer. This commit fixes the function so as to convert the ranges to
buffer ranges before calling range_filter.
2025-11-03 15:55:49 +00:00
dino
9cd297621a chore(vim): update find_mini_delimiters to use buffer range
This doesn't yet fix the user's issue, as it appears to not correctly be
copying the content inside the brackets, but it does appear to prevent
it from crashing as we're now converting the multibuffer's ranges to
buffer ranges.
2025-10-31 18:13:28 +00:00

View File

@@ -124,7 +124,6 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
candidates: Option<I>,
caret: DisplayPoint,
map: &DisplaySnapshot,
range_filter: Option<&dyn Fn(Range<usize>, Range<usize>) -> bool>,
) -> Option<CandidateWithRanges> {
let caret_offset = caret.to_offset(map, Bias::Left);
let mut covering = vec![];
@@ -135,11 +134,6 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
for (open_range, close_range) in ranges {
let start_off = open_range.start;
let end_off = close_range.end;
if let Some(range_filter) = range_filter
&& !range_filter(open_range.clone(), close_range.clone())
{
continue;
}
let candidate = CandidateWithRanges {
candidate: CandidateRange {
start: start_off.to_display_point(map),
@@ -214,16 +208,35 @@ fn find_mini_delimiters(
let visible_line_range = get_visible_line_range(&line_range);
let snapshot = &map.buffer_snapshot();
let excerpt = snapshot.excerpt_containing(offset..offset)?;
let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
let buffer = excerpt.buffer();
let buffer_offset = excerpt.map_offset_to_buffer(offset);
let bracket_filter = |open: Range<usize>, close: Range<usize>| {
is_valid_delimiter(buffer, open.start, close.start)
};
// Try to find delimiters in visible range first
let ranges = map.buffer_snapshot().bracket_ranges(visible_line_range);
if let Some(candidate) = cover_or_next(ranges, display_point, map, Some(&bracket_filter)) {
let ranges = map
.buffer_snapshot()
.bracket_ranges(visible_line_range)
.map(|ranges| {
ranges.filter_map(move |(open, close)| {
// Convert the ranges from multibuffer space to buffer space as
// that is what `is_valid_delimiter` expects, otherwise it might
// panic as the values might be out of bounds.
let buffer_open = excerpt.map_range_to_buffer(open.clone());
let buffer_close = excerpt.map_range_to_buffer(close.clone());
if is_valid_delimiter(buffer, buffer_open.start, buffer_close.start) {
Some((open, close))
} else {
None
}
})
});
if let Some(candidate) = cover_or_next(ranges, display_point, map) {
return Some(
DelimiterRange {
open: candidate.open_range,
@@ -234,8 +247,8 @@ fn find_mini_delimiters(
}
// Fall back to innermost enclosing brackets
let (open_bracket, close_bracket) =
buffer.innermost_enclosing_bracket_ranges(offset..offset, Some(&bracket_filter))?;
let (open_bracket, close_bracket) = buffer
.innermost_enclosing_bracket_ranges(buffer_offset..buffer_offset, Some(&bracket_filter))?;
Some(
DelimiterRange {
@@ -1736,8 +1749,10 @@ pub fn surrounding_markers(
#[cfg(test)]
mod test {
use editor::{Editor, EditorMode, MultiBuffer, test::editor_test_context::EditorTestContext};
use gpui::KeyBinding;
use indoc::indoc;
use text::Point;
use crate::{
object::{AnyBrackets, AnyQuotes, MiniBrackets},
@@ -3185,6 +3200,80 @@ mod test {
}
}
#[gpui::test]
async fn test_minibrackets_multibuffer(cx: &mut gpui::TestAppContext) {
// Initialize test context with the TypeScript language loaded, so we
// can actually get brackets definition.
let mut cx = VimTestContext::new(cx, true).await;
// Update `b` to `MiniBrackets` so we can later use it when simulating
// keystrokes.
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new("b", MiniBrackets, None)]);
});
// Setup MultiBuffer with 3 different excerpts, only showing the first
// two rows for each buffer.
let (editor, cx) = cx.add_window_view(|window, cx| {
let multi_buffer = MultiBuffer::build_multi(
[
("111\n222\n333\n444\n", vec![Point::row_range(0..2)]),
("111\na {bracket} example\n", vec![Point::row_range(0..2)]),
],
cx,
);
// In order for the brackets to actually be found, we need to update
// the language used for the second buffer. This is something that
// is handled automatically when simply using `VimTestContext::new`
// but, since this is being set manually, the language isn't
// automatically set.
let editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
if let Some(buffer) = multi_buffer.read(cx).buffer(buffer_ids[1]) {
buffer.update(cx, |buffer, cx| {
buffer.set_language(Some(language::rust_lang()), cx);
})
};
editor
});
let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
ˇ111
222
[EXCERPT]
111
a {bracket} example
"
});
cx.simulate_keystrokes("j j j j f r");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
111
222
[EXCERPT]
111
a {bˇracket} example
"
});
cx.simulate_keystrokes("d i b");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
111
222
[EXCERPT]
111
a {ˇ} example
"
});
}
#[gpui::test]
async fn test_minibrackets_trailing_space(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;