Start work on making row/columnwise movement work w/ byte columns

This commit is contained in:
Max Brunsfeld 2021-05-18 22:32:34 -07:00 committed by Antonio Scandurra
parent 6621b9b552
commit f3db0dcff6
4 changed files with 119 additions and 9 deletions

View file

@ -335,6 +335,9 @@ impl Chunk {
let mut point = Point::new(0, 0);
for ch in self.0.chars() {
if point >= target {
if point > target {
panic!("point {:?} is inside of character {:?}", target, ch);
}
break;
}
@ -346,8 +349,6 @@ impl Chunk {
}
offset += ch.len_utf8();
}
assert_eq!(point, target);
offset
}
}

View file

@ -2358,7 +2358,11 @@ impl workspace::ItemView for BufferView {
#[cfg(test)]
mod tests {
use super::*;
use crate::{editor::Point, settings, test::sample_text};
use crate::{
editor::Point,
settings,
test::{multibyte_sample_text, sample_text},
};
use unindent::Unindent;
#[gpui::test]
@ -2715,6 +2719,70 @@ mod tests {
});
}
#[gpui::test]
fn test_move_cursor_multibyte(app: &mut gpui::MutableAppContext) {
let buffer = app.add_model(|ctx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", ctx));
let settings = settings::channel(&app.font_cache()).unwrap().1;
let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
assert_eq!('ⓐ'.len_utf8(), 3);
assert_eq!('α'.len_utf8(), 2);
view.update(app, |view, ctx| {
view.fold_ranges(
vec![
Point::new(0, 6)..Point::new(0, 12),
Point::new(1, 2)..Point::new(1, 4),
Point::new(2, 4)..Point::new(2, 8),
],
ctx,
);
assert_eq!(view.text(ctx.as_ref()), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
view.move_right(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3)]
);
view.move_down(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
);
view.move_right(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2)]
);
view.move_down(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
);
view.move_left(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
);
view.move_up(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
);
view.move_up(&(), ctx);
assert_eq!(
view.selection_ranges(ctx.as_ref()),
&[DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3)]
);
});
}
#[gpui::test]
fn test_beginning_end_of_line(app: &mut gpui::MutableAppContext) {
let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n def", ctx));

View file

@ -92,8 +92,7 @@ impl DisplayMap {
let mut is_blank = true;
for c in self
.snapshot(ctx)
.chunks_at(DisplayPoint::new(display_row, 0), ctx)
.flat_map(str::chars)
.chars_at(DisplayPoint::new(display_row, 0), ctx)
{
if c == ' ' {
indent += 1;
@ -165,6 +164,32 @@ impl DisplayMapSnapshot {
self.chunks_at(point, app).flat_map(str::chars)
}
pub fn column_to_chars(&self, display_row: u32, target: u32, ctx: &AppContext) -> u32 {
let mut count = 0;
let mut column = 0;
for c in self.chars_at(DisplayPoint::new(display_row, 0), ctx) {
count += 1;
column += c.len_utf8() as u32;
if column >= target {
break;
}
}
count
}
pub fn column_from_chars(&self, display_row: u32, char_count: u32, ctx: &AppContext) -> u32 {
let mut count = 0;
let mut column = 0;
for c in self.chars_at(DisplayPoint::new(display_row, 0), ctx) {
count += 1;
column += c.len_utf8() as u32;
if count >= char_count {
break;
}
}
column
}
fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> DisplayPoint {
let chars = self
.folds_snapshot
@ -187,7 +212,6 @@ impl DisplayMapSnapshot {
let (collapsed, expanded_char_column, to_next_stop) =
collapse_tabs(chars, expanded, bias, self.tab_size);
*point.column_mut() = collapsed as u32;
(point, expanded_char_column, to_next_stop)
}
}
@ -360,6 +384,10 @@ pub fn collapse_tabs(
expanded_bytes += c.len_utf8();
}
collapsed_bytes += c.len_utf8();
if expanded_bytes > column {
panic!("column {} is inside of character {:?}", column, c);
}
}
(collapsed_bytes, expanded_chars, 0)
}

View file

@ -16,7 +16,12 @@ pub fn left(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Resu
pub fn right(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Result<DisplayPoint> {
let max_column = map.line_len(point.row(), app);
if point.column() < max_column {
*point.column_mut() += 1;
*point.column_mut() += map
.snapshot(app)
.chars_at(point, app)
.next()
.unwrap()
.len_utf8() as u32;
} else if point.row() < map.max_point(app).row() {
*point.row_mut() += 1;
*point.column_mut() = 0;
@ -35,9 +40,13 @@ pub fn up(
} else {
point.column()
};
let map = map.snapshot(app);
let char_column = map.column_to_chars(point.row(), goal_column, app);
if point.row() > 0 {
*point.row_mut() -= 1;
*point.column_mut() = cmp::min(goal_column, map.line_len(point.row(), app));
*point.column_mut() = map.column_from_chars(point.row(), char_column, app);
} else {
point = DisplayPoint::new(0, 0);
}
@ -56,10 +65,14 @@ pub fn down(
} else {
point.column()
};
let max_point = map.max_point(app);
let map = map.snapshot(app);
let char_column = map.column_to_chars(point.row(), goal_column, app);
if point.row() < max_point.row() {
*point.row_mut() += 1;
*point.column_mut() = cmp::min(goal_column, map.line_len(point.row(), app))
*point.column_mut() = map.column_from_chars(point.row(), char_column, app);
} else {
point = max_point;
}