From 811f952a41177242f7dfa4d66f2b16157f918718 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Wed, 14 Apr 2021 15:21:49 +0900
Subject: [PATCH] Center search results.

---
 helix-term/src/commands.rs | 47 +++++++++++++++++++-------------------
 helix-term/src/ui/mod.rs   |  6 ++---
 2 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 190ae3b9..3854f416 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -574,12 +574,11 @@ pub fn select_all(cx: &mut Context) {
 }
 
 pub fn select_regex(cx: &mut Context) {
-    let view_id = cx.view_id;
-    let prompt = ui::regex_prompt(cx, "select:".to_string(), move |doc, regex| {
+    let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, regex| {
         let text = doc.text().slice(..);
-        if let Some(selection) = selection::select_on_matches(text, doc.selection(view_id), &regex)
+        if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
         {
-            doc.set_selection(view_id, selection);
+            doc.set_selection(view.id, selection);
         }
     });
 
@@ -587,11 +586,10 @@ pub fn select_regex(cx: &mut Context) {
 }
 
 pub fn split_selection(cx: &mut Context) {
-    let view_id = cx.view_id;
-    let prompt = ui::regex_prompt(cx, "split:".to_string(), move |doc, regex| {
+    let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, regex| {
         let text = doc.text().slice(..);
-        let selection = selection::split_on_matches(text, doc.selection(view_id), &regex);
-        doc.set_selection(view_id, selection);
+        let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
+        doc.set_selection(view.id, selection);
     });
 
     cx.push_layer(Box::new(prompt));
@@ -614,9 +612,9 @@ pub fn split_selection_on_newline(cx: &mut Context) {
 // I'd probably collect all the matches right now and store the current index. The cache needs
 // wiping if input happens.
 
-fn _search(doc: &mut Document, view_id: ViewId, contents: &str, regex: &Regex, extend: bool) {
+fn _search(doc: &mut Document, view: &mut View, contents: &str, regex: &Regex, extend: bool) {
     let text = doc.text();
-    let selection = doc.selection(view_id);
+    let selection = doc.selection(view.id);
     let start = selection.cursor();
 
     // use find_at to find the next match after the cursor, loop around the end
@@ -627,14 +625,19 @@ fn _search(doc: &mut Document, view_id: ViewId, contents: &str, regex: &Regex, e
         let start = text.byte_to_char(mat.start());
         let end = text.byte_to_char(mat.end());
 
+        let head = end - 1;
+
         let selection = if extend {
-            selection.clone().push(Range::new(start, end - 1))
+            selection.clone().push(Range::new(start, head))
         } else {
-            Selection::single(start, end - 1)
+            Selection::single(start, head)
         };
 
         // TODO: (first_match, regex) stuff in register?
-        doc.set_selection(view_id, selection);
+        doc.set_selection(view.id, selection);
+        // TODO: extract this centering into a function to share with _goto?
+        let line = doc.text().char_to_line(head);
+        view.first_line = line.saturating_sub(view.area.height as usize / 2);
     };
 }
 
@@ -649,10 +652,10 @@ pub fn search(cx: &mut Context) {
     let contents = doc.text().slice(..).to_string();
 
     let view_id = cx.view_id;
-    let prompt = ui::regex_prompt(cx, "search:".to_string(), move |doc, regex| {
+    let prompt = ui::regex_prompt(cx, "search:".to_string(), move |view, doc, regex| {
         let text = doc.text();
-        let start = doc.selection(view_id).cursor();
-        _search(doc, view_id, &contents, &regex, false);
+        let start = doc.selection(view.id).cursor();
+        _search(doc, view, &contents, &regex, false);
 
         // TODO: only store on enter (accept), not update
         register::set('\\', vec![regex.as_str().to_string()]);
@@ -664,11 +667,10 @@ pub fn search(cx: &mut Context) {
 pub fn _search_next(cx: &mut Context, extend: bool) {
     if let Some(query) = register::get('\\') {
         let query = query.first().unwrap();
-        let view_id = cx.view_id;
-        let doc = cx.doc();
+        let (view, doc) = cx.current();
         let contents = doc.text().slice(..).to_string();
         let regex = Regex::new(&query).unwrap();
-        _search(doc, view_id, &contents, &regex, extend);
+        _search(doc, view, &contents, &regex, extend);
     }
 }
 
@@ -1652,12 +1654,11 @@ pub fn join_selections(cx: &mut Context) {
 
 pub fn keep_selections(cx: &mut Context) {
     // keep selections matching regex
-    let view_id = cx.view_id;
-    let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |doc, regex| {
+    let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, regex| {
         let text = doc.text().slice(..);
 
-        if let Some(selection) = selection::keep_matches(text, doc.selection(view_id), &regex) {
-            doc.set_selection(view_id, selection);
+        if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
+            doc.set_selection(view.id, selection);
         }
     });
 
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 57c08ddf..d9a05a9c 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -20,7 +20,7 @@ pub use tui::layout::Rect;
 pub use tui::style::{Color, Modifier, Style};
 
 use helix_core::regex::Regex;
-use helix_view::{Document, Editor};
+use helix_view::{View, Document, Editor};
 
 use std::path::{Path, PathBuf};
 
@@ -33,7 +33,7 @@ pub fn text_color() -> Style {
 pub fn regex_prompt(
     cx: &mut crate::commands::Context,
     prompt: String,
-    fun: impl Fn(&mut Document, Regex) + 'static,
+    fun: impl Fn(&mut View, &mut Document, Regex) + 'static,
 ) -> Prompt {
     let view_id = cx.view().id;
     let snapshot = cx.doc().selection(view_id).clone();
@@ -65,7 +65,7 @@ pub fn regex_prompt(
                             // TODO: also revert text
                             doc.set_selection(view.id, snapshot.clone());
 
-                            fun(doc, regex);
+                            fun(view, doc, regex);
 
                             view.ensure_cursor_in_view(doc);
                         }