From cc058ad78fa6462b3bbe79035b10653f4c7691b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= <blaz@mxxn.io>
Date: Thu, 1 Apr 2021 11:01:11 +0900
Subject: [PATCH] Simplify some code.

---
 helix-core/src/lib.rs      |  1 +
 helix-term/src/commands.rs | 78 +++++++++++++++-----------------------
 helix-term/src/ui/mod.rs   | 13 +++++--
 3 files changed, 40 insertions(+), 52 deletions(-)

diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs
index 30b3d37f..b3ce3c47 100644
--- a/helix-core/src/lib.rs
+++ b/helix-core/src/lib.rs
@@ -53,6 +53,7 @@ pub use {regex, tree_sitter};
 
 pub use position::{coords_at_pos, pos_at_coords, Position};
 pub use selection::{Range, Selection};
+pub use smallvec::SmallVec;
 pub use syntax::Syntax;
 
 pub use diagnostic::Diagnostic;
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 235cb52d..98ffa3d1 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1,10 +1,10 @@
 use helix_core::{
-    comment, coords_at_pos, graphemes, match_brackets,
+    comment, coords_at_pos, graphemes, indent, match_brackets,
     movement::{self, Direction},
     object, pos_at_coords,
     regex::{self, Regex},
     register, search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection,
-    Tendril, Transaction,
+    SmallVec, Tendril, Transaction,
 };
 
 use once_cell::sync::Lazy;
