fix: move word search activation onto its match
This commit is contained in:
+2
-2
@@ -174,11 +174,11 @@ Search is case-sensitive and single-line, with no regular expressions or Unicode
|
|||||||
|
|
||||||
Submitting `/` keeps each caret in place if it is at or inside an occurrence of the query; otherwise, it moves that caret to the next match, wrapping as needed. This also applies to an empty submission repeating the previous query. Use `n`/`N` to explicitly move to the next/previous match.
|
Submitting `/` keeps each caret in place if it is at or inside an occurrence of the query; otherwise, it moves that caret to the next match, wrapping as needed. This also applies to an empty submission repeating the previous query. Use `n`/`N` to explicitly move to the next/previous match.
|
||||||
|
|
||||||
Normal `*` highlights the primary caret's word without moving any caret when it selects a new query or changes the matching mode. Pressing `*` again for the same active word navigates forward; counts such as `2*` then skip to the requested occurrence, with every caret searching the primary word from its own position. Selecting a different word starts a new highlight-only search. It uses the same Unicode letters/digits/underscore boundaries as `w`, so `cat` skips `scatter`, `cat_cat`, and `cat2`. If the caret is not on a keyword, it takes the next keyword on that line; with none, it uses the punctuation run under or after the caret as a literal substring. A blank remainder reports no word and preserves the previous search. This also works in read-only diff text. Bind `config.core.search_word` to remap it.
|
Normal `*` highlights the primary caret's word when it selects a new query or changes the matching mode. Carets at or inside a matching occurrence stay put; carets outside one move to the next occurrence. Pressing `*` again for the same active word navigates forward; counts such as `2*` then skip to the requested occurrence, with every caret searching the primary word from its own position. Selecting a different word activates its search using the same positioning rules. It uses the same Unicode letters/digits/underscore boundaries as `w`, so `cat` skips `scatter`, `cat_cat`, and `cat2`. If the caret is not on a keyword, it takes the next keyword on that line; with none, it uses the punctuation run under or after the caret as a literal substring. A blank remainder reports no word and preserves the previous search. This also works in read-only diff text. Bind `config.core.search_word` to remap it.
|
||||||
|
|
||||||
`n`/`N` and an empty `/` submission retain the whole-word setting; a nonempty `/` query returns to literal substring search. Cancelling `/` preserves both the previous query and its matching behavior.
|
`n`/`N` and an empty `/` submission retain the whole-word setting; a nonempty `/` query returns to literal substring search. Cancelling `/` preserves both the previous query and its matching behavior.
|
||||||
|
|
||||||
Normal-mode Escape hides search highlights without forgetting the query or matching mode. The next `*` re-highlights without moving; `n`/`N` or submitting `/` resumes navigation and restores highlighting. Escape used to cancel a prompt, selection, or pending motion keeps its existing behavior.
|
Normal-mode Escape hides search highlights without forgetting the query or matching mode. The next `*` reactivates highlighting, staying put only when at or inside a match; `n`/`N` or submitting `/` resumes navigation and restores highlighting. Escape used to cancel a prompt, selection, or pending motion keeps its existing behavior.
|
||||||
|
|
||||||
Submitting an empty search repeats the last query. Esc cancels without moving the cursor or replacing the previous query/match. A missing match reports the query and leaves the cursor unchanged; searching without a previous query also leaves the cursor unchanged. Search never edits text or adds undo history. Buffer edits and undo/redo retain the query and recompute its visible highlights, so subsequent navigation searches the current text again.
|
Submitting an empty search repeats the last query. Esc cancels without moving the cursor or replacing the previous query/match. A missing match reports the query and leaves the cursor unchanged; searching without a previous query also leaves the cursor unchanged. Search never edits text or adds undo history. Buffer edits and undo/redo retain the query and recompute its visible highlights, so subsequent navigation searches the current text again.
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -8482,20 +8482,19 @@ impl Editor {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek_search(&mut self, action: Action, count: usize) {
|
fn seek_search(&mut self, mut action: Action, mut count: usize) {
|
||||||
self.reveal_caret();
|
self.reveal_caret();
|
||||||
let primary_origin = if action == Action::SearchWord {
|
let primary_origin = if action == Action::SearchWord {
|
||||||
let Some((origin, newly_active)) = self.set_word_search() else {
|
let Some((origin, newly_active)) = self.set_word_search() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if newly_active {
|
if newly_active {
|
||||||
self.active.view.search_match = Some(SearchMatch {
|
action = Action::Search;
|
||||||
start: origin,
|
count = 1;
|
||||||
end_column: origin.column + grapheme_count(&self.active.view.search_query),
|
None
|
||||||
});
|
} else {
|
||||||
return;
|
Some(origin)
|
||||||
}
|
}
|
||||||
Some(origin)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|||||||
+30
-2
@@ -120,6 +120,36 @@ fn wait_until(session: &mut EditorSession, ready: impl Fn(&EditorSession) -> boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn star_activation_moves_onto_the_selected_word_only_when_outside_it() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
for (text, column, expected) in [
|
||||||
|
(" word word", 0, 2),
|
||||||
|
(" word word", 1, 2),
|
||||||
|
(" word word", 2, 2),
|
||||||
|
(" word word", 4, 4),
|
||||||
|
("-- word word", 0, 3),
|
||||||
|
("\t界e\u{301} 界e\u{301}", 0, 1),
|
||||||
|
(" .* .*", 0, 1),
|
||||||
|
] {
|
||||||
|
let mut editor = fixture.editor(text);
|
||||||
|
keys(&mut editor, &"l".repeat(column));
|
||||||
|
keys(&mut editor, "*");
|
||||||
|
assert_eq!(
|
||||||
|
editor.cursor(),
|
||||||
|
Cursor {
|
||||||
|
row: 0,
|
||||||
|
column: expected
|
||||||
|
},
|
||||||
|
"{text:?}"
|
||||||
|
);
|
||||||
|
let first = editor.search_match().unwrap().start;
|
||||||
|
keys(&mut editor, "*");
|
||||||
|
assert!(editor.cursor().column > first.column, "{text:?}");
|
||||||
|
assert!(!editor.dirty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn submitting_search_keeps_each_caret_inside_a_current_match() {
|
fn submitting_search_keeps_each_caret_inside_a_current_match() {
|
||||||
let fixture = Fixture::new();
|
let fixture = Fixture::new();
|
||||||
@@ -253,9 +283,7 @@ fn star_search_uses_unicode_words_and_vim_same_line_fallbacks() {
|
|||||||
] {
|
] {
|
||||||
let mut editor = fixture.editor(text);
|
let mut editor = fixture.editor(text);
|
||||||
keys(&mut editor, positioning);
|
keys(&mut editor, positioning);
|
||||||
let origin = editor.cursor();
|
|
||||||
editor.handle(Input::Char('*'));
|
editor.handle(Input::Char('*'));
|
||||||
assert_eq!(editor.cursor(), origin, "{text:?}");
|
|
||||||
editor.handle(Input::Char('*'));
|
editor.handle(Input::Char('*'));
|
||||||
assert_eq!(editor.cursor(), Cursor { row, column: 0 }, "{text:?}");
|
assert_eq!(editor.cursor(), Cursor { row, column: 0 }, "{text:?}");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
Reference in New Issue
Block a user