mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Fix f,t on soft-wrapped lines
Also remove the (dangerously confusing) display_map.find_while
This commit is contained in:
parent
c2c04616b4
commit
5f897f45a8
@ -555,67 +555,6 @@ impl DisplaySnapshot {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of the start positions of the occurrences of `target` in the `self` after `from`
|
|
||||||
/// Stops if `condition` returns false for any of the character position pairs observed.
|
|
||||||
pub fn find_while<'a>(
|
|
||||||
&'a self,
|
|
||||||
from: DisplayPoint,
|
|
||||||
target: &str,
|
|
||||||
condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
|
|
||||||
) -> impl Iterator<Item = DisplayPoint> + 'a {
|
|
||||||
Self::find_internal(self.chars_at(from), target.chars().collect(), condition)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator of the end positions of the occurrences of `target` in the `self` before `from`
|
|
||||||
/// Stops if `condition` returns false for any of the character position pairs observed.
|
|
||||||
pub fn reverse_find_while<'a>(
|
|
||||||
&'a self,
|
|
||||||
from: DisplayPoint,
|
|
||||||
target: &str,
|
|
||||||
condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
|
|
||||||
) -> impl Iterator<Item = DisplayPoint> + 'a {
|
|
||||||
Self::find_internal(
|
|
||||||
self.reverse_chars_at(from),
|
|
||||||
target.chars().rev().collect(),
|
|
||||||
condition,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_internal<'a>(
|
|
||||||
iterator: impl Iterator<Item = (char, DisplayPoint)> + 'a,
|
|
||||||
target: Vec<char>,
|
|
||||||
mut condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
|
|
||||||
) -> impl Iterator<Item = DisplayPoint> + 'a {
|
|
||||||
// List of partial matches with the index of the last seen character in target and the starting point of the match
|
|
||||||
let mut partial_matches: Vec<(usize, DisplayPoint)> = Vec::new();
|
|
||||||
iterator
|
|
||||||
.take_while(move |(ch, point)| condition(*ch, *point))
|
|
||||||
.filter_map(move |(ch, point)| {
|
|
||||||
if Some(&ch) == target.get(0) {
|
|
||||||
partial_matches.push((0, point));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut found = None;
|
|
||||||
// Keep partial matches that have the correct next character
|
|
||||||
partial_matches.retain_mut(|(match_position, match_start)| {
|
|
||||||
if target.get(*match_position) == Some(&ch) {
|
|
||||||
*match_position += 1;
|
|
||||||
if *match_position == target.len() {
|
|
||||||
found = Some(match_start.clone());
|
|
||||||
// This match is completed. No need to keep tracking it
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
found
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
|
pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut column = 0;
|
let mut column = 0;
|
||||||
@ -933,7 +872,7 @@ pub mod tests {
|
|||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{env, sync::Arc};
|
use std::{env, sync::Arc};
|
||||||
use theme::SyntaxTheme;
|
use theme::SyntaxTheme;
|
||||||
use util::test::{marked_text_offsets, marked_text_ranges, sample_text};
|
use util::test::{marked_text_ranges, sample_text};
|
||||||
use Bias::*;
|
use Bias::*;
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
@ -1744,32 +1683,6 @@ pub mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_internal() {
|
|
||||||
assert("This is a ˇtest of find internal", "test");
|
|
||||||
assert("Some text ˇaˇaˇaa with repeated characters", "aa");
|
|
||||||
|
|
||||||
fn assert(marked_text: &str, target: &str) {
|
|
||||||
let (text, expected_offsets) = marked_text_offsets(marked_text);
|
|
||||||
|
|
||||||
let chars = text
|
|
||||||
.chars()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, ch)| (ch, DisplayPoint::new(0, index as u32)));
|
|
||||||
let target = target.chars();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
expected_offsets
|
|
||||||
.into_iter()
|
|
||||||
.map(|offset| offset as u32)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
DisplaySnapshot::find_internal(chars, target.collect(), |_, _| true)
|
|
||||||
.map(|point| point.column())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn syntax_chunks<'a>(
|
fn syntax_chunks<'a>(
|
||||||
rows: Range<u32>,
|
rows: Range<u32>,
|
||||||
map: &ModelHandle<DisplayMap>,
|
map: &ModelHandle<DisplayMap>,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::{cmp, sync::Arc};
|
use std::cmp;
|
||||||
|
|
||||||
use editor::{
|
use editor::{
|
||||||
char_kind,
|
char_kind,
|
||||||
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
|
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
|
||||||
movement::{self, FindRange},
|
movement::{self, find_boundary, find_preceding_boundary, FindRange},
|
||||||
Bias, CharKind, DisplayPoint, ToOffset,
|
Bias, CharKind, DisplayPoint, ToOffset,
|
||||||
};
|
};
|
||||||
use gpui::{actions, impl_actions, AppContext, WindowContext};
|
use gpui::{actions, impl_actions, AppContext, WindowContext};
|
||||||
@ -37,8 +37,8 @@ pub enum Motion {
|
|||||||
StartOfDocument,
|
StartOfDocument,
|
||||||
EndOfDocument,
|
EndOfDocument,
|
||||||
Matching,
|
Matching,
|
||||||
FindForward { before: bool, text: Arc<str> },
|
FindForward { before: bool, char: char },
|
||||||
FindBackward { after: bool, text: Arc<str> },
|
FindBackward { after: bool, char: char },
|
||||||
NextLineStart,
|
NextLineStart,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,25 +233,25 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
|
|||||||
|
|
||||||
fn repeat_motion(backwards: bool, cx: &mut WindowContext) {
|
fn repeat_motion(backwards: bool, cx: &mut WindowContext) {
|
||||||
let find = match Vim::read(cx).workspace_state.last_find.clone() {
|
let find = match Vim::read(cx).workspace_state.last_find.clone() {
|
||||||
Some(Motion::FindForward { before, text }) => {
|
Some(Motion::FindForward { before, char }) => {
|
||||||
if backwards {
|
if backwards {
|
||||||
Motion::FindBackward {
|
Motion::FindBackward {
|
||||||
after: before,
|
after: before,
|
||||||
text,
|
char,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Motion::FindForward { before, text }
|
Motion::FindForward { before, char }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Motion::FindBackward { after, text }) => {
|
Some(Motion::FindBackward { after, char }) => {
|
||||||
if backwards {
|
if backwards {
|
||||||
Motion::FindForward {
|
Motion::FindForward {
|
||||||
before: after,
|
before: after,
|
||||||
text,
|
char,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Motion::FindBackward { after, text }
|
Motion::FindBackward { after, char }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
@ -403,12 +403,12 @@ impl Motion {
|
|||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
),
|
),
|
||||||
Matching => (matching(map, point), SelectionGoal::None),
|
Matching => (matching(map, point), SelectionGoal::None),
|
||||||
FindForward { before, text } => (
|
FindForward { before, char } => (
|
||||||
find_forward(map, point, *before, text.clone(), times),
|
find_forward(map, point, *before, *char, times),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
),
|
),
|
||||||
FindBackward { after, text } => (
|
FindBackward { after, char } => (
|
||||||
find_backward(map, point, *after, text.clone(), times),
|
find_backward(map, point, *after, *char, times),
|
||||||
SelectionGoal::None,
|
SelectionGoal::None,
|
||||||
),
|
),
|
||||||
NextLineStart => (next_line_start(map, point, times), SelectionGoal::None),
|
NextLineStart => (next_line_start(map, point, times), SelectionGoal::None),
|
||||||
@ -793,44 +793,55 @@ fn find_forward(
|
|||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
from: DisplayPoint,
|
from: DisplayPoint,
|
||||||
before: bool,
|
before: bool,
|
||||||
target: Arc<str>,
|
target: char,
|
||||||
times: usize,
|
times: usize,
|
||||||
) -> DisplayPoint {
|
) -> DisplayPoint {
|
||||||
map.find_while(from, target.as_ref(), |ch, _| ch != '\n')
|
let mut to = from;
|
||||||
.skip_while(|found_at| found_at == &from)
|
let mut found = false;
|
||||||
.nth(times - 1)
|
|
||||||
.map(|mut found| {
|
for _ in 0..times {
|
||||||
if before {
|
found = false;
|
||||||
*found.column_mut() -= 1;
|
to = find_boundary(map, to, FindRange::SingleLine, |_, right| {
|
||||||
found = map.clip_point(found, Bias::Right);
|
found = right == target;
|
||||||
found
|
|
||||||
} else {
|
|
||||||
found
|
found
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
if before && to.column() > 0 {
|
||||||
|
*to.column_mut() -= 1;
|
||||||
|
map.clip_point(to, Bias::Left)
|
||||||
|
} else {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
from
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.unwrap_or(from)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_backward(
|
fn find_backward(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
from: DisplayPoint,
|
from: DisplayPoint,
|
||||||
after: bool,
|
after: bool,
|
||||||
target: Arc<str>,
|
target: char,
|
||||||
times: usize,
|
times: usize,
|
||||||
) -> DisplayPoint {
|
) -> DisplayPoint {
|
||||||
map.reverse_find_while(from, target.as_ref(), |ch, _| ch != '\n')
|
let mut to = from;
|
||||||
.skip_while(|found_at| found_at == &from)
|
|
||||||
.nth(times - 1)
|
for _ in 0..times {
|
||||||
.map(|mut found| {
|
to = find_preceding_boundary(map, to, FindRange::SingleLine, |_, right| right == target);
|
||||||
if after {
|
}
|
||||||
*found.column_mut() += 1;
|
|
||||||
found = map.clip_point(found, Bias::Left);
|
if map.buffer_snapshot.chars_at(to.to_point(map)).next() == Some(target) {
|
||||||
found
|
if after {
|
||||||
} else {
|
*to.column_mut() += 1;
|
||||||
found
|
map.clip_point(to, Bias::Right)
|
||||||
|
} else {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
from
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.unwrap_or(from)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
|
fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
|
||||||
|
@ -780,6 +780,7 @@ mod test {
|
|||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_f_and_t(cx: &mut gpui::TestAppContext) {
|
async fn test_f_and_t(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||||
|
|
||||||
for count in 1..=3 {
|
for count in 1..=3 {
|
||||||
let test_case = indoc! {"
|
let test_case = indoc! {"
|
||||||
ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
|
ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
|
||||||
|
@ -449,6 +449,13 @@ async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
|
|||||||
fourteen char
|
fourteen char
|
||||||
"})
|
"})
|
||||||
.await;
|
.await;
|
||||||
|
cx.simulate_shared_keystrokes(["j", "shift-f", "e", "f", "r"])
|
||||||
|
.await;
|
||||||
|
cx.assert_shared_state(indoc! {"
|
||||||
|
fourteen•
|
||||||
|
fourteen chaˇr
|
||||||
|
"})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -295,14 +295,20 @@ impl Vim {
|
|||||||
|
|
||||||
match Vim::read(cx).active_operator() {
|
match Vim::read(cx).active_operator() {
|
||||||
Some(Operator::FindForward { before }) => {
|
Some(Operator::FindForward { before }) => {
|
||||||
let find = Motion::FindForward { before, text };
|
let find = Motion::FindForward {
|
||||||
|
before,
|
||||||
|
char: text.chars().next().unwrap(),
|
||||||
|
};
|
||||||
Vim::update(cx, |vim, _| {
|
Vim::update(cx, |vim, _| {
|
||||||
vim.workspace_state.last_find = Some(find.clone())
|
vim.workspace_state.last_find = Some(find.clone())
|
||||||
});
|
});
|
||||||
motion::motion(find, cx)
|
motion::motion(find, cx)
|
||||||
}
|
}
|
||||||
Some(Operator::FindBackward { after }) => {
|
Some(Operator::FindBackward { after }) => {
|
||||||
let find = Motion::FindBackward { after, text };
|
let find = Motion::FindBackward {
|
||||||
|
after,
|
||||||
|
char: text.chars().next().unwrap(),
|
||||||
|
};
|
||||||
Vim::update(cx, |vim, _| {
|
Vim::update(cx, |vim, _| {
|
||||||
vim.workspace_state.last_find = Some(find.clone())
|
vim.workspace_state.last_find = Some(find.clone())
|
||||||
});
|
});
|
||||||
|
@ -53,3 +53,9 @@
|
|||||||
{"Key":"i"}
|
{"Key":"i"}
|
||||||
{"Key":"w"}
|
{"Key":"w"}
|
||||||
{"Get":{"state":"fourteenˇ \nfourteen char\n","mode":"Normal"}}
|
{"Get":{"state":"fourteenˇ \nfourteen char\n","mode":"Normal"}}
|
||||||
|
{"Key":"j"}
|
||||||
|
{"Key":"shift-f"}
|
||||||
|
{"Key":"e"}
|
||||||
|
{"Key":"f"}
|
||||||
|
{"Key":"r"}
|
||||||
|
{"Get":{"state":"fourteen \nfourteen chaˇr\n","mode":"Normal"}}
|
||||||
|
Loading…
Reference in New Issue
Block a user