From 7f053408ed11f0432cf1cd6e29c9a5cb7334f190 Mon Sep 17 00:00:00 2001 From: Aiko Mastboom Date: Thu, 18 Aug 2022 01:34:47 +0200 Subject: [PATCH] added Incrementer for config driven ordered lists. --- book/src/configuration.md | 1 + helix-core/src/increment/mod.rs | 1 + helix-core/src/increment/ordered_list.rs | 144 +++++++++++++++++++++++ helix-term/src/commands.rs | 11 +- helix-view/src/editor.rs | 6 + 5 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 helix-core/src/increment/ordered_list.rs diff --git a/book/src/configuration.md b/book/src/configuration.md index 74474a74d7e8b..41d3e0a485b7e 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -50,6 +50,7 @@ You may also specify a file to use for configuration with the `-c` or | `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` | | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` | | `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` | +| `ordered-lists` | Use increment/decrement command to traverse these lists (of arbitrary length) | `[["yes", "no"],["on","off"]]`| ### `[editor.statusline]` Section diff --git a/helix-core/src/increment/mod.rs b/helix-core/src/increment/mod.rs index f59457748e17a..9c8d416519d55 100644 --- a/helix-core/src/increment/mod.rs +++ b/helix-core/src/increment/mod.rs @@ -1,5 +1,6 @@ pub mod date_time; pub mod number; +pub mod ordered_list; use crate::{Range, Tendril}; diff --git a/helix-core/src/increment/ordered_list.rs b/helix-core/src/increment/ordered_list.rs new file mode 100644 index 0000000000000..ee8c84ce12942 --- /dev/null +++ b/helix-core/src/increment/ordered_list.rs @@ -0,0 +1,144 @@ +use super::Increment; +use ropey::RopeSlice; +use std::string::String; + +use crate::{ + textobject::{textobject_word, TextObject}, + Range, Tendril, +}; + +#[derive(Debug, PartialEq, Eq)] +pub struct OrderedListWalker<'a> { + walkway: &'a Vec, + index: usize, + is_capitalized: bool, + range: Range, +} + +impl<'a> OrderedListWalker<'a> { + pub fn from_range( + text: RopeSlice, + range: Range, + config: &'a Vec>, + ) -> Option> { + let range = textobject_word(text, range, TextObject::Inside, 1, false); + let word: String = text.slice(range.from()..range.to()).chars().collect(); + if word.len() <= 0 { + // no word found + return None; + } + let lower_case_word: String = word.to_lowercase(); + for (_i, walkway) in config.into_iter().enumerate() { + for (index, w) in walkway.into_iter().enumerate() { + if w.len() > 0 && lower_case_word.eq(w.to_lowercase().as_str()) { + let is_capitalized: bool = word.chars().next().unwrap().is_uppercase(); + return Some(OrderedListWalker { + walkway, + index, + is_capitalized, + range, + }); + } + } + } + None + } +} + +impl<'a> Increment for OrderedListWalker<'a> { + fn increment(&self, amount: i64) -> (Range, Tendril) { + let pos: usize = + (self.index as i64 + amount).rem_euclid(self.walkway.len() as i64) as usize; + let mut s: String = self.walkway.get(pos).unwrap().into(); + if self.is_capitalized { + // https://stackoverflow.com/questions/38406793/why-is-capitalizing-the-first-letter-of-a-string-so-convoluted-in-rust + s = s.chars().next().unwrap().to_uppercase().to_string() + + s.chars().skip(1).collect::().as_str(); + } + (self.range, s.into()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Rope; + + #[test] + fn test_ordered_from_range() { + let rope = Rope::from_str("Test text true more text."); + let range = Range::point(12); + let walkway_one = vec!["true".to_owned(), "false".to_owned()]; + let walkway_two = vec!["ja".to_owned(), "nee".to_owned()]; + let config = vec![walkway_two, walkway_one.to_owned()]; + assert_eq!( + OrderedListWalker::from_range(rope.slice(..), range, &config), + Some(OrderedListWalker { + range: Range::new(10, 14), + walkway: &walkway_one, + index: 0, + is_capitalized: false, + }) + ); + let range = Range::point(10); + assert_eq!( + OrderedListWalker::from_range(rope.slice(..), range, &config), + Some(OrderedListWalker { + range: Range::new(10, 14), + walkway: &walkway_one, + index: 0, + is_capitalized: false, + }) + ); + let range = Range::point(13); + assert_eq!( + OrderedListWalker::from_range(rope.slice(..), range, &config), + Some(OrderedListWalker { + range: Range::new(10, 14), + walkway: &walkway_one, + index: 0, + is_capitalized: false, + }) + ); + let range = Range::point(14); + assert_eq!( + OrderedListWalker::from_range(rope.slice(..), range, &config), + None, + ); + let range = Range::point(9); + assert_eq!( + OrderedListWalker::from_range(rope.slice(..), range, &config), + None, + ); + } + + #[test] + #[ignore] + fn test_ordered_increment() { + let walkway_one = vec!["true".to_owned(), "false".to_owned()]; + let walkway_two = vec!["ja".to_owned(), "nee".to_owned()]; + let config = vec![walkway_two, walkway_one]; + + let tests = [ + ("false", "false", 2), + ("false", "true", 1), + ("false", "true", -1), + ("false", "false", -2), + ("False", "True", 1), + ("True", "False", -1), + ("Ja", "Nee", 3), + ]; + + for (original, expected, amount) in tests { + let rope = Rope::from_str(original); + let range = Range::point(0); + assert_eq!( + OrderedListWalker::from_range(rope.slice(..), range, &config) + .unwrap() + .increment(amount) + .1, + Tendril::from(expected) + ); + } + } +} diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index ae50ed7c5ebd1..3ab496c6d8f81 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -10,8 +10,10 @@ pub use typed::*; use helix_core::{ comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes, history::UndoKind, - increment::date_time::DateTimeIncrementor, - increment::{number::NumberIncrementor, Increment}, + increment::{ + date_time::DateTimeIncrementor, number::NumberIncrementor, ordered_list::OrderedListWalker, + Increment, + }, indent, indent::IndentStyle, line_ending::{get_line_ending_of_str, line_end_char_index, str_is_line_ending}, @@ -4739,6 +4741,7 @@ fn increment_impl(cx: &mut Context, amount: i64) { 1, ); + let ordered_lists = &cx.editor.config().ordered_lists; let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id); let text = doc.text().slice(..); @@ -4752,6 +4755,10 @@ fn increment_impl(cx: &mut Context, amount: i64) { Box::new(incrementor) } else if let Some(incrementor) = NumberIncrementor::from_range(text, *range) { Box::new(incrementor) + } else if let Some(incrementor) = + OrderedListWalker::from_range(text, *range, ordered_lists) + { + Box::new(incrementor) } else { return None; }; diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 1e7f508c14bf9..8ead7b2cc51d8 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -165,6 +165,8 @@ pub struct Config { pub indent_guides: IndentGuidesConfig, /// Whether to color modes with different colors. Defaults to `false`. pub color_modes: bool, + /// ordered lists + pub ordered_lists: Vec>, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -499,6 +501,10 @@ impl Default for Config { whitespace: WhitespaceConfig::default(), indent_guides: IndentGuidesConfig::default(), color_modes: false, + ordered_lists: vec![ + vec!["yes".to_owned(), "no".to_owned()], + vec!["on".to_owned(), "off".to_owned()], + ], } } }