Fix find_{,preceding}boundary to work on buffer text
Before this change the bounday could mistakenly have happened on a soft line wrap. Also fixes interaction with inlays better.
This commit is contained in:
parent
e7ba5a1edb
commit
d3650594c3
10 changed files with 174 additions and 146 deletions
|
@ -3,7 +3,8 @@ use std::{cmp, sync::Arc};
|
|||
use editor::{
|
||||
char_kind,
|
||||
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
|
||||
movement, Bias, CharKind, DisplayPoint, ToOffset,
|
||||
movement::{self, FindRange},
|
||||
Bias, CharKind, DisplayPoint, ToOffset,
|
||||
};
|
||||
use gpui::{actions, impl_actions, AppContext, WindowContext};
|
||||
use language::{Point, Selection, SelectionGoal};
|
||||
|
@ -592,7 +593,7 @@ pub(crate) fn next_word_start(
|
|||
let language = map.buffer_snapshot.language_at(point.to_point(map));
|
||||
for _ in 0..times {
|
||||
let mut crossed_newline = false;
|
||||
point = movement::find_boundary(map, point, |left, right| {
|
||||
point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
|
||||
let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
let at_newline = right == '\n';
|
||||
|
@ -616,8 +617,14 @@ fn next_word_end(
|
|||
) -> DisplayPoint {
|
||||
let language = map.buffer_snapshot.language_at(point.to_point(map));
|
||||
for _ in 0..times {
|
||||
*point.column_mut() += 1;
|
||||
point = movement::find_boundary(map, point, |left, right| {
|
||||
if point.column() < map.line_len(point.row()) {
|
||||
*point.column_mut() += 1;
|
||||
} else if point.row() < map.max_buffer_row() {
|
||||
*point.row_mut() += 1;
|
||||
*point.column_mut() = 0;
|
||||
}
|
||||
// *point.column_mut() += 1;
|
||||
point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
|
||||
let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
|
||||
|
@ -649,12 +656,13 @@ fn previous_word_start(
|
|||
for _ in 0..times {
|
||||
// This works even though find_preceding_boundary is called for every character in the line containing
|
||||
// cursor because the newline is checked only once.
|
||||
point = movement::find_preceding_boundary(map, point, |left, right| {
|
||||
let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
point =
|
||||
movement::find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
|
||||
let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
|
||||
(left_kind != right_kind && !right.is_whitespace()) || left == '\n'
|
||||
});
|
||||
(left_kind != right_kind && !right.is_whitespace()) || left == '\n'
|
||||
});
|
||||
}
|
||||
point
|
||||
}
|
||||
|
|
|
@ -445,7 +445,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_e(cx: &mut gpui::TestAppContext) {
|
||||
async fn test_end_of_word(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await.binding(["e"]);
|
||||
cx.assert_all(indoc! {"
|
||||
Thˇe quicˇkˇ-browˇn
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
|
||||
use editor::{
|
||||
char_kind, display_map::DisplaySnapshot, movement, scroll::autoscroll::Autoscroll, CharKind,
|
||||
DisplayPoint,
|
||||
char_kind,
|
||||
display_map::DisplaySnapshot,
|
||||
movement::{self, FindRange},
|
||||
scroll::autoscroll::Autoscroll,
|
||||
CharKind, DisplayPoint,
|
||||
};
|
||||
use gpui::WindowContext;
|
||||
use language::Selection;
|
||||
|
@ -96,12 +99,15 @@ fn expand_changed_word_selection(
|
|||
.unwrap_or_default();
|
||||
|
||||
if in_word {
|
||||
selection.end = movement::find_boundary(map, selection.end, |left, right| {
|
||||
let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
selection.end =
|
||||
movement::find_boundary(map, selection.end, FindRange::MultiLine, |left, right| {
|
||||
let left_kind =
|
||||
char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind =
|
||||
char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
|
||||
left_kind != right_kind && left_kind != CharKind::Whitespace
|
||||
});
|
||||
left_kind != right_kind && left_kind != CharKind::Whitespace
|
||||
});
|
||||
true
|
||||
} else {
|
||||
Motion::NextWordStart { ignore_punctuation }
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use editor::{char_kind, display_map::DisplaySnapshot, movement, Bias, CharKind, DisplayPoint};
|
||||
use editor::{
|
||||
char_kind,
|
||||
display_map::DisplaySnapshot,
|
||||
movement::{self, FindRange},
|
||||
Bias, CharKind, DisplayPoint,
|
||||
};
|
||||
use gpui::{actions, impl_actions, AppContext, WindowContext};
|
||||
use language::Selection;
|
||||
use serde::Deserialize;
|
||||
|
@ -178,15 +183,16 @@ fn in_word(
|
|||
) -> Option<Range<DisplayPoint>> {
|
||||
// Use motion::right so that we consider the character under the cursor when looking for the start
|
||||
let language = map.buffer_snapshot.language_at(relative_to.to_point(map));
|
||||
let start = movement::find_preceding_boundary_in_line(
|
||||
let start = movement::find_preceding_boundary(
|
||||
map,
|
||||
right(map, relative_to, 1),
|
||||
movement::FindRange::SingleLine,
|
||||
|left, right| {
|
||||
char_kind(language, left).coerce_punctuation(ignore_punctuation)
|
||||
!= char_kind(language, right).coerce_punctuation(ignore_punctuation)
|
||||
},
|
||||
);
|
||||
let end = movement::find_boundary_in_line(map, relative_to, |left, right| {
|
||||
let end = movement::find_boundary(map, relative_to, FindRange::SingleLine, |left, right| {
|
||||
char_kind(language, left).coerce_punctuation(ignore_punctuation)
|
||||
!= char_kind(language, right).coerce_punctuation(ignore_punctuation)
|
||||
});
|
||||
|
@ -241,9 +247,10 @@ fn around_next_word(
|
|||
) -> Option<Range<DisplayPoint>> {
|
||||
let language = map.buffer_snapshot.language_at(relative_to.to_point(map));
|
||||
// Get the start of the word
|
||||
let start = movement::find_preceding_boundary_in_line(
|
||||
let start = movement::find_preceding_boundary(
|
||||
map,
|
||||
right(map, relative_to, 1),
|
||||
FindRange::SingleLine,
|
||||
|left, right| {
|
||||
char_kind(language, left).coerce_punctuation(ignore_punctuation)
|
||||
!= char_kind(language, right).coerce_punctuation(ignore_punctuation)
|
||||
|
@ -251,7 +258,7 @@ fn around_next_word(
|
|||
);
|
||||
|
||||
let mut word_found = false;
|
||||
let end = movement::find_boundary(map, relative_to, |left, right| {
|
||||
let end = movement::find_boundary(map, relative_to, FindRange::MultiLine, |left, right| {
|
||||
let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation);
|
||||
let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation);
|
||||
|
||||
|
@ -566,11 +573,18 @@ mod test {
|
|||
async fn test_visual_word_object(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
cx.set_shared_state("The quick ˇbrown\nfox").await;
|
||||
/*
|
||||
cx.set_shared_state("The quick ˇbrown\nfox").await;
|
||||
cx.simulate_shared_keystrokes(["v"]).await;
|
||||
cx.assert_shared_state("The quick «bˇ»rown\nfox").await;
|
||||
cx.simulate_shared_keystrokes(["i", "w"]).await;
|
||||
cx.assert_shared_state("The quick «brownˇ»\nfox").await;
|
||||
*/
|
||||
cx.set_shared_state("The quick brown\nˇ\nfox").await;
|
||||
cx.simulate_shared_keystrokes(["v"]).await;
|
||||
cx.assert_shared_state("The quick «bˇ»rown\nfox").await;
|
||||
cx.assert_shared_state("The quick brown\n«\nˇ»fox").await;
|
||||
cx.simulate_shared_keystrokes(["i", "w"]).await;
|
||||
cx.assert_shared_state("The quick «brownˇ»\nfox").await;
|
||||
cx.assert_shared_state("The quick brown\n«\nˇ»fox").await;
|
||||
|
||||
cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS)
|
||||
.await;
|
||||
|
|
|
@ -431,6 +431,24 @@ async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
|
|||
twelve char
|
||||
"})
|
||||
.await;
|
||||
|
||||
// line wraps as:
|
||||
// fourteen ch
|
||||
// ar
|
||||
// fourteen ch
|
||||
// ar
|
||||
cx.set_shared_state(indoc! { "
|
||||
fourteen chaˇr
|
||||
fourteen char
|
||||
"})
|
||||
.await;
|
||||
|
||||
cx.simulate_shared_keystrokes(["d", "i", "w"]).await;
|
||||
cx.assert_shared_state(indoc! {"
|
||||
fourteenˇ•
|
||||
fourteen char
|
||||
"})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
|
|
@ -153,6 +153,7 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
}
|
||||
|
||||
pub async fn assert_shared_state(&mut self, marked_text: &str) {
|
||||
let marked_text = marked_text.replace("•", " ");
|
||||
let neovim = self.neovim_state().await;
|
||||
let editor = self.editor_state();
|
||||
if neovim == marked_text && neovim == editor {
|
||||
|
@ -184,9 +185,9 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
message,
|
||||
initial_state,
|
||||
self.recent_keystrokes.join(" "),
|
||||
marked_text,
|
||||
neovim,
|
||||
editor
|
||||
marked_text.replace(" \n", "•\n"),
|
||||
neovim.replace(" \n", "•\n"),
|
||||
editor.replace(" \n", "•\n")
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue