diff --git a/helix-term/src/ui/tree.rs b/helix-term/src/ui/tree.rs index c9a97b84..8144c734 100644 --- a/helix-term/src/ui/tree.rs +++ b/helix-term/src/ui/tree.rs @@ -7,7 +7,7 @@ use crate::{ compositor::{Context, EventResult}, ctrl, key, shift, }; -use helix_core::unicode::width::UnicodeWidthStr; +use helix_core::{movement::Direction, unicode::width::UnicodeWidthStr}; use helix_view::{ graphics::Rect, input::{Event, KeyEvent}, @@ -142,7 +142,7 @@ impl<'a, T> Iterator for TreeIter<'a, T> { None } else { self.current_index_forward = self.current_index_forward.saturating_add(1); - self.tree.find_by_index(index) + self.tree.get(index) } } @@ -158,7 +158,7 @@ impl<'a, T> DoubleEndedIterator for TreeIter<'a, T> { None } else { self.current_index_reverse = self.current_index_reverse.saturating_sub(1); - self.tree.find_by_index(index as usize) + self.tree.get(index as usize) } } } @@ -166,13 +166,6 @@ impl<'a, T> DoubleEndedIterator for TreeIter<'a, T> { impl<'a, T> ExactSizeIterator for TreeIter<'a, T> {} impl Tree { - fn iter(&self) -> TreeIter { - TreeIter { - tree: self, - current_index_forward: 0, - current_index_reverse: (self.len() - 1) as isize, - } - } pub fn new(item: T, children: Vec>) -> Self { Self { item, @@ -182,28 +175,50 @@ impl Tree { is_opened: false, } } + fn iter(&self) -> TreeIter { + TreeIter { + tree: self, + current_index_forward: 0, + current_index_reverse: (self.len() - 1) as isize, + } + } + + /// Find an element in the tree with given `predicate`. + /// `start_index` is inclusive if direction is `Forward`. + /// `start_index` is exclusive if direction is `Backward`. + pub fn find(&self, start_index: usize, direction: Direction, predicate: F) -> Option + where + F: FnMut(&Tree) -> bool, + { + let iter = self.iter(); + match direction { + Direction::Forward => iter + .skip(start_index) + .position(predicate) + .map(|index| index + start_index), + Direction::Backward => iter.take(start_index).rposition(predicate), + } + } pub fn item(&self) -> &T { &self.item } - fn find_by_index(&self, index: usize) -> Option<&Tree> { + fn get(&self, index: usize) -> Option<&Tree> { if self.index == index { Some(self) } else { - self.children - .iter() - .find_map(|elem| elem.find_by_index(index)) + self.children.iter().find_map(|elem| elem.get(index)) } } - fn find_by_index_mut(&mut self, index: usize) -> Option<&mut Tree> { + fn get_mut(&mut self, index: usize) -> Option<&mut Tree> { if self.index == index { Some(self) } else { self.children .iter_mut() - .find_map(|elem| elem.find_by_index_mut(index)) + .find_map(|elem| elem.get_mut(index)) } } @@ -298,18 +313,6 @@ impl TreeView { self } - fn find(&self, start: usize, reverse: bool, f: F) -> Option - where - F: FnMut(&Tree) -> bool, - { - let iter = self.tree.iter(); - if reverse { - iter.take(start).rposition(f) - } else { - iter.skip(start).position(f).map(|index| index + start) - } - } - /// Reveal item in the tree based on the given `segments`. /// /// The name of the root should be excluded. @@ -424,7 +427,7 @@ impl TreeView { // } // } // - let mut selected_item = self.find_by_index_mut(selected_index); + let mut selected_item = self.get_mut(selected_index); if selected_item.is_opened { selected_item.is_opened = false; selected_item.children = vec![]; @@ -434,7 +437,7 @@ impl TreeView { if let Some(mut on_open_fn) = self.on_opened_fn.take() { let mut f = || { - let current = &mut self.find_by_index_mut(selected_index); + let current = &mut self.get_mut(selected_index); match on_open_fn(&mut current.item, cx, params) { TreeOp::Restore => { panic!(); @@ -474,7 +477,7 @@ impl TreeView { self.on_opened_fn = Some(on_open_fn) } else { panic!(); - self.find_by_index_mut(selected_index).children = vec![]; + self.get_mut(selected_index).children = vec![]; // let current = &mut self.items[selected_index]; // let inserts = std::mem::take(&mut current.folded); // let _: Vec<_> = self @@ -496,7 +499,8 @@ impl TreeView { pub fn search_next(&mut self, cx: &mut Context, s: &str, params: &mut T::Params) { let skip = std::cmp::max(2, self.save_view.0 + 1); self.selected = self - .find(skip, false, |e| e.item.filter(s)) + .tree + .find(skip, Direction::Forward, |e| e.item.filter(s)) .unwrap_or(self.save_view.0); self.winline = (self.save_view.1 + self.selected).saturating_sub(self.save_view.0); @@ -505,7 +509,8 @@ impl TreeView { pub fn search_previous(&mut self, cx: &mut Context, s: &str, params: &mut T::Params) { let take = self.save_view.0; self.selected = self - .find(take, true, |e| e.item.filter(s)) + .tree + .find(take, Direction::Backward, |e| e.item.filter(s)) .unwrap_or(self.save_view.0); self.winline = (self.save_view.1 + self.selected).saturating_sub(self.save_view.0); @@ -570,28 +575,25 @@ impl TreeView { (self.selected, self.winline) = self.save_view; } - fn find_by_index(&self, index: usize) -> &Tree { - self.tree - .iter() - .find_map(|item| item.find_by_index(index)) - .unwrap() + fn get(&self, index: usize) -> &Tree { + self.tree.get(index).unwrap() } - fn find_by_index_mut(&mut self, index: usize) -> &mut Tree { - self.tree.find_by_index_mut(index).unwrap() + fn get_mut(&mut self, index: usize) -> &mut Tree { + self.tree.get_mut(index).unwrap() } pub fn current(&self) -> &Tree { - self.find_by_index(self.selected) + self.get(self.selected) } pub fn current_mut(&mut self) -> &mut Tree { - self.find_by_index_mut(self.selected) + self.get_mut(self.selected) } fn current_parent(&self) -> Option<&Tree> { if let Some(parent_index) = self.current().parent_index { - Some(self.find_by_index(parent_index)) + Some(self.get(parent_index)) } else { None } @@ -599,7 +601,7 @@ impl TreeView { fn current_parent_mut(&mut self) -> Option<&mut Tree> { if let Some(parent_index) = self.current().parent_index { - Some(self.find_by_index_mut(parent_index)) + Some(self.get_mut(parent_index)) } else { None } @@ -1011,10 +1013,12 @@ fn index_elems(start_index: usize, elems: Vec>) -> Vec> { #[cfg(test)] mod test_tree { + use helix_core::movement::Direction; + use super::{index_elems, Tree}; #[test] - fn test_indexs_elems_1() { + fn test_indexs_elems() { let result = index_elems( 0, vec![ @@ -1065,7 +1069,7 @@ mod test_tree { } #[test] - fn test_iter_1() { + fn test_iter() { let tree = Tree::new( "spam", vec![ @@ -1083,7 +1087,25 @@ mod test_tree { } #[test] - fn test_len_1() { + fn test_iter_double_ended() { + let tree = Tree::new( + "spam", + vec![ + Tree::new("jar", vec![Tree::new("yo", vec![])]), + Tree::new("foo", vec![Tree::new("bar", vec![])]), + ], + ); + + let mut iter = tree.iter(); + assert_eq!(iter.next_back().map(|tree| tree.item), Some("bar")); + assert_eq!(iter.next_back().map(|tree| tree.item), Some("foo")); + assert_eq!(iter.next_back().map(|tree| tree.item), Some("yo")); + assert_eq!(iter.next_back().map(|tree| tree.item), Some("jar")); + assert_eq!(iter.next_back().map(|tree| tree.item), Some("spam")); + } + + #[test] + fn test_len() { let tree = Tree::new( "spam", vec![ @@ -1094,4 +1116,84 @@ mod test_tree { assert_eq!(tree.len(), 5) } + + #[test] + fn test_find_forward() { + let tree = Tree::new( + ".cargo", + vec![ + Tree::new("jar", vec![Tree::new("Cargo.toml", vec![])]), + Tree::new("Cargo.toml", vec![Tree::new("bar", vec![])]), + ], + ); + let result = tree.find(0, Direction::Forward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(0)); + + let result = tree.find(1, Direction::Forward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(2)); + + let result = tree.find(2, Direction::Forward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(2)); + + let result = tree.find(3, Direction::Forward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(3)); + + let result = tree.find(4, Direction::Forward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, None); + } + + #[test] + fn test_find_backward() { + let tree = Tree::new( + ".cargo", + vec![ + Tree::new("jar", vec![Tree::new("Cargo.toml", vec![])]), + Tree::new("Cargo.toml", vec![Tree::new("bar", vec![])]), + ], + ); + let result = tree.find(0, Direction::Backward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, None); + + let result = tree.find(1, Direction::Backward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(0)); + + let result = tree.find(2, Direction::Backward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(0)); + + let result = tree.find(3, Direction::Backward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(2)); + + let result = tree.find(4, Direction::Backward, |tree| { + tree.item.to_lowercase().contains(&"cargo".to_lowercase()) + }); + + assert_eq!(result, Some(3)); + } }