Start work on making row/columnwise movement work w/ byte columns
This commit is contained in:
parent
6621b9b552
commit
f3db0dcff6
4 changed files with 119 additions and 9 deletions
|
@ -335,6 +335,9 @@ impl Chunk {
|
||||||
let mut point = Point::new(0, 0);
|
let mut point = Point::new(0, 0);
|
||||||
for ch in self.0.chars() {
|
for ch in self.0.chars() {
|
||||||
if point >= target {
|
if point >= target {
|
||||||
|
if point > target {
|
||||||
|
panic!("point {:?} is inside of character {:?}", target, ch);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,8 +349,6 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
offset += ch.len_utf8();
|
offset += ch.len_utf8();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(point, target);
|
|
||||||
offset
|
offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2358,7 +2358,11 @@ impl workspace::ItemView for BufferView {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{editor::Point, settings, test::sample_text};
|
use crate::{
|
||||||
|
editor::Point,
|
||||||
|
settings,
|
||||||
|
test::{multibyte_sample_text, sample_text},
|
||||||
|
};
|
||||||
use unindent::Unindent;
|
use unindent::Unindent;
|
||||||
|
|
||||||
#[gpui::test]
|
#[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]
|
#[gpui::test]
|
||||||
fn test_beginning_end_of_line(app: &mut gpui::MutableAppContext) {
|
fn test_beginning_end_of_line(app: &mut gpui::MutableAppContext) {
|
||||||
let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n def", ctx));
|
let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n def", ctx));
|
||||||
|
|
|
@ -92,8 +92,7 @@ impl DisplayMap {
|
||||||
let mut is_blank = true;
|
let mut is_blank = true;
|
||||||
for c in self
|
for c in self
|
||||||
.snapshot(ctx)
|
.snapshot(ctx)
|
||||||
.chunks_at(DisplayPoint::new(display_row, 0), ctx)
|
.chars_at(DisplayPoint::new(display_row, 0), ctx)
|
||||||
.flat_map(str::chars)
|
|
||||||
{
|
{
|
||||||
if c == ' ' {
|
if c == ' ' {
|
||||||
indent += 1;
|
indent += 1;
|
||||||
|
@ -165,6 +164,32 @@ impl DisplayMapSnapshot {
|
||||||
self.chunks_at(point, app).flat_map(str::chars)
|
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 {
|
fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> DisplayPoint {
|
||||||
let chars = self
|
let chars = self
|
||||||
.folds_snapshot
|
.folds_snapshot
|
||||||
|
@ -187,7 +212,6 @@ impl DisplayMapSnapshot {
|
||||||
let (collapsed, expanded_char_column, to_next_stop) =
|
let (collapsed, expanded_char_column, to_next_stop) =
|
||||||
collapse_tabs(chars, expanded, bias, self.tab_size);
|
collapse_tabs(chars, expanded, bias, self.tab_size);
|
||||||
*point.column_mut() = collapsed as u32;
|
*point.column_mut() = collapsed as u32;
|
||||||
|
|
||||||
(point, expanded_char_column, to_next_stop)
|
(point, expanded_char_column, to_next_stop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,6 +384,10 @@ pub fn collapse_tabs(
|
||||||
expanded_bytes += c.len_utf8();
|
expanded_bytes += c.len_utf8();
|
||||||
}
|
}
|
||||||
collapsed_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)
|
(collapsed_bytes, expanded_chars, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
pub fn right(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Result<DisplayPoint> {
|
||||||
let max_column = map.line_len(point.row(), app);
|
let max_column = map.line_len(point.row(), app);
|
||||||
if point.column() < max_column {
|
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() {
|
} else if point.row() < map.max_point(app).row() {
|
||||||
*point.row_mut() += 1;
|
*point.row_mut() += 1;
|
||||||
*point.column_mut() = 0;
|
*point.column_mut() = 0;
|
||||||
|
@ -35,9 +40,13 @@ pub fn up(
|
||||||
} else {
|
} else {
|
||||||
point.column()
|
point.column()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let map = map.snapshot(app);
|
||||||
|
let char_column = map.column_to_chars(point.row(), goal_column, app);
|
||||||
|
|
||||||
if point.row() > 0 {
|
if point.row() > 0 {
|
||||||
*point.row_mut() -= 1;
|
*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 {
|
} else {
|
||||||
point = DisplayPoint::new(0, 0);
|
point = DisplayPoint::new(0, 0);
|
||||||
}
|
}
|
||||||
|
@ -56,10 +65,14 @@ pub fn down(
|
||||||
} else {
|
} else {
|
||||||
point.column()
|
point.column()
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_point = map.max_point(app);
|
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() {
|
if point.row() < max_point.row() {
|
||||||
*point.row_mut() += 1;
|
*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 {
|
} else {
|
||||||
point = max_point;
|
point = max_point;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue