summaryrefslogtreecommitdiffstats
path: root/src/components/utils/statustree.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/utils/statustree.rs')
-rw-r--r--src/components/utils/statustree.rs1742
1 files changed, 871 insertions, 871 deletions
diff --git a/src/components/utils/statustree.rs b/src/components/utils/statustree.rs
index ba8d5a5e..1edc7ce6 100644
--- a/src/components/utils/statustree.rs
+++ b/src/components/utils/statustree.rs
@@ -1,5 +1,5 @@
use super::filetree::{
- FileTreeItem, FileTreeItemKind, FileTreeItems, PathCollapsed,
+ FileTreeItem, FileTreeItemKind, FileTreeItems, PathCollapsed,
};
use anyhow::Result;
use asyncgit::StatusItem;
@@ -10,895 +10,895 @@ use std::{cmp, collections::BTreeSet};
///
#[derive(Default)]
pub struct StatusTree {
- pub tree: FileTreeItems,
- pub selection: Option<usize>,
+ pub tree: FileTreeItems,
+ pub selection: Option<usize>,
- // some folders may be folded up, this allows jumping
- // over folders which are folded into their parent
- pub available_selections: Vec<usize>,
+ // some folders may be folded up, this allows jumping
+ // over folders which are folded into their parent
+ pub available_selections: Vec<usize>,
}
///
#[derive(Copy, Clone, Debug)]
pub enum MoveSelection {
- Up,
- Down,
- Left,
- Right,
- Home,
- End,
+ Up,
+ Down,
+ Left,
+ Right,
+ Home,
+ End,
}
#[derive(Copy, Clone, Debug)]
struct SelectionChange {
- new_index: usize,
- changes: bool,
+ new_index: usize,
+ changes: bool,
}
impl SelectionChange {
- const fn new(new_index: usize, changes: bool) -> Self {
- Self { new_index, changes }
- }
+ const fn new(new_index: usize, changes: bool) -> Self {
+ Self { new_index, changes }
+ }
}
impl StatusTree {
- /// update tree with a new list, try to retain selection and collapse states
- pub fn update(&mut self, list: &[StatusItem]) -> Result<()> {
- let last_collapsed = self.all_collapsed();
-
- let last_selection =
- self.selected_item().map(|e| e.info.full_path);
- let last_selection_index = self.selection.unwrap_or(0);
-
- self.tree = FileTreeItems::new(list, &last_collapsed)?;
- self.selection = last_selection.as_ref().map_or_else(
- || self.tree.items().first().map(|_| 0),
- |last_selection| {
- self.find_last_selection(
- last_selection,
- last_selection_index,
- )
- .or_else(|| self.tree.items().first().map(|_| 0))
- },
- );
-
- self.update_visibility(None, 0, true);
- self.available_selections = self.setup_available_selections();
-
- //NOTE: now that visibility is set we can make sure selection is visible
- if let Some(idx) = self.selection {
- self.selection = Some(self.find_visible_idx(idx));
- }
-
- Ok(())
- }
-
- /// Return which indices can be selected, taking into account that
- /// some folders may be folded up into their parent
- ///
- /// It should be impossible to select a folder which has been folded into its parent
- fn setup_available_selections(&self) -> Vec<usize> {
- // use the same algorithm as in filetree build_vec_text_for_drawing function
- let mut should_skip_over: usize = 0;
- let mut vec_available_selections: Vec<usize> = vec![];
- let tree_items = self.tree.items();
- for index in 0..tree_items.len() {
- if should_skip_over > 0 {
- should_skip_over -= 1;
- continue;
- }
- let mut idx_temp = index;
- vec_available_selections.push(index);
-
- while idx_temp < tree_items.len().saturating_sub(2)
- && tree_items[idx_temp].info.indent
- < tree_items[idx_temp + 1].info.indent
- {
- // fold up the folder/file
- idx_temp += 1;
- should_skip_over += 1;
-
- // don't fold files up
- if let FileTreeItemKind::File(_) =
- &tree_items[idx_temp].kind
- {
- should_skip_over -= 1;
- break;
- }
-
- // don't fold up if more than one folder in folder
- if self.tree.multiple_items_at_path(idx_temp) {
- should_skip_over -= 1;
- break;
- }
- }
- }
- vec_available_selections
- }
-
- fn find_visible_idx(&self, mut idx: usize) -> usize {
- while idx > 0 {
- if self.is_visible_index(idx) {
- break;
- }
-
- idx -= 1;
- }
-
- idx
- }
-
- ///
- pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
- self.selection.map_or(false, |selection| {
- let selection_change = match dir {
- MoveSelection::Up => {
- self.selection_updown(selection, true)
- }
- MoveSelection::Down => {
- self.selection_updown(selection, false)
- }
- MoveSelection::Left => self.selection_left(selection),
- MoveSelection::Right => {
- self.selection_right(selection)
- }
- MoveSelection::Home => SelectionChange::new(0, false),
- MoveSelection::End => self.selection_end(),
- };
-
- let changed_index =
- selection_change.new_index != selection;
-
- self.selection = Some(selection_change.new_index);
-
- changed_index || selection_change.changes
- })
- }
-
- ///
- pub fn selected_item(&self) -> Option<FileTreeItem> {
- self.selection.map(|i| self.tree[i].clone())
- }
-
- ///
- pub fn is_empty(&self) -> bool {
- self.tree.items().is_empty()
- }
-
- fn all_collapsed(&self) -> BTreeSet<&String> {
- let mut res = BTreeSet::new();
-
- for i in self.tree.items() {
- if let FileTreeItemKind::Path(PathCollapsed(collapsed)) =
- i.kind
- {
- if collapsed {
- res.insert(&i.info.full_path);
- }
- }
- }
-
- res
- }
-
- fn find_last_selection(
- &self,
- last_selection: &str,
- last_index: usize,
- ) -> Option<usize> {
- if self.is_empty() {
- return None;
- }
-
- if let Ok(i) = self.tree.items().binary_search_by(|e| {
- e.info.full_path.as_str().cmp(last_selection)
- }) {
- return Some(i);
- }
-
- Some(cmp::min(last_index, self.tree.len() - 1))
- }
-
- fn selection_updown(
- &self,
- current_index: usize,
- up: bool,
- ) -> SelectionChange {
- let mut current_index_in_available_selections;
- let mut cur_index_find = current_index;
- if self.available_selections.is_empty() {
- // Go to top
- current_index_in_available_selections = 0;
- } else {
- loop {
- if let Some(pos) = self
- .available_selections
- .iter()
- .position(|i| *i == cur_index_find)
- {
- current_index_in_available_selections = pos;
- break;
- }
-
- // Find the closest to the index, usually this shouldn't happen
- if current_index == 0 {
- // This should never happen
- current_index_in_available_selections = 0;
- break;
- }
- cur_index_find -= 1;
- }
- }
-
- let mut new_index;
-
- loop {
- // Use available_selections to go to the correct selection as
- // some of the folders may be folded up
- new_index = if up {
- current_index_in_available_selections =
- current_index_in_available_selections
- .saturating_sub(1);
- self.available_selections
- [current_index_in_available_selections]
- } else if current_index_in_available_selections
- .saturating_add(1)
- <= self.available_selections.len().saturating_sub(1)
- {
- current_index_in_available_selections =
- current_index_in_available_selections
- .saturating_add(1);
- self.available_selections
- [current_index_in_available_selections]
- } else {
- // can't move down anymore
- new_index = current_index;
- break;
- };
-
- if self.is_visible_index(new_index) {
- break;
- }
- }
- SelectionChange::new(new_index, false)
- }
-
- fn selection_end(&self) -> SelectionChange {
- let items_max = self.tree.len().saturating_sub(1);
-
- let mut new_index = items_max;
-
- loop {
- if self.is_visible_index(new_index) {
- break;
- }
-
- if new_index == 0 {
- break;
- }
-
- new_index = new_index.saturating_sub(1);
- new_index = cmp::min(new_index, items_max);
- }
-
- SelectionChange::new(new_index, false)
- }
-
- fn is_visible_index(&self, idx: usize) -> bool {
- self.tree[idx].info.visible
- }
-
- fn selection_right(
- &mut self,
- current_selection: usize,
- ) -> SelectionChange {
- let item_kind = self.tree[current_selection].kind.clone();
- let item_path =
- self.tree[current_selection].info.full_path.clone();
-
- match item_kind {
- FileTreeItemKind::Path(PathCollapsed(collapsed))
- if collapsed =>
- {
- self.expand(&item_path, current_selection);
- return SelectionChange::new(current_selection, true);
- }
- FileTreeItemKind::Path(PathCollapsed(collapsed))
- if !collapsed =>
- {
- return self
- .selection_updown(current_selection, false);
- }
- _ => (),
- }
-
- SelectionChange::new(current_selection, false)
- }
-
- fn selection_left(
- &mut self,
- current_selection: usize,
- ) -> SelectionChange {
- let item_kind = self.tree[current_selection].kind.clone();
- let item_path =
- self.tree[current_selection].info.full_path.clone();
-
- if matches!(item_kind, FileTreeItemKind::File(_))
- || matches!(item_kind,FileTreeItemKind::Path(PathCollapsed(collapsed))
+ /// update tree with a new list, try to retain selection and collapse states
+ pub fn update(&mut self, list: &[StatusItem]) -> Result<()> {
+ let last_collapsed = self.all_collapsed();
+
+ let last_selection =
+ self.selected_item().map(|e| e.info.full_path);
+ let last_selection_index = self.selection.unwrap_or(0);
+
+ self.tree = FileTreeItems::new(list, &last_collapsed)?;
+ self.selection = last_selection.as_ref().map_or_else(
+ || self.tree.items().first().map(|_| 0),
+ |last_selection| {
+ self.find_last_selection(
+ last_selection,
+ last_selection_index,
+ )
+ .or_else(|| self.tree.items().first().map(|_| 0))
+ },
+ );
+
+ self.update_visibility(None, 0, true);
+ self.available_selections = self.setup_available_selections();
+
+ //NOTE: now that visibility is set we can make sure selection is visible
+ if let Some(idx) = self.selection {
+ self.selection = Some(self.find_visible_idx(idx));
+ }
+
+ Ok(())
+ }
+
+ /// Return which indices can be selected, taking into account that
+ /// some folders may be folded up into their parent
+ ///
+ /// It should be impossible to select a folder which has been folded into its parent
+ fn setup_available_selections(&self) -> Vec<usize> {
+ // use the same algorithm as in filetree build_vec_text_for_drawing function
+ let mut should_skip_over: usize = 0;
+ let mut vec_available_selections: Vec<usize> = vec![];
+ let tree_items = self.tree.items();
+ for index in 0..tree_items.len() {
+ if should_skip_over > 0 {
+ should_skip_over -= 1;
+ continue;
+ }
+ let mut idx_temp = index;
+ vec_available_selections.push(index);
+
+ while idx_temp < tree_items.len().saturating_sub(2)
+ && tree_items[idx_temp].info.indent
+ < tree_items[idx_temp + 1].info.indent
+ {
+ // fold up the folder/file
+ idx_temp += 1;
+ should_skip_over += 1;
+
+ // don't fold files up
+ if let FileTreeItemKind::File(_) =
+ &tree_items[idx_temp].kind
+ {
+ should_skip_over -= 1;
+ break;
+ }
+
+ // don't fold up if more than one folder in folder
+ if self.tree.multiple_items_at_path(idx_temp) {
+ should_skip_over -= 1;
+ break;
+ }
+ }
+ }
+ vec_available_selections
+ }
+
+ fn find_visible_idx(&self, mut idx: usize) -> usize {
+ while idx > 0 {
+ if self.is_visible_index(idx) {
+ break;
+ }
+
+ idx -= 1;
+ }
+
+ idx
+ }
+
+ ///
+ pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
+ self.selection.map_or(false, |selection| {
+ let selection_change = match dir {
+ MoveSelection::Up => {
+ self.selection_updown(selection, true)
+ }
+ MoveSelection::Down => {
+ self.selection_updown(selection, false)
+ }
+ MoveSelection::Left => self.selection_left(selection),
+ MoveSelection::Right => {
+ self.selection_right(selection)
+ }
+ MoveSelection::Home => SelectionChange::new(0, false),
+ MoveSelection::End => self.selection_end(),
+ };
+
+ let changed_index =
+ selection_change.new_index != selection;
+
+ self.selection = Some(selection_change.new_index);
+
+ changed_index || selection_change.changes
+ })
+ }
+
+ ///
+ pub fn selected_item(&self) -> Option<FileTreeItem> {
+ self.selection.map(|i| self.tree[i].clone())
+ }
+
+ ///
+ pub fn is_empty(&self) -> bool {
+ self.tree.items().is_empty()
+ }
+
+ fn all_collapsed(&self) -> BTreeSet<&String> {
+ let mut res = BTreeSet::new();
+
+ for i in self.tree.items() {
+ if let FileTreeItemKind::Path(PathCollapsed(collapsed)) =
+ i.kind
+ {
+ if collapsed {
+ res.insert(&i.info.full_path);
+ }
+ }
+ }
+
+ res
+ }
+
+ fn find_last_selection(
+ &self,
+ last_selection: &str,
+ last_index: usize,
+ ) -> Option<usize> {
+ if self.is_empty() {
+ return None;
+ }
+
+ if let Ok(i) = self.tree.items().binary_search_by(|e| {
+ e.info.full_path.as_str().cmp(last_selection)
+ }) {
+ return Some(i);
+ }
+
+ Some(cmp::min(last_index, self.tree.len() - 1))
+ }
+
+ fn selection_updown(
+ &self,
+ current_index: usize,
+ up: bool,
+ ) -> SelectionChange {
+ let mut current_index_in_available_selections;
+ let mut cur_index_find = current_index;
+ if self.available_selections.is_empty() {
+ // Go to top
+ current_index_in_available_selections = 0;
+ } else {
+ loop {
+ if let Some(pos) = self
+ .available_selections
+ .iter()
+ .position(|i| *i == cur_index_find)
+ {
+ current_index_in_available_selections = pos;
+ break;
+ }
+
+ // Find the closest to the index, usually this shouldn't happen
+ if current_index == 0 {
+ // This should never happen
+ current_index_in_available_selections = 0;
+ break;
+ }
+ cur_index_find -= 1;
+ }
+ }
+
+ let mut new_index;
+
+ loop {
+ // Use available_selections to go to the correct selection as
+ // some of the folders may be folded up
+ new_index = if up {
+ current_index_in_available_selections =
+ current_index_in_available_selections
+ .saturating_sub(1);
+ self.available_selections
+ [current_index_in_available_selections]
+ } else if current_index_in_available_selections
+ .saturating_add(1)
+ <= self.available_selections.len().saturating_sub(1)
+ {
+ current_index_in_available_selections =
+ current_index_in_available_selections
+ .saturating_add(1);
+ self.available_selections
+ [current_index_in_available_selections]
+ } else {
+ // can't move down anymore
+ new_index = current_index;
+ break;
+ };
+
+ if self.is_visible_index(new_index) {
+ break;
+ }
+ }
+ SelectionChange::new(new_index, false)
+ }
+
+ fn selection_end(&self) -> SelectionChange {
+ let items_max = self.tree.len().saturating_sub(1);
+
+ let mut new_index = items_max;
+
+ loop {
+ if self.is_visible_index(new_index) {
+ break;
+ }
+
+ if new_index == 0 {
+ break;
+ }
+
+ new_index = new_index.saturating_sub(1);
+ new_index = cmp::min(new_index, items_max);
+ }
+
+ SelectionChange::new(new_index, false)
+ }
+
+ fn is_visible_index(&self, idx: usize) -> bool {
+ self.tree[idx].info.visible
+ }
+
+ fn selection_right(
+ &mut self,
+ current_selection: usize,
+ ) -> SelectionChange {
+ let item_kind = self.tree[current_selection].kind.clone();
+ let item_path =
+ self.tree[current_selection].info.full_path.clone();
+
+ match item_kind {
+ FileTreeItemKind::Path(PathCollapsed(collapsed))
+ if collapsed =>
+ {
+ self.expand(&item_path, current_selection);
+ return SelectionChange::new(current_selection, true);
+ }
+ FileTreeItemKind::Path(PathCollapsed(collapsed))
+ if !collapsed =>
+ {
+ return self
+ .selection_updown(current_selection, false);
+ }
+ _ => (),
+ }
+
+ SelectionChange::new(current_selection, false)
+ }
+
+ fn selection_left(
+ &mut self,
+ current_selection: usize,
+ ) -> SelectionChange {
+ let item_kind = self.tree[current_selection].kind.clone();
+ let item_path =
+ self.tree[current_selection].info.full_path.clone();
+
+ if matches!(item_kind, FileTreeItemKind::File(_))
+ || matches!(item_kind,FileTreeItemKind::Path(PathCollapsed(collapsed))
if collapsed)
- {
- let mut cur_parent =
- self.tree.find_parent_index(current_selection);
- while !self.available_selections.contains(&cur_parent)
- && cur_parent != 0
- {
- cur_parent = self.tree.find_parent_index(cur_parent);
- }
- SelectionChange::new(cur_parent, false)
- } else if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed))
+ {
+ let mut cur_parent =
+ self.tree.find_parent_index(current_selection);
+ while !self.available_selections.contains(&cur_parent)
+ && cur_parent != 0
+ {
+ cur_parent = self.tree.find_parent_index(cur_parent);
+ }
+ SelectionChange::new(cur_parent, false)
+ } else if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed))
if !collapsed)
- {
- self.collapse(&item_path, current_selection);
- SelectionChange::new(current_selection, true)
- } else {
- SelectionChange::new(current_selection, false)
- }
- }
-
- fn collapse(&mut self, path: &str, index: usize) {
- if let FileTreeItemKind::Path(PathCollapsed(
- ref mut collapsed,
- )) = self.tree[index].kind
- {
- *collapsed = true;
- }
-
- let path = format!("{}/", path);
-
- for i in index + 1..self.tree.len() {
- let item = &mut self.tree[i];
- let item_path = &item.info.full_path;
- if item_path.starts_with(&path) {
- item.info.visible = false;
- } else {
- return;
- }
- }
- }
-
- fn expand(&mut self, path: &str, current_index: usize) {
- if let FileTreeItemKind::Path(PathCollapsed(
- ref mut collapsed,
- )) = self.tree[current_index].kind
- {
- *collapsed = false;
- }
-
- let path = format!("{}/", path);
-
- self.update_visibility(
- Some(path.as_str()),
- current_index + 1,
- false,
- );
- }
-
- fn update_visibility(
- &mut self,
- prefix: Option<&str>,
- start_idx: usize,
- set_defaults: bool,
- ) {
- // if we are in any subpath that is collapsed we keep skipping over it
- let mut inner_collapsed: Option<String> = None;
-
- for i in start_idx..self.tree.len() {
- if let Some(ref collapsed_path) = inner_collapsed {
- let p: &String = &self.tree[i].info.full_path;
- if p.starts_with(collapsed_path) {
- if set_defaults {
- self.tree[i].info.visible = false;
- }
- // we are still in a collapsed inner path
- continue;
- }
- inner_collapsed = None;
- }
-
- let item_kind = self.tree[i].kind.clone();
- let item_path = &self.tree[i].info.full_path;
-
- if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed)) if collapsed)
- {
- // we encountered an inner path that is still collapsed
- inner_collapsed = Some(format!("{}/", &item_path));
- }
-
- if prefix
- .map_or(true, |prefix| item_path.starts_with(prefix))
- {
- self.tree[i].info.visible = true;
- } else {
- // if we do not set defaults we can early out
- if set_defaults {
- self.tree[i].info.visible = false;
- } else {
- return;
- }
- }
- }
- }
+ {
+ self.collapse(&item_path, current_selection);
+ SelectionChange::new(current_selection, true)
+ } else {
+ SelectionChange::new(current_selection, false)
+ }
+ }
+
+ fn collapse(&mut self, path: &str, index: usize) {
+ if let FileTreeItemKind::Path(PathCollapsed(
+ ref mut collapsed,
+ )) = self.tree[index].kind
+ {
+ *collapsed = true;
+ }
+
+ let path = format!("{}/", path);
+
+ for i in index + 1..self.tree.len() {
+ let item = &mut self.tree[i];
+ let item_path = &item.info.full_path;
+ if item_path.starts_with(&path) {
+ item.info.visible = false;
+ } else {
+ return;
+ }
+ }
+ }
+
+ fn expand(&mut self, path: &str, current_index: usize) {
+ if let FileTreeItemKind::Path(PathCollapsed(
+ ref mut collapsed,
+ )) = self.tree[current_index].kind
+ {
+ *collapsed = false;
+ }
+
+ let path = format!("{}/", path);
+
+ self.update_visibility(
+ Some(path.as_str()),
+ current_index + 1,
+ false,
+ );
+ }
+
+ fn update_visibility(
+ &mut self,
+ prefix: Option<&str>,
+ start_idx: usize,
+ set_defaults: bool,
+ ) {
+ // if we are in any subpath that is collapsed we keep skipping over it
+ let mut inner_collapsed: Option<String> = None;
+
+ for i in start_idx..self.tree.len() {
+ if let Some(ref collapsed_path) = inner_collapsed {
+ let p: &String = &self.tree[i].info.full_path;
+ if p.starts_with(collapsed_path) {
+ if set_defaults {
+ self.tree[i].info.visible = false;
+ }
+ // we are still in a collapsed inner path
+ continue;
+ }
+ inner_collapsed = None;
+ }
+
+ let item_kind = self.tree[i].kind.clone();
+ let item_path = &self.tree[i].info.full_path;
+
+ if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed)) if collapsed)
+ {
+ // we encountered an inner path that is still collapsed
+ inner_collapsed = Some(format!("{}/", &item_path));
+ }
+
+ if prefix
+ .map_or(true, |prefix| item_path.starts_with(prefix))
+ {
+ self.tree[i].info.visible = true;
+ } else {
+ // if we do not set defaults we can early out
+ if set_defaults {
+ self.tree[i].info.visible = false;
+ } else {
+ return;
+ }
+ }
+ }
+ }
}
#[cfg(test)]
mod tests {
- use super::*;
- use asyncgit::StatusItemType;
-
- fn string_vec_to_status(items: &[&str]) -> Vec<StatusItem> {
- items
- .iter()
- .map(|a| StatusItem {
- path: String::from(*a),
- status: StatusItemType::Modified,
- })
- .collect::<Vec<_>>()
- }
-
- fn get_visibles(tree: &StatusTree) -> Vec<bool> {
- tree.tree
- .items()
- .iter()
- .map(|e| e.info.visible)
- .collect::<Vec<_>>()
- }
-
- #[test]
- fn test_selection() {
- let items = string_vec_to_status(&[
- "a/b", //
- ]);
-
- let mut res = StatusTree::default();
- res.update(&items).unwrap();
-
- assert!(res.move_selection(MoveSelection::Down));
-
- assert_eq!(res.selection, Some(1));
-
- assert!(res.move_selection(MoveSelection::Left));
-
- assert_eq!(res.selection, Some(0));
- }
-
- #[test]
- fn test_keep_selected_item() {
- let mut res = StatusTree::default();
- res.update(&string_vec_to_status(&["b"])).unwrap();
-
- assert_eq!(res.selection, Some(0));
-
- res.update(&string_vec_to_status(&["a", "b"])).unwrap();
-
- assert_eq!(res.selection, Some(1));
- }
-
- #[test]
- fn test_keep_selected_index() {
- let mut res = StatusTree::default();
- res.update(&string_vec_to_status(&["a", "b"])).unwrap();
- res.selection = Some(1);
-
- res.update(&string_vec_to_status(&["d", "c", "a"])).unwrap();
- assert_eq!(res.selection, Some(1));
- }
-
- #[test]
- fn test_keep_selected_index_if_not_collapsed() {
- let mut res = StatusTree::default();
- res.update(&string_vec_to_status(&["a/b", "c"])).unwrap();
-
- res.collapse("a/b", 0);
-
- res.selection = Some(2);
-
- res.update(&string_vec_to_status(&["a/b"])).unwrap();
- assert_eq!(
- get_visibles(&res),
- vec![
- true, //
- false, //
- ]
- );
- assert_eq!(
- res.is_visible_index(res.selection.unwrap()),
- true
- );
- assert_eq!(res.selection, Some(0));
- }
-
- #[test]
- fn test_keep_collapsed_states() {
- let mut res = StatusTree::default();
- res.update(&string_vec_to_status(&[
- "a/b", //
- "c",
- ]))
- .unwrap();
-
- res.collapse("a", 0);
-
- assert_eq!(
- res.all_collapsed().iter().collect::<Vec<_>>(),
- vec![&&String::from("a")]
- );
-
- assert_eq!(
- get_visibles(&res),
- vec![
- true, //
- false, //
- true, //
- ]
- );
-
- res.update(&string_vec_to_status(&[
- "a/b", //
- "c", //
- "d",
- ]))
- .unwrap();
-
- assert_eq!(
- res.all_collapsed().iter().collect::<Vec<_>>(),
- vec![&&String::from("a")]
- );
-
- assert_eq!(
- get_visibles(&res),
- vec![
- true, //
- false, //
- true, //
- true
- ]
- );
- }
-
- #[test]
- fn test_expand() {
- let items = string_vec_to_status(&[
- "a/b/c", //
- "a/d", //
- ]);
-
- //0 a/
- //1 b/
- //2 c
- //3 d
-
- let mut res = StatusTree::default();
- res.update(&items).unwrap();
-
- res.collapse(&String::from("a/b"), 1);
-
- let visibles = get_visibles(&res);
-
- assert_eq!(
- visibles,
- vec![
- true, //
- true, //
- false, //
- true,
- ]
- );
-
- res.expand(&String::from("a/b"), 1);
-
- let visibles = get_visibles(&res);
-
- assert_eq!(
- visibles,
- vec![
- true, //
- true, //
- true, //
- true,
- ]
- );
- }
-
- #[test]
- fn test_expand_bug() {
- let items = string_vec_to_status(&[
- "a/b/c", //
- "a/b2/d", //
- ]);
-
- //0 a/
- //1 b/
- //2 c
- //3 b2/
- //4 d
-
- let mut res = StatusTree::default();
- res.update(&items).unwrap();
-
- res.collapse(&String::from("b"), 1);
- res.collapse(&String::from("a"), 0);
-
- assert_eq!(
- get_visibles(&res),
- vec![
- true, //
- false, //
- false, //
- false, //
- false,
- ]
- );
-
- res.expand(&String::from("a"), 0);
-
- assert_eq!(
- get_visibles(&res),
- vec![
- true, //
- true, //
- false, //
- true, //
- true,
- ]
- );
- }
-
- #[test]
- fn test_collapse_too_much() {
- let items = string_vec_to_status(&[
- "a/b", //
- "a2/c", //
- ]);
-
- //0 a/
- //1 b
- //2 a2/
- //3 c
-
- let mut res = StatusTree::default();
- res.update(&items).unwrap();
-
- res.collapse(&String::from("a"), 0);
-
- let visibles = get_visibles(&res);
-
- assert_eq!(
- visibles,
- vec![
- true, //
- false, //
- true, //
- true,
- ]
- );
- }
-
- #[test]
- fn test_expand_with_collapsed_sub_parts() {
- let items = string_vec_to_status(&[
- "a/b/c", //
- "a/d", //
- ]);
-
- //0 a/
- //1 b/
- //2 c
- //3 d
-
- let mut res = StatusTree::default();
- res.update(&items).unwrap();
-
- res.collapse(&String::from("a/b"), 1);
-
- let visibles = get_visibles(&res);
-
- assert_eq!(
- visibles,
- vec![
- true, //
- true, //
- false, //
- true,
- ]
- );
-
- res.collapse(&String::from("a"), 0);
-
- let visibles = get_visibles(&res);
-
- assert_eq!(
- visibles,
- vec![
- true, //
- false, //
- false, //
- false,
- ]
- );
-
- res.expand(&String::from("a"), 0);
-
- let visibles = get_visibles(&res);
-
- assert_eq!(
- visibles,
- vec![
- true, //
- true, //
- false, //
- true,
- ]
- );
- }
-
- #[test]
- fn test_selection_skips_collapsed() {
- let items = string_vec_to_status(&[
- "a/b/c", //
- "a/d", //
- ]);
-
- //0 a/
- //1 b/