on master help sign in
search: Barebones implementation.
83d48f1Blaž Hrastnik
helix-term/src/commands.rs+63
@@ -351,6 +351,69 @@ pub fn split_selection_on_newline(cx: &mut Context) {
351 doc.set_selection(selection);
351 doc.set_selection(selection);
352}
352}
353
353
354+// search: searches for the first occurence in file, provides a prompt
355+// search_next: reuses the last search regex and searches for the next match. The next match becomes the main selection.
356+// -> we always search from after the cursor.head
357+// TODO: be able to use selection as search query (*/alt *)
358+// I'd probably collect all the matches right now and store the current index. The cache needs
359+// wiping if input happens.
360+
361+fn _search(doc: &mut Document, contents: &str, regex: &Regex) {
362+ let text = doc.text();
363+ let start = doc.selection().cursor();
364+
365+ // TODO: use find_at to find the next match after the cursor, loop around the end
366+ if let Some(mat) = regex.find_at(&contents, start) {
367+ let start = text.byte_to_char(mat.start());
368+ let end = text.byte_to_char(mat.end());
369+ let selection = Selection::single(start, end - 1);
370+ // TODO: (first_match, regex) stuff in register?
371+ doc.set_selection(selection);
372+ };
373+}
374+
375+// TODO: use one function for search vs extend
376+pub fn search(cx: &mut Context) {
377+ let doc = cx.doc();
378+
379+ // TODO: could probably share with select_on_matches?
380+
381+ // HAXX: sadly we can't avoid allocating a single string for the whole buffer since we can't
382+ // feed chunks into the regex yet
383+ let contents = doc.text().slice(..).to_string();
384+
385+ let prompt = ui::regex_prompt(cx, "search:".to_string(), move |doc, regex| {
386+ let text = doc.text();
387+ let start = doc.selection().cursor();
388+ _search(doc, &contents, &regex);
389+
390+ // TODO: only store on enter (accept), not update
391+ register::set('\\', vec![regex.as_str().to_string()]);
392+ });
393+
394+ cx.callback = Some(Box::new(
395+ move |compositor: &mut Compositor, editor: &mut Editor| {
396+ compositor.push(Box::new(prompt));
397+ },
398+ ));
399+}
400+
401+pub fn search_next(cx: &mut Context) {
402+ if let Some(query) = register::get('\\') {
403+ let query = query.first().unwrap();
404+ let doc = cx.doc();
405+ let contents = doc.text().slice(..).to_string();
406+ let regex = Regex::new(&query).unwrap();
407+ _search(doc, &contents, &regex);
408+ }
409+}
410+
411+// TODO: N -> search_prev
412+// need to loop around buffer also and show a message
413+// same for no matches
414+
415+//
416+
354pub fn select_line(cx: &mut Context) {
417pub fn select_line(cx: &mut Context) {
355 // TODO: count
418 // TODO: count
356 let pos = cx.doc().selection().primary();
419 let pos = cx.doc().selection().primary();
helix-term/src/keymap.rs+2
@@ -168,6 +168,8 @@ pub fn default() -> Keymaps {
168 vec![alt!(';')] => commands::flip_selections,
168 vec![alt!(';')] => commands::flip_selections,
169 vec![key!('%')] => commands::select_all,
169 vec![key!('%')] => commands::select_all,
170 vec![key!('x')] => commands::select_line,
170 vec![key!('x')] => commands::select_line,
171+ vec![key!('/')] => commands::search,
172+ vec![key!('n')] => commands::search_next,
171 vec![key!('u')] => commands::undo,
173 vec![key!('u')] => commands::undo,
172 vec![shift!('U')] => commands::redo,
174 vec![shift!('U')] => commands::redo,
173 vec![key!('y')] => commands::yank,
175 vec![key!('y')] => commands::yank,
helix-term/src/ui/mod.rs+4−1
@@ -43,12 +43,15 @@ pub fn regex_prompt(
43 PromptEvent::Update => {
43 PromptEvent::Update => {
44 match Regex::new(input) {
44 match Regex::new(input) {
45 Ok(regex) => {
45 Ok(regex) => {
46− let doc = &mut editor.view_mut().doc;
46+ let view = &mut editor.view_mut();
47+ let doc = &mut view.doc;
47
48
48 // revert state to what it was before the last update
49 // revert state to what it was before the last update
49 doc.state = snapshot.clone();
50 doc.state = snapshot.clone();
50
51
51 fun(doc, regex);
52 fun(doc, regex);
53+
54+ view.ensure_cursor_in_view();
52 }
55 }
53 Err(_err) => (), // TODO: mark command line as error
56 Err(_err) => (), // TODO: mark command line as error
54 }
57 }