diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 30b3d37f1..b3ce3c47b 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 235cb52dc..98ffa3d17 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 = 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 = 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 = 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 = 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 8cbf706e8..57c08ddf9 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 { 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 { 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