multi_buffer: Typed MultiBufferOffset (#42707)

This PR introduces a new `MultiBufferOffset` new type wrapping size. The
goal of this is to make it clear at the type level when we are
interacting with offsets of a multi buffer versus offsets of a language
/ text buffer. This improves readability of things quite a bit by making
it clear what kind of offsets one is working with while also reducing
accidental bugs by using the wrong kin of offset for the wrong API.

This PR also uncovered two minor bugs due to that.

Does not yet introduce the MultiBufferPoint equivalent, that is for a
follow up PR.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-11-19 22:00:58 +00:00
committed by GitHub
parent 5e21457f21
commit c98b2d6944
81 changed files with 2957 additions and 1564 deletions
+4 -4
View File
@@ -6,8 +6,8 @@ mod select;
use editor::display_map::DisplaySnapshot;
use editor::{
DisplayPoint, Editor, EditorSettings, HideMouseCursorOrigin, SelectionEffects, ToOffset,
ToPoint, movement,
DisplayPoint, Editor, EditorSettings, HideMouseCursorOrigin, MultiBufferOffset,
SelectionEffects, ToOffset, ToPoint, movement,
};
use gpui::actions;
use gpui::{Context, Window};
@@ -523,7 +523,7 @@ impl Vim {
..range.end.to_offset(&display_map, Bias::Left);
if !byte_range.is_empty() {
let replacement_text = text.repeat(byte_range.len());
let replacement_text = text.repeat(byte_range.end - byte_range.start);
edits.push((byte_range, replacement_text));
}
}
@@ -620,7 +620,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
let newest = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx));
editor.change_selections(Default::default(), window, cx, |s| s.select(vec![newest]));
});
}
+39 -42
View File
@@ -1,10 +1,7 @@
use std::{
cmp::Ordering,
ops::{Deref, DerefMut, Range},
};
use std::{cmp::Ordering, ops::Range};
use editor::{
DisplayPoint,
DisplayPoint, MultiBufferOffset,
display_map::{DisplaySnapshot, ToDisplayPoint},
movement,
};
@@ -104,8 +101,8 @@ trait BoundedObject {
let next_end = self.next_end(map, end_search_start, outer)?;
let maybe_next_start = self.next_start(map, start_search_start, outer);
if let Some(next_start) = maybe_next_start
&& (*next_start < *next_end
|| *next_start == *next_end && self.can_be_zero_width(outer))
&& (next_start.0 < next_end.0
|| next_start.0 == next_end.0 && self.can_be_zero_width(outer))
&& !self.ambiguous_outer()
{
let closing = self.close_at_end(next_start, map, outer)?;
@@ -133,8 +130,8 @@ trait BoundedObject {
let previous_start = self.previous_start(map, start_search_end, outer)?;
let maybe_previous_end = self.previous_end(map, end_search_end, outer);
if let Some(previous_end) = maybe_previous_end
&& (*previous_end > *previous_start
|| *previous_end == *previous_start && self.can_be_zero_width(outer))
&& (previous_end.0 > previous_start.0
|| previous_end.0 == previous_start.0 && self.can_be_zero_width(outer))
&& !self.ambiguous_outer()
{
let closing = self.close_at_start(previous_end, map, outer)?;
@@ -151,30 +148,22 @@ trait BoundedObject {
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
struct Offset(usize);
impl Deref for Offset {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Offset {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone, Copy, PartialEq, Debug, PartialOrd, Ord, Eq)]
struct Offset(MultiBufferOffset);
impl Offset {
fn next(self, map: &DisplaySnapshot) -> Option<Self> {
let next = Self(map.buffer_snapshot().clip_offset(*self + 1, Bias::Right));
(*next > *self).then(|| next)
let next = Self(
map.buffer_snapshot()
.clip_offset(self.0 + 1usize, Bias::Right),
);
(next.0 > self.0).then(|| next)
}
fn previous(self, map: &DisplaySnapshot) -> Option<Self> {
if *self == 0 {
if self.0 == MultiBufferOffset(0) {
return None;
}
Some(Self(
map.buffer_snapshot().clip_offset(*self - 1, Bias::Left),
map.buffer_snapshot().clip_offset(self.0 - 1, Bias::Left),
))
}
fn range(
@@ -211,7 +200,7 @@ impl<B: BoundedObject> HelixTextObject for B {
let max_end = self.close_at_end(search_start, map, find_outer)?;
let min_start = self.close_at_start(max_end, map, find_outer)?;
(*min_start <= *relative_to.start).then(|| min_start..max_end)
(min_start <= relative_to.start).then(|| min_start..max_end)
})
}
@@ -279,8 +268,8 @@ fn relative_range<B: BoundedObject>(
min_start..max_end
};
let start = wanted_range.start.clone().to_display_point(map);
let end = wanted_range.end.clone().to_display_point(map);
let start = wanted_range.start.0.to_display_point(map);
let end = wanted_range.end.0.to_display_point(map);
Some(start..end)
}
@@ -390,7 +379,7 @@ impl ImmediateBoundary {
impl BoundedObject for ImmediateBoundary {
fn next_start(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option<Offset> {
try_find_boundary(map, from, |left, right| {
let classifier = map.buffer_snapshot().char_classifier_at(*from);
let classifier = map.buffer_snapshot().char_classifier_at(from.0);
if outer {
self.is_outer_start(left, right, classifier)
} else {
@@ -400,7 +389,7 @@ impl BoundedObject for ImmediateBoundary {
}
fn next_end(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option<Offset> {
try_find_boundary(map, from, |left, right| {
let classifier = map.buffer_snapshot().char_classifier_at(*from);
let classifier = map.buffer_snapshot().char_classifier_at(from.0);
if outer {
self.is_outer_end(left, right, classifier)
} else {
@@ -410,7 +399,7 @@ impl BoundedObject for ImmediateBoundary {
}
fn previous_start(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option<Offset> {
try_find_preceding_boundary(map, from, |left, right| {
let classifier = map.buffer_snapshot().char_classifier_at(*from);
let classifier = map.buffer_snapshot().char_classifier_at(from.0);
if outer {
self.is_outer_start(left, right, classifier)
} else {
@@ -420,7 +409,7 @@ impl BoundedObject for ImmediateBoundary {
}
fn previous_end(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option<Offset> {
try_find_preceding_boundary(map, from, |left, right| {
let classifier = map.buffer_snapshot().char_classifier_at(*from);
let classifier = map.buffer_snapshot().char_classifier_at(from.0);
if outer {
self.is_outer_end(left, right, classifier)
} else {
@@ -572,7 +561,7 @@ impl FuzzyBoundary {
boundary_kind: Boundary,
) -> Option<Offset> {
let generate_boundary_data = |left, right, point: Offset| {
let classifier = map.buffer_snapshot().char_classifier_at(*from);
let classifier = map.buffer_snapshot().char_classifier_at(from.0);
let reach_boundary = if outer && boundary_kind == Boundary::Start {
self.is_near_potential_outer_start(left, right, &classifier)
} else if !outer && boundary_kind == Boundary::Start {
@@ -598,9 +587,9 @@ impl FuzzyBoundary {
Ordering::Greater => !backward,
});
if backward {
boundaries.max_by_key(|boundary| **boundary)
boundaries.max_by_key(|boundary| *boundary)
} else {
boundaries.min_by_key(|boundary| **boundary)
boundaries.min_by_key(|boundary| *boundary)
}
}
}
@@ -662,15 +651,15 @@ fn try_find_boundary_data<T>(
) -> Option<T> {
let mut prev_ch = map
.buffer_snapshot()
.reversed_chars_at(*from)
.reversed_chars_at(from.0)
.next()
.unwrap_or('\0');
for ch in map.buffer_snapshot().chars_at(*from).chain(['\0']) {
for ch in map.buffer_snapshot().chars_at(from.0).chain(['\0']) {
if let Some(boundary_information) = boundary_information(prev_ch, ch, from) {
return Some(boundary_information);
}
*from += ch.len_utf8();
from.0 += ch.len_utf8();
prev_ch = ch;
}
@@ -702,13 +691,21 @@ fn try_find_preceding_boundary_data<T>(
mut from: Offset,
is_boundary: impl Fn(char, char, Offset) -> Option<T>,
) -> Option<T> {
let mut prev_ch = map.buffer_snapshot().chars_at(*from).next().unwrap_or('\0');
let mut prev_ch = map
.buffer_snapshot()
.chars_at(from.0)
.next()
.unwrap_or('\0');
for ch in map.buffer_snapshot().reversed_chars_at(*from).chain(['\0']) {
for ch in map
.buffer_snapshot()
.reversed_chars_at(from.0)
.chain(['\0'])
{
if let Some(boundary_information) = is_boundary(ch, prev_ch, from) {
return Some(boundary_information);
}
from.0 = from.0.saturating_sub(ch.len_utf8());
from.0.0 = from.0.0.saturating_sub(ch.len_utf8());
prev_ch = ch;
}
+2 -2
View File
@@ -1,6 +1,6 @@
use std::ops::Range;
use editor::{DisplayPoint, display_map::DisplaySnapshot};
use editor::{DisplayPoint, MultiBufferOffset, display_map::DisplaySnapshot};
use gpui::Context;
use text::Bias;
use ui::Window;
@@ -111,7 +111,7 @@ fn find_next_valid_duplicate_space(
fn display_point_range_to_offset_range(
range: &Range<DisplayPoint>,
map: &DisplaySnapshot,
) -> Range<usize> {
) -> Range<MultiBufferOffset> {
range.start.to_offset(map, Bias::Left)..range.end.to_offset(map, Bias::Right)
}
+1 -1
View File
@@ -125,7 +125,7 @@ impl Vim {
s.select_ranges(new_selections.into_iter().map(|(anchor, len)| {
let offset = anchor.to_offset(&snapshot);
if action.before {
offset.saturating_sub(len)..offset
offset.saturating_sub_usize(len)..offset
} else {
offset..(offset + len)
}
+23 -16
View File
@@ -1,5 +1,5 @@
use editor::{
Anchor, Bias, DisplayPoint, Editor, RowExt, ToOffset, ToPoint,
Anchor, Bias, BufferOffset, DisplayPoint, Editor, MultiBufferOffset, RowExt, ToOffset, ToPoint,
display_map::{DisplayRow, DisplaySnapshot, FoldPoint, ToDisplayPoint},
movement::{
self, FindRange, TextLayoutDetails, find_boundary, find_preceding_boundary_display_point,
@@ -2143,7 +2143,7 @@ pub(crate) fn sentence_backwards(
if start_of_next_sentence < start {
times = times.saturating_sub(1);
}
if times == 0 || offset == 0 {
if times == 0 || offset.0 == 0 {
return map.clip_point(
start_of_next_sentence
.to_offset(&map.buffer_snapshot())
@@ -2207,7 +2207,7 @@ pub(crate) fn sentence_forwards(
map.max_point()
}
fn next_non_blank(map: &DisplaySnapshot, start: usize) -> usize {
fn next_non_blank(map: &DisplaySnapshot, start: MultiBufferOffset) -> MultiBufferOffset {
for (c, o) in map.buffer_chars_at(start) {
if c == '\n' || !c.is_whitespace() {
return o;
@@ -2219,7 +2219,10 @@ fn next_non_blank(map: &DisplaySnapshot, start: usize) -> usize {
// given the offset after a ., !, or ? find the start of the next sentence.
// if this is not a sentence boundary, returns None.
fn start_of_next_sentence(map: &DisplaySnapshot, end_of_sentence: usize) -> Option<usize> {
fn start_of_next_sentence(
map: &DisplaySnapshot,
end_of_sentence: MultiBufferOffset,
) -> Option<MultiBufferOffset> {
let chars = map.buffer_chars_at(end_of_sentence);
let mut seen_space = false;
@@ -2253,10 +2256,10 @@ fn go_to_line(map: &DisplaySnapshot, display_point: DisplayPoint, line: usize) -
.clip_point(Point::new((line - 1) as u32, point.column), Bias::Left),
);
let buffer_range = excerpt.buffer_range();
if offset >= buffer_range.start && offset <= buffer_range.end {
if offset >= buffer_range.start.0 && offset <= buffer_range.end.0 {
let point = map
.buffer_snapshot()
.offset_to_point(excerpt.map_offset_from_buffer(offset));
.offset_to_point(excerpt.map_offset_from_buffer(BufferOffset(offset)));
return map.clip_point(map.point_to_display_point(point, Bias::Left), Bias::Left);
}
let mut last_position = None;
@@ -2360,6 +2363,9 @@ fn matching_tag(map: &DisplaySnapshot, head: DisplayPoint) -> Option<DisplayPoin
}
fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
if !map.is_singleton() {
return display_point;
}
// https://github.com/vim/vim/blob/1d87e11a1ef201b26ed87585fba70182ad0c468a/runtime/doc/motion.txt#L1200
let display_point = map.clip_at_line_end(display_point);
let point = display_point.to_point(map);
@@ -2375,9 +2381,10 @@ fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint
// Attempt to find the smallest enclosing bracket range that also contains
// the offset, which only happens if the cursor is currently in a bracket.
let range_filter = |_buffer: &language::BufferSnapshot,
opening_range: Range<usize>,
closing_range: Range<usize>| {
opening_range.contains(&offset) || closing_range.contains(&offset)
opening_range: Range<BufferOffset>,
closing_range: Range<BufferOffset>| {
opening_range.contains(&BufferOffset(offset.0))
|| closing_range.contains(&BufferOffset(offset.0))
};
let bracket_ranges = snapshot
@@ -2840,7 +2847,7 @@ fn method_motion(
for _ in 0..times {
let point = map.display_point_to_point(display_point, Bias::Left);
let offset = point.to_offset(&map.buffer_snapshot());
let offset = point.to_offset(&map.buffer_snapshot()).0;
let range = if direction == Direction::Prev {
0..offset
} else {
@@ -2869,7 +2876,7 @@ fn method_motion(
} else {
possibilities.min().unwrap_or(offset)
};
let new_point = map.clip_point(dest.to_display_point(map), Bias::Left);
let new_point = map.clip_point(MultiBufferOffset(dest).to_display_point(map), Bias::Left);
if new_point == display_point {
break;
}
@@ -2890,7 +2897,7 @@ fn comment_motion(
for _ in 0..times {
let point = map.display_point_to_point(display_point, Bias::Left);
let offset = point.to_offset(&map.buffer_snapshot());
let offset = point.to_offset(&map.buffer_snapshot()).0;
let range = if direction == Direction::Prev {
0..offset
} else {
@@ -2923,7 +2930,7 @@ fn comment_motion(
} else {
possibilities.min().unwrap_or(offset)
};
let new_point = map.clip_point(dest.to_display_point(map), Bias::Left);
let new_point = map.clip_point(MultiBufferOffset(dest).to_display_point(map), Bias::Left);
if new_point == display_point {
break;
}
@@ -2946,7 +2953,7 @@ fn section_motion(
.display_point_to_point(display_point, Bias::Left)
.to_offset(&map.buffer_snapshot());
let range = if direction == Direction::Prev {
0..offset
MultiBufferOffset(0)..offset
} else {
offset..map.buffer_snapshot().len()
};
@@ -2977,7 +2984,7 @@ fn section_motion(
let relevant = if is_start { range.start } else { range.end };
if direction == Direction::Prev && relevant < offset {
Some(relevant)
} else if direction == Direction::Next && relevant > offset + 1 {
} else if direction == Direction::Next && relevant > offset + 1usize {
Some(relevant)
} else {
None
@@ -2985,7 +2992,7 @@ fn section_motion(
});
let offset = if direction == Direction::Prev {
possibilities.max().unwrap_or(0)
possibilities.max().unwrap_or(MultiBufferOffset(0))
} else {
possibilities.min().unwrap_or(map.buffer_snapshot().len())
};
+7 -2
View File
@@ -211,9 +211,14 @@ fn find_target(
let mut pre_char = String::new();
// Backward scan to find the start of the number, but stop at start_offset
for ch in snapshot.reversed_chars_at(offset + if offset < snapshot.len() { 1 } else { 0 }) {
let next_offset = if offset < snapshot.len() {
offset + 1usize
} else {
offset
};
for ch in snapshot.reversed_chars_at(next_offset) {
// Search boundaries
if offset == 0 || ch.is_whitespace() || (need_range && offset <= start_offset) {
if offset.0 == 0 || ch.is_whitespace() || (need_range && offset <= start_offset) {
break;
}
+8 -2
View File
@@ -1,4 +1,7 @@
use editor::{DisplayPoint, RowExt, SelectionEffects, display_map::ToDisplayPoint, movement};
use editor::{
DisplayPoint, MultiBufferOffset, RowExt, SelectionEffects, display_map::ToDisplayPoint,
movement,
};
use gpui::{Action, Context, Window};
use language::{Bias, SelectionGoal};
use schemars::JsonSchema;
@@ -174,7 +177,10 @@ impl Vim {
original_indent_columns.push(original_indent_column);
}
let cursor_offset = editor.selections.last::<usize>(&display_map).head();
let cursor_offset = editor
.selections
.last::<MultiBufferOffset>(&display_map)
.head();
if editor
.buffer()
.read(cx)
+40 -26
View File
@@ -6,7 +6,7 @@ use crate::{
state::{Mode, Operator},
};
use editor::{
Bias, DisplayPoint, Editor, ToOffset,
Bias, BufferOffset, DisplayPoint, Editor, MultiBufferOffset, ToOffset,
display_map::{DisplaySnapshot, ToDisplayPoint},
movement::{self, FindRange},
};
@@ -81,8 +81,8 @@ pub struct CandidateRange {
#[derive(Debug, Clone)]
pub struct CandidateWithRanges {
candidate: CandidateRange,
open_range: Range<usize>,
close_range: Range<usize>,
open_range: Range<MultiBufferOffset>,
close_range: Range<MultiBufferOffset>,
}
/// Selects text at the same indentation level.
@@ -120,7 +120,7 @@ struct CurlyBrackets {
opening: bool,
}
fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
fn cover_or_next<I: Iterator<Item = (Range<MultiBufferOffset>, Range<MultiBufferOffset>)>>(
candidates: Option<I>,
caret: DisplayPoint,
map: &DisplaySnapshot,
@@ -128,7 +128,7 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
let caret_offset = caret.to_offset(map, Bias::Left);
let mut covering = vec![];
let mut next_ones = vec![];
let snapshot = &map.buffer_snapshot();
let snapshot = map.buffer_snapshot();
if let Some(ranges) = candidates {
for (open_range, close_range) in ranges {
@@ -171,7 +171,7 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
if !next_ones.is_empty() {
return next_ones.into_iter().min_by_key(|r| {
let start = r.candidate.start.to_offset(map, Bias::Left);
(start as isize - caret_offset as isize).abs()
(start.0 as isize - caret_offset.0 as isize).abs()
});
}
@@ -181,8 +181,8 @@ fn cover_or_next<I: Iterator<Item = (Range<usize>, Range<usize>)>>(
type DelimiterPredicate = dyn Fn(&BufferSnapshot, usize, usize) -> bool;
struct DelimiterRange {
open: Range<usize>,
close: Range<usize>,
open: Range<MultiBufferOffset>,
close: Range<MultiBufferOffset>,
}
impl DelimiterRange {
@@ -221,14 +221,14 @@ fn find_mini_delimiters(
.buffer_snapshot()
.bracket_ranges(visible_line_range)
.map(|ranges| {
ranges.filter_map(move |(open, close)| {
ranges.filter_map(|(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) {
if is_valid_delimiter(buffer, buffer_open.start.0, buffer_close.start.0) {
Some((open, close))
} else {
None
@@ -252,8 +252,12 @@ fn find_mini_delimiters(
Some(
DelimiterRange {
open: open_bracket,
close: close_bracket,
open: excerpt.map_range_from_buffer(
BufferOffset(open_bracket.start)..BufferOffset(open_bracket.end),
),
close: excerpt.map_range_from_buffer(
BufferOffset(close_bracket.start)..BufferOffset(close_bracket.end),
),
}
.to_display_range(map, around),
)
@@ -899,7 +903,7 @@ pub fn surrounding_html_tag(
// Find the most closest to current offset
let mut cursor = buffer.syntax_layer_at(offset)?.node().walk();
let mut last_child_node = cursor.node();
while cursor.goto_first_child_for_byte(offset).is_some() {
while cursor.goto_first_child_for_byte(offset.0).is_some() {
last_child_node = cursor.node();
}
@@ -916,10 +920,16 @@ pub fn surrounding_html_tag(
- range.start.to_offset(map, Bias::Left)
<= 1
{
offset <= last_child.end_byte()
offset.0 <= last_child.end_byte()
} else {
range.start.to_offset(map, Bias::Left) >= first_child.start_byte()
&& range.end.to_offset(map, Bias::Left) <= last_child.start_byte() + 1
excerpt
.map_offset_to_buffer(range.start.to_offset(map, Bias::Left))
.0
>= first_child.start_byte()
&& excerpt
.map_offset_to_buffer(range.end.to_offset(map, Bias::Left))
.0
<= last_child.start_byte() + 1
};
if open_tag.is_some() && open_tag == close_tag && is_valid {
let range = if around {
@@ -927,6 +937,7 @@ pub fn surrounding_html_tag(
} else {
first_child.byte_range().end..last_child.byte_range().start
};
let range = BufferOffset(range.start)..BufferOffset(range.end);
if excerpt.contains_buffer_range(range.clone()) {
let result = excerpt.map_range_from_buffer(range);
return Some(
@@ -1093,7 +1104,8 @@ fn text_object(
.collect();
matches.sort_by_key(|r| r.end - r.start);
if let Some(buffer_range) = matches.first() {
let range = excerpt.map_range_from_buffer(buffer_range.clone());
let buffer_range = BufferOffset(buffer_range.start)..BufferOffset(buffer_range.end);
let range = excerpt.map_range_from_buffer(buffer_range);
return Some(range.start.to_display_point(map)..range.end.to_display_point(map));
}
@@ -1113,10 +1125,12 @@ fn text_object(
if let Some(buffer_range) = matches.first()
&& !buffer_range.is_empty()
{
let range = excerpt.map_range_from_buffer(buffer_range.clone());
let buffer_range = BufferOffset(buffer_range.start)..BufferOffset(buffer_range.end);
let range = excerpt.map_range_from_buffer(buffer_range);
return Some(range.start.to_display_point(map)..range.end.to_display_point(map));
}
let buffer_range = excerpt.map_range_from_buffer(around_range.clone());
let around_range = BufferOffset(around_range.start)..BufferOffset(around_range.end);
let buffer_range = excerpt.map_range_from_buffer(around_range);
return Some(buffer_range.start.to_display_point(map)..buffer_range.end.to_display_point(map));
}
@@ -1134,9 +1148,9 @@ fn argument(
fn comma_delimited_range_at(
buffer: &BufferSnapshot,
mut offset: usize,
mut offset: BufferOffset,
include_comma: bool,
) -> Option<Range<usize>> {
) -> Option<Range<BufferOffset>> {
// Seek to the first non-whitespace character
offset += buffer
.chars_at(offset)
@@ -1151,7 +1165,7 @@ fn argument(
}
// If the cursor is outside the brackets, ignore them
if open.start == offset || close.end == offset {
if open.start == offset.0 || close.end == offset.0 {
return false;
}
@@ -1167,7 +1181,7 @@ fn argument(
let (open_bracket, close_bracket) =
buffer.innermost_enclosing_bracket_ranges(offset..offset, Some(&bracket_filter))?;
let inner_bracket_range = open_bracket.end..close_bracket.start;
let inner_bracket_range = BufferOffset(open_bracket.end)..BufferOffset(close_bracket.start);
let layer = buffer.syntax_layer_at(offset)?;
let node = layer.node();
@@ -1186,7 +1200,7 @@ fn argument(
parent_covers_bracket_range = covers_bracket_range;
// Unable to find a child node with a parent that covers the bracket range, so no argument to select
cursor.goto_first_child_for_byte(offset)?;
cursor.goto_first_child_for_byte(offset.0)?;
}
let mut argument_node = cursor.node();
@@ -1256,7 +1270,7 @@ fn argument(
}
}
Some(start..end)
Some(BufferOffset(start)..BufferOffset(end))
}
let result = comma_delimited_range_at(buffer, excerpt.map_offset_to_buffer(offset), around)?;
@@ -1387,7 +1401,7 @@ fn is_possible_sentence_start(character: char) -> bool {
const SENTENCE_END_PUNCTUATION: &[char] = &['.', '!', '?'];
const SENTENCE_END_FILLERS: &[char] = &[')', ']', '"', '\''];
const SENTENCE_END_WHITESPACE: &[char] = &[' ', '\t', '\n'];
fn is_sentence_end(map: &DisplaySnapshot, offset: usize) -> bool {
fn is_sentence_end(map: &DisplaySnapshot, offset: MultiBufferOffset) -> bool {
let mut next_chars = map.buffer_chars_at(offset).peekable();
if let Some((char, _)) = next_chars.next() {
// We are at a double newline. This position is a sentence end.
+6 -6
View File
@@ -4,7 +4,7 @@ use crate::{
object::{Object, surrounding_markers},
state::Mode,
};
use editor::{Bias, movement};
use editor::{Bias, MultiBufferOffset, movement};
use gpui::{Context, Window};
use language::BracketPair;
@@ -175,7 +175,7 @@ impl Vim {
while let Some((ch, offset)) = chars_and_offset.next() {
if ch.to_string() == pair.start {
let start = offset;
let mut end = start + 1;
let mut end = start + 1usize;
if surround
&& let Some((next_ch, _)) = chars_and_offset.peek()
&& next_ch.eq(&' ')
@@ -193,7 +193,7 @@ impl Vim {
while let Some((ch, offset)) = reverse_chars_and_offsets.next() {
if ch.to_string() == pair.end {
let mut start = offset;
let end = start + 1;
let end = start + 1usize;
if surround
&& let Some((next_ch, _)) = reverse_chars_and_offsets.peek()
&& next_ch.eq(&' ')
@@ -282,7 +282,7 @@ impl Vim {
// that the end replacement string does not exceed
// this value. Helpful when dealing with newlines.
let mut edit_len = 0;
let mut open_range_end = 0;
let mut open_range_end = MultiBufferOffset(0);
let mut chars_and_offset = display_map
.buffer_chars_at(range.start.to_offset(&display_map, Bias::Left))
.peekable();
@@ -291,7 +291,7 @@ impl Vim {
if ch.to_string() == will_replace_pair.start {
let mut open_str = pair.start.clone();
let start = offset;
open_range_end = start + 1;
open_range_end = start + 1usize;
while let Some((next_ch, _)) = chars_and_offset.next()
&& next_ch == ' '
{
@@ -322,7 +322,7 @@ impl Vim {
if ch.to_string() == will_replace_pair.end {
let mut close_str = String::new();
let mut start = offset;
let end = start + 1;
let end = start + 1usize;
while let Some((next_ch, _)) = reverse_chars_and_offsets.next()
&& next_ch == ' '
&& close_str.len() < edit_len - 1
+5 -2
View File
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Duration};
use collections::HashMap;
use command_palette::CommandPalette;
use editor::{
AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer,
AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, MultiBufferOffset,
actions::{DeleteLine, WrapSelectionsInTag},
code_context_menus::CodeContextMenu,
display_map::DisplayRow,
@@ -908,6 +908,9 @@ fn assert_pending_input(cx: &mut VimTestContext, expected: &str) {
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot()))
.collect::<Vec<_>>(),
ranges
.iter()
.map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
.collect::<Vec<_>>()
)
});
}
@@ -967,7 +970,7 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
.iter()
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot()))
.collect::<Vec<_>>(),
vec![0..1]
vec![MultiBufferOffset(0)..MultiBufferOffset(1)]
)
});
cx.executor().advance_clock(Duration::from_millis(500));
+4 -4
View File
@@ -21,8 +21,8 @@ mod visual;
use collections::HashMap;
use editor::{
Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, SelectionEffects,
ToPoint,
Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, MultiBufferOffset,
SelectionEffects, ToPoint,
actions::Paste,
movement::{self, FindRange},
};
@@ -1388,7 +1388,7 @@ impl Vim {
let newest_selection_empty = editor.update(cx, |editor, cx| {
editor
.selections
.newest::<usize>(&editor.display_snapshot(cx))
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
.is_empty()
});
let editor = editor.read(cx);
@@ -1488,7 +1488,7 @@ impl Vim {
let snapshot = &editor.snapshot(window, cx);
let selection = editor
.selections
.newest::<usize>(&snapshot.display_snapshot);
.newest::<MultiBufferOffset>(&snapshot.display_snapshot);
let snapshot = snapshot.buffer_snapshot();
let (range, kind) =
+6 -6
View File
@@ -2,7 +2,7 @@ use std::sync::Arc;
use collections::HashMap;
use editor::{
Bias, DisplayPoint, Editor, SelectionEffects,
Bias, DisplayPoint, Editor, MultiBufferOffset, SelectionEffects,
display_map::{DisplaySnapshot, ToDisplayPoint},
movement,
};
@@ -778,7 +778,7 @@ impl Vim {
{
let range = row_range.start.to_offset(&display_map, Bias::Right)
..row_range.end.to_offset(&display_map, Bias::Right);
let text = text.repeat(range.len());
let text = text.repeat(range.end - range.start);
edits.push((range, text));
}
}
@@ -844,8 +844,8 @@ impl Vim {
return;
};
let vim_is_normal = self.mode == Mode::Normal;
let mut start_selection = 0usize;
let mut end_selection = 0usize;
let mut start_selection = MultiBufferOffset(0);
let mut end_selection = MultiBufferOffset(0);
self.update_editor(cx, |_, editor, _| {
editor.set_collapse_matches(false);
@@ -868,7 +868,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
let latest = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx));
start_selection = latest.start;
end_selection = latest.end;
});
@@ -891,7 +891,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| {
let latest = editor
.selections
.newest::<usize>(&editor.display_snapshot(cx));
.newest::<MultiBufferOffset>(&editor.display_snapshot(cx));
if vim_is_normal {
start_selection = latest.start;
end_selection = latest.end;