@@ -951,26 +951,29 @@ pub fn open_below(cx: &mut Context) {
     let (view, doc) = cx.current();
     enter_insert_mode(doc);
 
+    let text = doc.text().slice(..);
     let lines = selection_lines(doc.text(), doc.selection(view.id));
 
-    let positions = lines.into_iter().map(|index| {
-        // adjust all positions to the end of the line (next line minus one)
-        doc.text().line_to_char(index + 1).saturating_sub(1)
-    });
+    let mut ranges = SmallVec::with_capacity(lines.len());
 
-    let text = doc.text().slice(..);
+    let changes: Vec<Change> = lines
+        .into_iter()
+        .map(|line| {
+            // adjust all positions to the end of the line (next line minus one)
+            let index = doc.text().line_to_char(line + 1).saturating_sub(1);
 
-    let changes: Vec<Change> = positions
-        .map(|index| {
             // TODO: share logic with insert_newline for indentation
-            let indent_level =
-                helix_core::indent::suggested_indent_for_pos(doc.syntax(), text, index, true);
+            let indent_level = indent::suggested_indent_for_pos(doc.syntax(), text, index, true);
             let indent = doc.indent_unit().repeat(indent_level);
             let mut text = String::with_capacity(1 + indent.len());
             text.push('\n');
             text.push_str(&indent);
             let text = text.repeat(count);
 
+            // calculate new selection range
+            let pos = index + text.len();
+            ranges.push(Range::new(pos, pos));
+
             (index, index, Some(text.into()))
         })
         .collect();
@@ -978,17 +981,7 @@ pub fn open_below(cx: &mut Context) {
     // TODO: count actually inserts "n" new lines and starts editing on all of them.
     // TODO: append "count" newlines and modify cursors to those lines
 
-    let selection = Selection::new(
-        changes
-            .iter()
-            .map(|(start, _end, text): &Change| {
-                let len = text.as_ref().map(|text| text.len()).unwrap(); // minus newline
-                let pos = start + len;
-                Range::new(pos, pos)
-            })
-            .collect(),
-        0,
-    );
+    let selection = Selection::new(ranges, 0);
 
     let transaction =
         Transaction::change(doc.text(), changes.into_iter()).with_selection(selection);
@@ -1002,26 +995,29 @@ pub fn open_above(cx: &mut Context) {
     let (view, doc) = cx.current();
     enter_insert_mode(doc);
 
+    let text = doc.text().slice(..);
     let lines = selection_lines(doc.text(), doc.selection(view.id));
 
-    let positions = lines.into_iter().map(|index| {
-        // adjust all positions to the end of the previous line
-        doc.text().line_to_char(index).saturating_sub(1)
-    });
+    let mut ranges = SmallVec::with_capacity(lines.len());
 
-    let text = doc.text().slice(..);
+    let changes: Vec<Change> = lines
+        .into_iter()
+        .map(|line| {
+            // adjust all positions to the end of the previous line
+            let index = doc.text().line_to_char(line).saturating_sub(1);
 
-    let changes: Vec<Change> = positions
-        .map(|index| {
             // TODO: share logic with insert_newline for indentation
-            let indent_level =
-                helix_core::indent::suggested_indent_for_pos(doc.syntax(), text, index, true);
+            let indent_level = indent::suggested_indent_for_pos(doc.syntax(), text, index, true);
             let indent = doc.indent_unit().repeat(indent_level);
             let mut text = String::with_capacity(1 + indent.len());
             text.push('\n');
             text.push_str(&indent);
             let text = text.repeat(count);
 
+            // calculate new selection range
+            let pos = index + text.len();
+            ranges.push(Range::new(pos, pos));
+
             // generate changes
             (index, index, Some(text.into()))
         })
@@ -1030,17 +1026,7 @@ pub fn open_above(cx: &mut Context) {
     // TODO: count actually inserts "n" new lines and starts editing on all of them.
     // TODO: append "count" newlines and modify cursors to those lines
 
-    let selection = Selection::new(
-        changes
-            .iter()
-            .map(|(start, _end, text): &Change| {
-                let len = text.as_ref().map(|text| text.len()).unwrap(); // minus newline
-                let pos = start + len;
-                Range::new(pos, pos)
-            })
-            .collect(),
-        0,
-    );
+    let selection = Selection::new(ranges, 0);
 
     let transaction =
         Transaction::change(doc.text(), changes.into_iter()).with_selection(selection);
@@ -1359,12 +1345,8 @@ pub mod insert {
         let transaction =
             Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
                 // TODO: offset range.head by 1? when calculating?
-                let indent_level = helix_core::indent::suggested_indent_for_pos(
-                    doc.syntax(),
-                    text,
-                    range.head,
-                    true,
-                );
+                let indent_level =
+                    indent::suggested_indent_for_pos(doc.syntax(), text, range.head, true);
                 let indent = doc.indent_unit().repeat(indent_level);
                 let mut text = String::with_capacity(1 + indent.len());
                 text.push('\n');
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 8cbf706e..57c08ddf 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -82,7 +82,7 @@ pub fn file_picker(root: PathBuf) -> Picker<PathBuf> {
     let files = Walk::new(root.clone()).filter_map(|entry| match entry {
         Ok(entry) => {
             // filter dirs, but we might need special handling for symlinks!
-            if !entry.file_type().unwrap().is_dir() {
+            if !entry.file_type().map_or(false, |entry| entry.is_dir()) {
                 Some(entry.into_path())
             } else {
                 None
@@ -97,7 +97,11 @@ pub fn file_picker(root: PathBuf) -> Picker<PathBuf> {
         files.take(MAX).collect(),
         move |path: &PathBuf| {
             // format_fn
-            path.strip_prefix(&root).unwrap().to_str().unwrap().into()
+            path.strip_prefix(&root)
+                .unwrap_or(path)
+                .to_str()
+                .unwrap()
+                .into()
         },
         move |editor: &mut Editor, path: &PathBuf, action| {
             let document_id = editor
@@ -144,12 +148,13 @@ pub mod completers {
                 file.ok().map(|entry| {
                     let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
 
-                    let mut path = entry.path().strip_prefix(&dir).unwrap().to_path_buf();
+                    let path = entry.path();
+                    let mut path = path.strip_prefix(&dir).unwrap_or(path).to_path_buf();
 
                     if is_dir {
                         path.push("");
                     }
-                    let path = path.to_str().unwrap().to_string();
+                    let path = path.to_str().unwrap().to_owned();
                     (end.clone(), Cow::from(path))
                 })
             }) // TODO: unwrap or skip