Use Horizontal ranges everywhere
This commit is contained in:
parent
002e2cc42c
commit
ab050d1890
13 changed files with 229 additions and 149 deletions
|
@ -1,5 +1,3 @@
|
|||
use std::cmp;
|
||||
|
||||
use editor::{
|
||||
char_kind,
|
||||
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
|
||||
|
@ -371,13 +369,13 @@ impl Motion {
|
|||
Backspace => (backspace(map, point, times), SelectionGoal::None),
|
||||
Down {
|
||||
display_lines: false,
|
||||
} => down(map, point, goal, times),
|
||||
} => up_down_buffer_rows(map, point, goal, times as isize, &text_layout_details),
|
||||
Down {
|
||||
display_lines: true,
|
||||
} => down_display(map, point, goal, times, &text_layout_details),
|
||||
Up {
|
||||
display_lines: false,
|
||||
} => up(map, point, goal, times),
|
||||
} => up_down_buffer_rows(map, point, goal, 0 - times as isize, &text_layout_details),
|
||||
Up {
|
||||
display_lines: true,
|
||||
} => up_display(map, point, goal, times, &text_layout_details),
|
||||
|
@ -536,35 +534,86 @@ fn backspace(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> Di
|
|||
point
|
||||
}
|
||||
|
||||
fn down(
|
||||
pub(crate) fn start_of_relative_buffer_row(
|
||||
map: &DisplaySnapshot,
|
||||
point: DisplayPoint,
|
||||
times: isize,
|
||||
) -> DisplayPoint {
|
||||
let start = map.display_point_to_fold_point(point, Bias::Left);
|
||||
let target = start.row() as isize + times;
|
||||
let new_row = (target.max(0) as u32).min(map.fold_snapshot.max_point().row());
|
||||
|
||||
map.clip_point(
|
||||
map.fold_point_to_display_point(
|
||||
map.fold_snapshot
|
||||
.clip_point(FoldPoint::new(new_row, 0), Bias::Right),
|
||||
),
|
||||
Bias::Right,
|
||||
)
|
||||
}
|
||||
|
||||
fn up_down_buffer_rows(
|
||||
map: &DisplaySnapshot,
|
||||
point: DisplayPoint,
|
||||
mut goal: SelectionGoal,
|
||||
times: usize,
|
||||
times: isize,
|
||||
text_layout_details: &TextLayoutDetails,
|
||||
) -> (DisplayPoint, SelectionGoal) {
|
||||
let start = map.display_point_to_fold_point(point, Bias::Left);
|
||||
let begin_folded_line = map.fold_point_to_display_point(
|
||||
map.fold_snapshot
|
||||
.clip_point(FoldPoint::new(start.row(), 0), Bias::Left),
|
||||
);
|
||||
let select_nth_wrapped_row = point.row() - begin_folded_line.row();
|
||||
|
||||
let goal_column = match goal {
|
||||
SelectionGoal::Column(column) => column,
|
||||
SelectionGoal::ColumnRange { end, .. } => end,
|
||||
let (goal_wrap, goal_x) = match goal {
|
||||
SelectionGoal::WrappedHorizontalPosition((row, x)) => (row, x),
|
||||
SelectionGoal::WrappedHorizontalRange { end: (row, x), .. } => (row, x),
|
||||
SelectionGoal::HorizontalRange { end, .. } => (select_nth_wrapped_row, end),
|
||||
SelectionGoal::HorizontalPosition(x) => (select_nth_wrapped_row, x),
|
||||
_ => {
|
||||
goal = SelectionGoal::Column(start.column());
|
||||
start.column()
|
||||
let x = map.x_for_point(point, text_layout_details);
|
||||
goal = SelectionGoal::WrappedHorizontalPosition((select_nth_wrapped_row, x));
|
||||
(select_nth_wrapped_row, x)
|
||||
}
|
||||
};
|
||||
|
||||
let new_row = cmp::min(
|
||||
start.row() + times as u32,
|
||||
map.fold_snapshot.max_point().row(),
|
||||
);
|
||||
let new_col = cmp::min(goal_column, map.fold_snapshot.line_len(new_row));
|
||||
let point = map.fold_point_to_display_point(
|
||||
let target = start.row() as isize + times;
|
||||
let new_row = (target.max(0) as u32).min(map.fold_snapshot.max_point().row());
|
||||
|
||||
let mut begin_folded_line = map.fold_point_to_display_point(
|
||||
map.fold_snapshot
|
||||
.clip_point(FoldPoint::new(new_row, new_col), Bias::Left),
|
||||
.clip_point(FoldPoint::new(new_row, 0), Bias::Left),
|
||||
);
|
||||
|
||||
// clip twice to "clip at end of line"
|
||||
(map.clip_point(point, Bias::Left), goal)
|
||||
let mut i = 0;
|
||||
while i < goal_wrap && begin_folded_line.row() < map.max_point().row() {
|
||||
let next_folded_line = DisplayPoint::new(begin_folded_line.row() + 1, 0);
|
||||
if map
|
||||
.display_point_to_fold_point(next_folded_line, Bias::Right)
|
||||
.row()
|
||||
== new_row
|
||||
{
|
||||
i += 1;
|
||||
begin_folded_line = next_folded_line;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let new_col = if i == goal_wrap {
|
||||
map.column_for_x(begin_folded_line.row(), goal_x, text_layout_details)
|
||||
} else {
|
||||
map.line_len(begin_folded_line.row())
|
||||
};
|
||||
|
||||
(
|
||||
map.clip_point(
|
||||
DisplayPoint::new(begin_folded_line.row(), new_col),
|
||||
Bias::Left,
|
||||
),
|
||||
goal,
|
||||
)
|
||||
}
|
||||
|
||||
fn down_display(
|
||||
|
@ -581,33 +630,6 @@ fn down_display(
|
|||
(point, goal)
|
||||
}
|
||||
|
||||
pub(crate) fn up(
|
||||
map: &DisplaySnapshot,
|
||||
point: DisplayPoint,
|
||||
mut goal: SelectionGoal,
|
||||
times: usize,
|
||||
) -> (DisplayPoint, SelectionGoal) {
|
||||
let start = map.display_point_to_fold_point(point, Bias::Left);
|
||||
|
||||
let goal_column = match goal {
|
||||
SelectionGoal::Column(column) => column,
|
||||
SelectionGoal::ColumnRange { end, .. } => end,
|
||||
_ => {
|
||||
goal = SelectionGoal::Column(start.column());
|
||||
start.column()
|
||||
}
|
||||
};
|
||||
|
||||
let new_row = start.row().saturating_sub(times as u32);
|
||||
let new_col = cmp::min(goal_column, map.fold_snapshot.line_len(new_row));
|
||||
let point = map.fold_point_to_display_point(
|
||||
map.fold_snapshot
|
||||
.clip_point(FoldPoint::new(new_row, new_col), Bias::Left),
|
||||
);
|
||||
|
||||
(map.clip_point(point, Bias::Left), goal)
|
||||
}
|
||||
|
||||
fn up_display(
|
||||
map: &DisplaySnapshot,
|
||||
mut point: DisplayPoint,
|
||||
|
@ -894,7 +916,7 @@ fn find_backward(
|
|||
}
|
||||
|
||||
fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
|
||||
let correct_line = down(map, point, SelectionGoal::None, times).0;
|
||||
let correct_line = start_of_relative_buffer_row(map, point, times as isize);
|
||||
first_non_whitespace(map, false, correct_line)
|
||||
}
|
||||
|
||||
|
@ -904,7 +926,7 @@ pub(crate) fn next_line_end(
|
|||
times: usize,
|
||||
) -> DisplayPoint {
|
||||
if times > 1 {
|
||||
point = down(map, point, SelectionGoal::None, times - 1).0;
|
||||
point = start_of_relative_buffer_row(map, point, times as isize - 1);
|
||||
}
|
||||
end_of_line(map, false, point)
|
||||
}
|
||||
|
|
|
@ -194,9 +194,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
|
|||
vim.switch_mode(Mode::Insert, false, cx);
|
||||
vim.update_active_editor(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_cursors_with(|map, cursor, goal| {
|
||||
(right(map, cursor, 1), SelectionGoal::None)
|
||||
});
|
||||
s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -219,7 +217,7 @@ fn insert_first_non_whitespace(
|
|||
vim.switch_mode(Mode::Insert, false, cx);
|
||||
vim.update_active_editor(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_cursors_with(|map, cursor, goal| {
|
||||
s.move_cursors_with(|map, cursor, _| {
|
||||
(
|
||||
first_non_whitespace(map, false, cursor),
|
||||
SelectionGoal::None,
|
||||
|
@ -236,7 +234,7 @@ fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewConte
|
|||
vim.switch_mode(Mode::Insert, false, cx);
|
||||
vim.update_active_editor(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_cursors_with(|map, cursor, goal| {
|
||||
s.move_cursors_with(|map, cursor, _| {
|
||||
(next_line_end(map, cursor, 1), SelectionGoal::None)
|
||||
});
|
||||
});
|
||||
|
@ -267,7 +265,7 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex
|
|||
editor.edit_with_autoindent(edits, cx);
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_cursors_with(|map, cursor, _| {
|
||||
let previous_line = motion::up(map, cursor, SelectionGoal::None, 1).0;
|
||||
let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
|
||||
let insert_point = motion::end_of_line(map, false, previous_line);
|
||||
(insert_point, SelectionGoal::None)
|
||||
});
|
||||
|
@ -398,12 +396,26 @@ mod test {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_j(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await.binding(["j"]);
|
||||
cx.assert_all(indoc! {"
|
||||
ˇThe qˇuick broˇwn
|
||||
ˇfox jumps"
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
aaˇaa
|
||||
😃😃"
|
||||
})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes(["j"]).await;
|
||||
cx.assert_shared_state(indoc! {"
|
||||
aaaa
|
||||
😃ˇ😃"
|
||||
})
|
||||
.await;
|
||||
|
||||
for marked_position in cx.each_marked_position(indoc! {"
|
||||
ˇThe qˇuick broˇwn
|
||||
ˇfox jumps"
|
||||
}) {
|
||||
cx.assert_neovim_compatible(&marked_position, ["j"]).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
|
|
@ -3,11 +3,7 @@ use gpui::{actions, AppContext, WindowContext};
|
|||
use language::Point;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::{
|
||||
motion::{right, Motion},
|
||||
utils::copy_selections_content,
|
||||
Mode, Vim,
|
||||
};
|
||||
use crate::{motion::Motion, utils::copy_selections_content, Mode, Vim};
|
||||
|
||||
actions!(vim, [Substitute, SubstituteLine]);
|
||||
|
||||
|
|
|
@ -652,3 +652,59 @@ async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
|
|||
Lorem Ipsum"})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_wrap(12).await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
aaˇaa
|
||||
😃😃"
|
||||
})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes(["j"]).await;
|
||||
cx.assert_shared_state(indoc! {"
|
||||
aaaa
|
||||
😃ˇ😃"
|
||||
})
|
||||
.await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
123456789012aaˇaa
|
||||
123456789012😃😃"
|
||||
})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes(["j"]).await;
|
||||
cx.assert_shared_state(indoc! {"
|
||||
123456789012aaaa
|
||||
123456789012😃ˇ😃"
|
||||
})
|
||||
.await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
123456789012aaˇaa
|
||||
123456789012😃😃"
|
||||
})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes(["j"]).await;
|
||||
cx.assert_shared_state(indoc! {"
|
||||
123456789012aaaa
|
||||
123456789012😃ˇ😃"
|
||||
})
|
||||
.await;
|
||||
|
||||
cx.set_shared_state(indoc! {"
|
||||
123456789012aaaaˇaaaaaaaa123456789012
|
||||
wow
|
||||
123456789012😃😃😃😃😃😃123456789012"
|
||||
})
|
||||
.await;
|
||||
cx.simulate_shared_keystrokes(["j", "j"]).await;
|
||||
cx.assert_shared_state(indoc! {"
|
||||
123456789012aaaaaaaaaaaa123456789012
|
||||
wow
|
||||
123456789012😃😃ˇ😃😃😃😃123456789012"
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
|
|
@ -581,7 +581,7 @@ impl Setting for VimModeSetting {
|
|||
fn local_selections_changed(newest: Selection<usize>, cx: &mut WindowContext) {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
if vim.enabled && vim.state().mode == Mode::Normal && !newest.is_empty() {
|
||||
if matches!(newest.goal, SelectionGoal::ColumnRange { .. }) {
|
||||
if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
|
||||
vim.switch_mode(Mode::VisualBlock, false, cx);
|
||||
} else {
|
||||
vim.switch_mode(Mode::Visual, false, cx)
|
||||
|
|
|
@ -140,17 +140,21 @@ pub fn visual_block_motion(
|
|||
SelectionGoal,
|
||||
) -> Option<(DisplayPoint, SelectionGoal)>,
|
||||
) {
|
||||
let text_layout_details = TextLayoutDetails::new(editor, cx);
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
let map = &s.display_map();
|
||||
let mut head = s.newest_anchor().head().to_display_point(map);
|
||||
let mut tail = s.oldest_anchor().tail().to_display_point(map);
|
||||
|
||||
let (start, end) = match s.newest_anchor().goal {
|
||||
SelectionGoal::ColumnRange { start, end } if preserve_goal => (start, end),
|
||||
SelectionGoal::Column(start) if preserve_goal => (start, start + 1),
|
||||
_ => (tail.column(), head.column()),
|
||||
SelectionGoal::HorizontalRange { start, end } if preserve_goal => (start, end),
|
||||
SelectionGoal::HorizontalPosition(start) if preserve_goal => (start, start + 10.0),
|
||||
_ => (
|
||||
map.x_for_point(tail, &text_layout_details),
|
||||
map.x_for_point(head, &text_layout_details),
|
||||
),
|
||||
};
|
||||
let goal = SelectionGoal::ColumnRange { start, end };
|
||||
let goal = SelectionGoal::HorizontalRange { start, end };
|
||||
|
||||
let was_reversed = tail.column() > head.column();
|
||||
if !was_reversed && !preserve_goal {
|
||||
|
@ -172,21 +176,39 @@ pub fn visual_block_motion(
|
|||
head = movement::saturating_right(map, head)
|
||||
}
|
||||
|
||||
let columns = if is_reversed {
|
||||
head.column()..tail.column()
|
||||
let positions = if is_reversed {
|
||||
map.x_for_point(head, &text_layout_details)..map.x_for_point(tail, &text_layout_details)
|
||||
} else if head.column() == tail.column() {
|
||||
head.column()..(head.column() + 1)
|
||||
map.x_for_point(head, &text_layout_details)
|
||||
..map.x_for_point(head, &text_layout_details) + 10.0
|
||||
} else {
|
||||
tail.column()..head.column()
|
||||
map.x_for_point(tail, &text_layout_details)..map.x_for_point(head, &text_layout_details)
|
||||
};
|
||||
|
||||
let mut selections = Vec::new();
|
||||
let mut row = tail.row();
|
||||
|
||||
loop {
|
||||
let start = map.clip_point(DisplayPoint::new(row, columns.start), Bias::Left);
|
||||
let end = map.clip_point(DisplayPoint::new(row, columns.end), Bias::Left);
|
||||
if columns.start <= map.line_len(row) {
|
||||
let start = map.clip_point(
|
||||
DisplayPoint::new(
|
||||
row,
|
||||
map.column_for_x(row, positions.start, &text_layout_details),
|
||||
),
|
||||
Bias::Left,
|
||||
);
|
||||
let end = map.clip_point(
|
||||
DisplayPoint::new(
|
||||
row,
|
||||
map.column_for_x(row, positions.end, &text_layout_details),
|
||||
),
|
||||
Bias::Left,
|
||||
);
|
||||
if positions.start
|
||||
<= map.x_for_point(
|
||||
DisplayPoint::new(row, map.line_len(row)),
|
||||
&text_layout_details,
|
||||
)
|
||||
{
|
||||
let selection = Selection {
|
||||
id: s.new_selection_id(),
|
||||
start: start.to_point(map),
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
{"Put":{"state":"aaˇaa\n😃😃"}}
|
||||
{"Key":"j"}
|
||||
{"Get":{"state":"aaaa\n😃ˇ😃","mode":"Normal"}}
|
||||
{"Put":{"state":"ˇThe quick brown\nfox jumps"}}
|
||||
{"Key":"j"}
|
||||
{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue