vim: Handle exclusive-linewise edgecase correctly (#27786)
Before this change we didn't explicitly handle vim's exclusive-linewise edgecase (https://neovim.io/doc/user/motion.html#exclusive). Instead we had hard-coded workarounds in a few places to make our tests pass. The most pernicious of these workarounds was that we represented a visual line selection as including the trailing newline (or leading newline for files that end with no newline), which other code had to undo to get back to what the user indended. Closes #21440 Updates #6900 Release Notes: - vim: Fixed `d]}` to not delete the closing brace - vim: Fixed `d}` from the start of the line to not delete the paragraph separator - vim: Fixed `d}` from the middle of the line to not delete the final newline
This commit is contained in:
+45
-18
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
display_map::{DisplayRow, DisplaySnapshot, ToDisplayPoint},
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
movement,
|
||||
scroll::Autoscroll,
|
||||
Bias, DisplayPoint, Editor, ToOffset,
|
||||
@@ -15,7 +15,7 @@ use util::ResultExt;
|
||||
use workspace::searchable::Direction;
|
||||
|
||||
use crate::{
|
||||
motion::{first_non_whitespace, next_line_end, start_of_line, Motion},
|
||||
motion::{first_non_whitespace, next_line_end, start_of_line, Motion, MotionKind},
|
||||
object::Object,
|
||||
state::{Mark, Mode, Operator},
|
||||
Vim,
|
||||
@@ -503,6 +503,7 @@ impl Vim {
|
||||
self.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
let mut original_columns: HashMap<_, _> = Default::default();
|
||||
let line_mode = line_mode || editor.selections.line_mode;
|
||||
editor.selections.line_mode = false;
|
||||
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||
@@ -515,28 +516,49 @@ impl Vim {
|
||||
original_columns.insert(selection.id, position.to_point(map).column);
|
||||
if vim.mode == Mode::VisualBlock {
|
||||
*selection.end.column_mut() = map.line_len(selection.end.row())
|
||||
} else if vim.mode != Mode::VisualLine {
|
||||
selection.start = DisplayPoint::new(selection.start.row(), 0);
|
||||
selection.end =
|
||||
map.next_line_boundary(selection.end.to_point(map)).1;
|
||||
if selection.end.row() == map.max_point().row() {
|
||||
selection.end = map.max_point();
|
||||
if selection.start == selection.end {
|
||||
let prev_row =
|
||||
DisplayRow(selection.start.row().0.saturating_sub(1));
|
||||
selection.start =
|
||||
DisplayPoint::new(prev_row, map.line_len(prev_row));
|
||||
}
|
||||
} else {
|
||||
let start = selection.start.to_point(map);
|
||||
let end = selection.end.to_point(map);
|
||||
selection.start = map.prev_line_boundary(start).1;
|
||||
if end.column == 0 && end > start {
|
||||
let row = end.row.saturating_sub(1);
|
||||
selection.end = Point::new(
|
||||
row,
|
||||
map.buffer_snapshot.line_len(MultiBufferRow(row)),
|
||||
)
|
||||
.to_display_point(map)
|
||||
} else {
|
||||
*selection.end.row_mut() += 1;
|
||||
*selection.end.column_mut() = 0;
|
||||
selection.end = map.next_line_boundary(end).1;
|
||||
}
|
||||
}
|
||||
}
|
||||
selection.goal = SelectionGoal::None;
|
||||
});
|
||||
});
|
||||
vim.copy_selections_content(editor, line_mode, window, cx);
|
||||
let kind = if line_mode {
|
||||
MotionKind::Linewise
|
||||
} else {
|
||||
MotionKind::Exclusive
|
||||
};
|
||||
vim.copy_selections_content(editor, kind, window, cx);
|
||||
|
||||
if line_mode && vim.mode != Mode::VisualBlock {
|
||||
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
let end = selection.end.to_point(map);
|
||||
let start = selection.start.to_point(map);
|
||||
if end.row < map.buffer_snapshot.max_point().row {
|
||||
selection.end = Point::new(end.row + 1, 0).to_display_point(map)
|
||||
} else if start.row > 0 {
|
||||
selection.start = Point::new(
|
||||
start.row - 1,
|
||||
map.buffer_snapshot.line_len(MultiBufferRow(start.row - 1)),
|
||||
)
|
||||
.to_display_point(map)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
editor.insert("", window, cx);
|
||||
|
||||
// Fixup cursor position after the deletion
|
||||
@@ -565,7 +587,12 @@ impl Vim {
|
||||
self.update_editor(window, cx, |vim, editor, window, cx| {
|
||||
let line_mode = line_mode || editor.selections.line_mode;
|
||||
editor.selections.line_mode = line_mode;
|
||||
vim.yank_selections_content(editor, line_mode, window, cx);
|
||||
let kind = if line_mode {
|
||||
MotionKind::Linewise
|
||||
} else {
|
||||
MotionKind::Exclusive
|
||||
};
|
||||
vim.yank_selections_content(editor, kind, window, cx);
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if line_mode {
|
||||
|
||||
Reference in New Issue
Block a user