This commit is contained in:
Mathieu Agopian 2024-11-20 17:56:41 -06:00 committed by GitHub
commit 03bd4445c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 84 additions and 16 deletions

View File

@ -154,13 +154,13 @@ ### Search
Search commands all operate on the `/` register by default. To use a different register, use `"<char>`.
| Key | Description | Command |
| ----- | ----------- | ------- |
| `/` | Search for regex pattern | `search` |
| `?` | Search for previous pattern | `rsearch` |
| `n` | Select next search match | `search_next` |
| `N` | Select previous search match | `search_prev` |
| `*` | Use current selection as the search pattern | `search_selection` |
| Key | Description | Command |
| ----- | ----------- | ------- |
| `/` | Search for regex pattern | `search` |
| `?` | Search for previous pattern | `rsearch` |
| `n` | Select next search match | `search_next` |
| `N` | Select previous search match | `search_prev` |
| `*` | Use current selection as the search pattern, if only a single char is selected the current word (miW) is used instead | `search_selection` |
### Minor modes

View File

@ -2266,19 +2266,37 @@ fn extend_search_prev(cx: &mut Context) {
fn search_selection(cx: &mut Context) {
let register = cx.register.unwrap_or('/');
let count = cx.count();
let (view, doc) = current!(cx.editor);
let contents = doc.text().slice(..);
let regex = doc
.selection(view.id)
.iter()
.map(|selection| regex::escape(&selection.fragment(contents)))
.collect::<HashSet<_>>() // Collect into hashset to deduplicate identical regexes
.into_iter()
.collect::<Vec<_>>()
.join("|");
// Checks whether there is only one selection with a width of 1
let selections = doc.selection(view.id);
let primary = selections.primary();
let regex = if selections.len() == 1 && primary.len() == 1 {
let text = doc.text();
let text_slice = text.slice(..);
// In this case select the WORD under the cursor
let current_word = textobject::textobject_word(
text_slice,
primary,
textobject::TextObject::Inside,
count,
false,
);
let text_to_search = current_word.fragment(text_slice).to_string();
regex::escape(&text_to_search)
} else {
selections
.iter()
.map(|selection| regex::escape(&selection.fragment(contents)))
.collect::<HashSet<_>>() // Collect into hashset to deduplicate identical regexes
.into_iter()
.collect::<Vec<_>>()
.join("|")
};
let msg = format!("register '{}' set to '{}'", register, &regex);
let msg = format!("register '{}' set to '{}'", '/', &regex);
match cx.editor.registers.push(register, regex) {
Ok(_) => {
cx.editor.registers.last_search_register = register;

View File

@ -178,6 +178,56 @@ fn match_paths(app: &Application, matches: Vec<&str>) -> usize {
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn test_search_selection() -> anyhow::Result<()> {
// Single selection with a length of 1: search for the whole word
test_key_sequence(
&mut helpers::AppBuilder::new().build()?,
Some("ifoobar::baz<esc>3bl*"), // 3b places the cursor on the first letter of 'foobar', then move one to the right for good measure
Some(&|app| {
assert!(
r#"register '/' set to 'foobar'"# == app.editor.get_status().unwrap().0
&& Some(&"foobar".to_string()) == app.editor.registers.first('/')
);
}),
false,
)
.await?;
// Single selection with a length greather than 1: only search for the selection
test_key_sequence(
&mut helpers::AppBuilder::new().build()?,
Some("ifoobar::baz<esc>3blvll*"), // 3b places the cursor on the first letter of 'foobar', then move one to the right for good measure, then select two more chars for a total of three
Some(&|app| {
assert!(
r#"register '/' set to 'oob'"# == app.editor.get_status().unwrap().0
&& Some(&"oob".to_string()) == app.editor.registers.first('/')
);
}),
false,
)
.await?;
// Multiple selection of length 1 each : should still only search for the selection
test_key_sequence(
&mut helpers::AppBuilder::new().build()?,
Some("ifoobar::baz<ret>bar::crux<esc>k3blC*"), // k3b places the cursor on the first letter of 'foobar', then move one to the right for good measure, then adds a cursor on the line below
Some(&|app| {
assert!(
// The selections don't seem to be ordered, so we have to test for the two possible orders.
(r#"register '/' set to 'o|a'"# == app.editor.get_status().unwrap().0
|| r#"register '/' set to 'a|o'"# == app.editor.get_status().unwrap().0)
&& (Some(&"o|a".to_string()) == app.editor.registers.first('/')
|| Some(&"a|o".to_string()) == app.editor.registers.first('/'))
);
}),
false,
)
.await?;
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn test_multi_selection_paste() -> anyhow::Result<()> {
test((