parent
c6a42c19de
commit
af8389c41c
15 changed files with 152 additions and 95 deletions
|
@ -678,6 +678,19 @@ impl<'a> MutableSelectionsCollection<'a> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn maybe_move_cursors_with(
|
||||||
|
&mut self,
|
||||||
|
mut update_cursor_position: impl FnMut(
|
||||||
|
&DisplaySnapshot,
|
||||||
|
DisplayPoint,
|
||||||
|
SelectionGoal,
|
||||||
|
) -> Option<(DisplayPoint, SelectionGoal)>,
|
||||||
|
) {
|
||||||
|
self.move_cursors_with(|map, point, goal| {
|
||||||
|
update_cursor_position(map, point, goal).unwrap_or((point, goal))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn replace_cursors_with(
|
pub fn replace_cursors_with(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec<DisplayPoint>,
|
mut find_replacement_cursors: impl FnMut(&DisplaySnapshot) -> Vec<DisplayPoint>,
|
||||||
|
|
|
@ -137,6 +137,11 @@ impl Motion {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn infallible(self) -> bool {
|
||||||
|
use Motion::*;
|
||||||
|
matches!(self, StartOfDocument | CurrentLine | EndOfDocument)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inclusive(self) -> bool {
|
pub fn inclusive(self) -> bool {
|
||||||
use Motion::*;
|
use Motion::*;
|
||||||
match self {
|
match self {
|
||||||
|
@ -164,9 +169,9 @@ impl Motion {
|
||||||
point: DisplayPoint,
|
point: DisplayPoint,
|
||||||
goal: SelectionGoal,
|
goal: SelectionGoal,
|
||||||
times: usize,
|
times: usize,
|
||||||
) -> (DisplayPoint, SelectionGoal) {
|
) -> Option<(DisplayPoint, SelectionGoal)> {
|
||||||
use Motion::*;
|
use Motion::*;
|
||||||
match self {
|
let (new_point, goal) = match self {
|
||||||
Left => (left(map, point, times), SelectionGoal::None),
|
Left => (left(map, point, times), SelectionGoal::None),
|
||||||
Backspace => (backspace(map, point, times), SelectionGoal::None),
|
Backspace => (backspace(map, point, times), SelectionGoal::None),
|
||||||
Down => down(map, point, goal, times),
|
Down => down(map, point, goal, times),
|
||||||
|
@ -191,7 +196,9 @@ impl Motion {
|
||||||
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
|
StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
|
||||||
EndOfDocument => (end_of_document(map, point, times), SelectionGoal::None),
|
EndOfDocument => (end_of_document(map, point, times), SelectionGoal::None),
|
||||||
Matching => (matching(map, point), SelectionGoal::None),
|
Matching => (matching(map, point), SelectionGoal::None),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
(new_point != point || self.infallible()).then_some((new_point, goal))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expands a selection using self motion for an operator
|
// Expands a selection using self motion for an operator
|
||||||
|
@ -201,12 +208,13 @@ impl Motion {
|
||||||
selection: &mut Selection<DisplayPoint>,
|
selection: &mut Selection<DisplayPoint>,
|
||||||
times: usize,
|
times: usize,
|
||||||
expand_to_surrounding_newline: bool,
|
expand_to_surrounding_newline: bool,
|
||||||
) {
|
) -> bool {
|
||||||
let (new_head, goal) = self.move_point(map, selection.head(), selection.goal, times);
|
if let Some((new_head, goal)) =
|
||||||
selection.set_head(new_head, goal);
|
self.move_point(map, selection.head(), selection.goal, times)
|
||||||
|
{
|
||||||
|
selection.set_head(new_head, goal);
|
||||||
|
|
||||||
if self.linewise() {
|
if self.linewise() {
|
||||||
if selection.start != selection.end {
|
|
||||||
selection.start = map.prev_line_boundary(selection.start.to_point(map)).1;
|
selection.start = map.prev_line_boundary(selection.start.to_point(map)).1;
|
||||||
|
|
||||||
if expand_to_surrounding_newline {
|
if expand_to_surrounding_newline {
|
||||||
|
@ -215,7 +223,7 @@ impl Motion {
|
||||||
*selection.end.column_mut() = 0;
|
*selection.end.column_mut() = 0;
|
||||||
selection.end = map.clip_point(selection.end, Bias::Right);
|
selection.end = map.clip_point(selection.end, Bias::Right);
|
||||||
// Don't reset the end here
|
// Don't reset the end here
|
||||||
return;
|
return true;
|
||||||
} else if selection.start.row() > 0 {
|
} else if selection.start.row() > 0 {
|
||||||
*selection.start.row_mut() -= 1;
|
*selection.start.row_mut() -= 1;
|
||||||
*selection.start.column_mut() = map.line_len(selection.start.row());
|
*selection.start.column_mut() = map.line_len(selection.start.row());
|
||||||
|
@ -224,31 +232,33 @@ impl Motion {
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
|
(_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
|
||||||
}
|
} else {
|
||||||
} else {
|
// If the motion is exclusive and the end of the motion is in column 1, the
|
||||||
// If the motion is exclusive and the end of the motion is in column 1, the
|
// end of the motion is moved to the end of the previous line and the motion
|
||||||
// end of the motion is moved to the end of the previous line and the motion
|
// becomes inclusive. Example: "}" moves to the first line after a paragraph,
|
||||||
// becomes inclusive. Example: "}" moves to the first line after a paragraph,
|
// but "d}" will not include that line.
|
||||||
// but "d}" will not include that line.
|
let mut inclusive = self.inclusive();
|
||||||
let mut inclusive = self.inclusive();
|
if !inclusive
|
||||||
if !inclusive
|
&& self != Motion::Backspace
|
||||||
&& self != Motion::Backspace
|
&& selection.end.row() > selection.start.row()
|
||||||
&& selection.end.row() > selection.start.row()
|
&& selection.end.column() == 0
|
||||||
&& selection.end.column() == 0
|
{
|
||||||
&& selection.end.row() > 0
|
inclusive = true;
|
||||||
{
|
*selection.end.row_mut() -= 1;
|
||||||
inclusive = true;
|
*selection.end.column_mut() = 0;
|
||||||
*selection.end.row_mut() -= 1;
|
selection.end = map.clip_point(
|
||||||
*selection.end.column_mut() = 0;
|
map.next_line_boundary(selection.end.to_point(map)).1,
|
||||||
selection.end = map.clip_point(
|
Bias::Left,
|
||||||
map.next_line_boundary(selection.end.to_point(map)).1,
|
);
|
||||||
Bias::Left,
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if inclusive && selection.end.column() < map.line_len(selection.end.row()) {
|
if inclusive && selection.end.column() < map.line_len(selection.end.row()) {
|
||||||
*selection.end.column_mut() += 1;
|
*selection.end.column_mut() += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,9 +335,7 @@ pub(crate) fn next_word_start(
|
||||||
|| at_newline && crossed_newline
|
|| at_newline && crossed_newline
|
||||||
|| at_newline && left == '\n'; // Prevents skipping repeated empty lines
|
|| at_newline && left == '\n'; // Prevents skipping repeated empty lines
|
||||||
|
|
||||||
if at_newline {
|
crossed_newline |= at_newline;
|
||||||
crossed_newline = true;
|
|
||||||
}
|
|
||||||
found
|
found
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -350,7 +358,7 @@ fn next_word_end(
|
||||||
});
|
});
|
||||||
|
|
||||||
// find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know
|
// find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know
|
||||||
// we have backtraced already
|
// we have backtracked already
|
||||||
if !map
|
if !map
|
||||||
.chars_at(point)
|
.chars_at(point)
|
||||||
.nth(1)
|
.nth(1)
|
||||||
|
|
|
@ -115,7 +115,11 @@ pub fn normal_object(object: Object, cx: &mut MutableAppContext) {
|
||||||
fn move_cursor(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) {
|
fn move_cursor(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) {
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| motion.move_point(map, cursor, goal, times))
|
s.move_cursors_with(|map, cursor, goal| {
|
||||||
|
motion
|
||||||
|
.move_point(map, cursor, goal, times)
|
||||||
|
.unwrap_or((cursor, goal))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -125,7 +129,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
|
||||||
vim.switch_mode(Mode::Insert, false, cx);
|
vim.switch_mode(Mode::Insert, false, cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::Right.move_point(map, cursor, goal, 1)
|
Motion::Right.move_point(map, cursor, goal, 1)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -142,7 +146,7 @@ fn insert_first_non_whitespace(
|
||||||
vim.switch_mode(Mode::Insert, false, cx);
|
vim.switch_mode(Mode::Insert, false, cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::FirstNonWhitespace.move_point(map, cursor, goal, 1)
|
Motion::FirstNonWhitespace.move_point(map, cursor, goal, 1)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -155,7 +159,7 @@ fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewConte
|
||||||
vim.switch_mode(Mode::Insert, false, cx);
|
vim.switch_mode(Mode::Insert, false, cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::EndOfLine.move_point(map, cursor, goal, 1)
|
Motion::EndOfLine.move_point(map, cursor, goal, 1)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -215,7 +219,7 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex
|
||||||
(end_of_line..end_of_line, new_text)
|
(end_of_line..end_of_line, new_text)
|
||||||
});
|
});
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.move_cursors_with(|map, cursor, goal| {
|
s.maybe_move_cursors_with(|map, cursor, goal| {
|
||||||
Motion::EndOfLine.move_point(map, cursor, goal, 1)
|
Motion::EndOfLine.move_point(map, cursor, goal, 1)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,27 +1,40 @@
|
||||||
use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
|
use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
|
||||||
use editor::{char_kind, display_map::DisplaySnapshot, movement, Autoscroll, DisplayPoint};
|
use editor::{
|
||||||
|
char_kind, display_map::DisplaySnapshot, movement, Autoscroll, CharKind, DisplayPoint,
|
||||||
|
};
|
||||||
use gpui::MutableAppContext;
|
use gpui::MutableAppContext;
|
||||||
use language::Selection;
|
use language::Selection;
|
||||||
|
|
||||||
pub fn change_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) {
|
pub fn change_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) {
|
||||||
|
// Some motions ignore failure when switching to normal mode
|
||||||
|
let mut motion_succeeded = matches!(
|
||||||
|
motion,
|
||||||
|
Motion::Left | Motion::Right | Motion::EndOfLine | Motion::Backspace | Motion::StartOfLine
|
||||||
|
);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
editor.transact(cx, |editor, cx| {
|
editor.transact(cx, |editor, cx| {
|
||||||
// We are swapping to insert mode anyway. Just set the line end clipping behavior now
|
// We are swapping to insert mode anyway. Just set the line end clipping behavior now
|
||||||
editor.set_clip_at_line_ends(false, cx);
|
editor.set_clip_at_line_ends(false, cx);
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.move_with(|map, selection| {
|
s.move_with(|map, selection| {
|
||||||
if let Motion::NextWordStart { ignore_punctuation } = motion {
|
motion_succeeded |= if let Motion::NextWordStart { ignore_punctuation } = motion
|
||||||
expand_changed_word_selection(map, selection, times, ignore_punctuation);
|
{
|
||||||
|
expand_changed_word_selection(map, selection, times, ignore_punctuation)
|
||||||
} else {
|
} else {
|
||||||
motion.expand_selection(map, selection, times, false);
|
motion.expand_selection(map, selection, times, false)
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
copy_selections_content(editor, motion.linewise(), cx);
|
copy_selections_content(editor, motion.linewise(), cx);
|
||||||
editor.insert("", cx);
|
editor.insert("", cx);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
vim.switch_mode(Mode::Insert, false, cx)
|
|
||||||
|
if motion_succeeded {
|
||||||
|
vim.switch_mode(Mode::Insert, false, cx)
|
||||||
|
} else {
|
||||||
|
vim.switch_mode(Mode::Normal, false, cx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) {
|
pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) {
|
||||||
|
@ -49,36 +62,45 @@ pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Mutab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// From the docs https://vimhelp.org/change.txt.html#cw
|
// From the docs https://vimdoc.sourceforge.net/htmldoc/motion.html
|
||||||
// Special case: When the cursor is in a word, "cw" and "cW" do not include the
|
// Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is
|
||||||
// white space after a word, they only change up to the end of the word. This is
|
// on a non-blank. This is because "cw" is interpreted as change-word, and a
|
||||||
// because Vim interprets "cw" as change-word, and a word does not include the
|
// word does not include the following white space. {Vi: "cw" when on a blank
|
||||||
// following white space.
|
// followed by other blanks changes only the first blank; this is probably a
|
||||||
|
// bug, because "dw" deletes all the blanks}
|
||||||
|
//
|
||||||
|
// NOT HANDLED YET
|
||||||
|
// Another special case: When using the "w" motion in combination with an
|
||||||
|
// operator and the last word moved over is at the end of a line, the end of
|
||||||
|
// that word becomes the end of the operated text, not the first word in the
|
||||||
|
// next line.
|
||||||
fn expand_changed_word_selection(
|
fn expand_changed_word_selection(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
selection: &mut Selection<DisplayPoint>,
|
selection: &mut Selection<DisplayPoint>,
|
||||||
times: usize,
|
times: usize,
|
||||||
ignore_punctuation: bool,
|
ignore_punctuation: bool,
|
||||||
) {
|
) -> bool {
|
||||||
if times > 1 {
|
if times == 1 {
|
||||||
Motion::NextWordStart { ignore_punctuation }.expand_selection(
|
let in_word = map
|
||||||
map,
|
.chars_at(selection.head())
|
||||||
selection,
|
.next()
|
||||||
times - 1,
|
.map(|(c, _)| char_kind(c) != CharKind::Whitespace)
|
||||||
false,
|
.unwrap_or_default();
|
||||||
);
|
|
||||||
|
if in_word {
|
||||||
|
selection.end = movement::find_boundary(map, selection.end, |left, right| {
|
||||||
|
let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
|
||||||
|
let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
|
||||||
|
|
||||||
|
left_kind != right_kind && left_kind != CharKind::Whitespace
|
||||||
|
});
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, 1, false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Motion::NextWordStart { ignore_punctuation }.expand_selection(map, selection, times, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if times == 1 && selection.end.column() == map.line_len(selection.end.row()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.end = movement::find_boundary(map, selection.end, |left, right| {
|
|
||||||
let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation);
|
|
||||||
let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation);
|
|
||||||
|
|
||||||
left_kind != right_kind || left == '\n' || right == '\n'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -143,7 +143,7 @@ mod test {
|
||||||
Test test
|
Test test
|
||||||
ˇ
|
ˇ
|
||||||
test"},
|
test"},
|
||||||
ExemptionFeatures::DeletionOnEmptyLine,
|
ExemptionFeatures::DeleteWordOnEmptyLine,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ mod test {
|
||||||
Test test
|
Test test
|
||||||
ˇ
|
ˇ
|
||||||
test"},
|
test"},
|
||||||
ExemptionFeatures::DeletionOnEmptyLine,
|
ExemptionFeatures::OperatorLastNewlineRemains,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,10 @@ use util::test::marked_text_offsets;
|
||||||
use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext};
|
use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext};
|
||||||
use crate::state::Mode;
|
use crate::state::Mode;
|
||||||
|
|
||||||
pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[];
|
pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[
|
||||||
|
ExemptionFeatures::DeletionOnEmptyLine,
|
||||||
|
ExemptionFeatures::OperatorAbortsOnFailedMotion,
|
||||||
|
];
|
||||||
|
|
||||||
/// Enum representing features we have tests for but which don't work, yet. Used
|
/// Enum representing features we have tests for but which don't work, yet. Used
|
||||||
/// to add exemptions and automatically
|
/// to add exemptions and automatically
|
||||||
|
@ -19,6 +22,10 @@ pub enum ExemptionFeatures {
|
||||||
DeletionOnEmptyLine,
|
DeletionOnEmptyLine,
|
||||||
// When a motion fails, it should should not apply linewise operations
|
// When a motion fails, it should should not apply linewise operations
|
||||||
OperatorAbortsOnFailedMotion,
|
OperatorAbortsOnFailedMotion,
|
||||||
|
// When an operator completes at the end of the file, an extra newline is left
|
||||||
|
OperatorLastNewlineRemains,
|
||||||
|
// Deleting a word on an empty line doesn't remove the newline
|
||||||
|
DeleteWordOnEmptyLine,
|
||||||
|
|
||||||
// OBJECTS
|
// OBJECTS
|
||||||
// Resulting position after the operation is slightly incorrect for unintuitive reasons.
|
// Resulting position after the operation is slightly incorrect for unintuitive reasons.
|
||||||
|
|
|
@ -30,20 +30,23 @@ pub fn visual_motion(motion: Motion, times: usize, cx: &mut MutableAppContext) {
|
||||||
s.move_with(|map, selection| {
|
s.move_with(|map, selection| {
|
||||||
let was_reversed = selection.reversed;
|
let was_reversed = selection.reversed;
|
||||||
|
|
||||||
let (new_head, goal) =
|
if let Some((new_head, goal)) =
|
||||||
motion.move_point(map, selection.head(), selection.goal, times);
|
motion.move_point(map, selection.head(), selection.goal, times)
|
||||||
selection.set_head(new_head, goal);
|
{
|
||||||
|
selection.set_head(new_head, goal);
|
||||||
|
|
||||||
if was_reversed && !selection.reversed {
|
if was_reversed && !selection.reversed {
|
||||||
// Head was at the start of the selection, and now is at the end. We need to move the start
|
// Head was at the start of the selection, and now is at the end. We need to move the start
|
||||||
// back by one if possible in order to compensate for this change.
|
// back by one if possible in order to compensate for this change.
|
||||||
*selection.start.column_mut() = selection.start.column().saturating_sub(1);
|
*selection.start.column_mut() =
|
||||||
selection.start = map.clip_point(selection.start, Bias::Left);
|
selection.start.column().saturating_sub(1);
|
||||||
} else if !was_reversed && selection.reversed {
|
selection.start = map.clip_point(selection.start, Bias::Left);
|
||||||
// Head was at the end of the selection, and now is at the start. We need to move the end
|
} else if !was_reversed && selection.reversed {
|
||||||
// forward by one if possible in order to compensate for this change.
|
// Head was at the end of the selection, and now is at the start. We need to move the end
|
||||||
*selection.end.column_mut() = selection.end.column() + 1;
|
// forward by one if possible in order to compensate for this change.
|
||||||
selection.end = map.clip_point(selection.end, Bias::Right);
|
*selection.end.column_mut() = selection.end.column() + 1;
|
||||||
|
selection.end = map.clip_point(selection.end, Bias::Right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]
|
[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"}]
|
|
@ -1 +1 @@
|
||||||
[{"Text":"\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}]
|
[{"Text":"\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nbrown fox\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nbrown fox\njumps over\nthe lazy"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}]
|
|
@ -1 +1 @@
|
||||||
[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}]
|
[{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,6],"end":[2,6]}},{"Mode":"Normal"},{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"}]
|
|
@ -1 +1 @@
|
||||||
[{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}]
|
[{"Text":"\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]
|
|
@ -1 +1 @@
|
||||||
[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"}]
|
[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}]
|
|
@ -1 +1 @@
|
||||||
[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"}]
|
[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,5],"end":[2,5]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"}]
|
|
@ -1 +1 @@
|
||||||
[{"Text":"jumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]
|
[{"Text":"jumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over\nthe lazy"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}]
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue