diff --git a/helix-term/src/ui/explore.rs b/helix-term/src/ui/explore.rs index 375a6d3b..c1ec0067 100644 --- a/helix-term/src/ui/explore.rs +++ b/helix-term/src/ui/explore.rs @@ -9,7 +9,7 @@ use helix_view::{ editor::{Action, ExplorerPositionEmbed}, graphics::{CursorKind, Rect}, input::{Event, KeyEvent}, - Editor, + DocumentId, Editor, }; use std::borrow::Cow; use std::cmp::Ordering; @@ -137,8 +137,8 @@ enum PromptAction { parent_index: usize, }, RemoveDir, - RemoveFile, - RenameFile, + RemoveFile(Option), + RenameFile(Option), Filter, } @@ -232,7 +232,7 @@ impl Explorer { ) .split(std::path::MAIN_SEPARATOR) .collect::>(); - self.tree.reveal_item(segments)?; + self.tree.reveal_item(segments, &self.state.filter)?; self.focus(); Ok(()) } @@ -332,7 +332,8 @@ impl Explorer { self.tree.save_view(); self.prompt = Some(( PromptAction::Filter, - Prompt::new(" Filter: ".into(), None, ui::completers::none, |_, _, _| {}), + Prompt::new(" Filter: ".into(), None, ui::completers::none, |_, _, _| {}) + .with_line(self.state.filter.clone()), )) } @@ -400,17 +401,17 @@ impl Explorer { } } - fn new_rename_prompt(&mut self) { - let name = self.tree.current_item().path.to_string_lossy(); + fn new_rename_prompt(&mut self, cx: &mut Context) { + let path = self.tree.current_item().path.clone(); self.prompt = Some(( - PromptAction::RenameFile, + PromptAction::RenameFile(cx.editor.document_by_path(&path).map(|doc| doc.id())), Prompt::new( format!(" Rename to ").into(), None, ui::completers::none, |_, _, _| {}, ) - .with_line(name.to_string()), + .with_line(path.to_string_lossy().to_string()), )); } @@ -419,18 +420,18 @@ impl Explorer { let check = || { ensure!(item.path.is_file(), "The path is not a file"); let doc = cx.editor.document_by_path(&item.path); - ensure!(doc.is_none(), "The file is opened"); - Ok(()) + Ok(doc.map(|doc| doc.id())) }; - if let Err(e) = check() { - cx.editor.set_error(format!("{e}")); - return; + match check() { + Err(err) => cx.editor.set_error(format!("{err}")), + Ok(document_id) => { + let p = format!(" Delete file: '{}'? y/n: ", item.path.display()); + self.prompt = Some(( + PromptAction::RemoveFile(document_id), + Prompt::new(p.into(), None, ui::completers::none, |_, _, _| {}), + )); + } } - let p = format!(" Delete file: '{}'? y/n: ", item.path.display()); - self.prompt = Some(( - PromptAction::RemoveFile, - Prompt::new(p.into(), None, ui::completers::none, |_, _, _| {}), - )); } fn new_remove_dir_prompt(&mut self, cx: &mut Context) { @@ -506,7 +507,7 @@ impl Explorer { cx.editor.theme.get("ui.text"), ); self.tree - .render(list_area.clip_top(1), surface, cx, &mut self.state); + .render(list_area.clip_top(1), surface, cx, &self.state.filter); } pub fn render_embed( @@ -553,7 +554,7 @@ impl Explorer { cx.editor.theme.get("ui.text"), ); self.tree - .render(list_area.clip_top(1), surface, cx, &mut self.state); + .render(list_area.clip_top(1), surface, cx, &self.state.filter); { let statusline = if self.is_focus() { @@ -618,9 +619,10 @@ impl Explorer { match event.into() { key!(Tab) | key!(Down) | ctrl!('j') => { self.tree.clean_recycle(); + let filter = self.state.filter.clone(); return self .tree - .handle_event(Event::Key(event), cx, &mut self.state); + .handle_event(Event::Key(event), cx, &mut self.state, &filter); } key!(Enter) => { if let EventResult::Consumed(_) = prompt.handle_event(Event::Key(event), cx) { @@ -632,6 +634,7 @@ impl Explorer { if let EventResult::Consumed(_) = prompt.handle_event(Event::Key(event), cx) { self.tree.filter(prompt.line()); } + self.state.filter = prompt.line().clone(); self.prompt = Some((action, prompt)); } }; @@ -646,9 +649,10 @@ impl Explorer { }; match event.into() { key!(Tab) | key!(Down) | ctrl!('j') => { + let filter = self.state.filter.clone(); return self .tree - .handle_event(Event::Key(event), cx, &mut self.state) + .handle_event(Event::Key(event), cx, &mut self.state, &filter); } key!(Enter) => { let search_str = prompt.line().clone(); @@ -727,18 +731,24 @@ impl Explorer { explorer.tree.remove_current(); } } - (PromptAction::RemoveFile, key!(Enter)) => { + (PromptAction::RemoveFile(document_id), key!(Enter)) => { if line == "y" { let item = explorer.tree.current_item(); std::fs::remove_file(&item.path).map_err(anyhow::Error::from)?; explorer.tree.remove_current(); + if let Some(id) = document_id { + cx.editor.close_document(*id, true)? + } } } - (PromptAction::RenameFile, key!(Enter)) => { + (PromptAction::RenameFile(document_id), key!(Enter)) => { let item = explorer.tree.current_item(); std::fs::rename(&item.path, line)?; explorer.tree.remove_current(); explorer.reveal_file(PathBuf::from(line))?; + if let Some(id) = document_id { + cx.editor.close_document(*id, true)? + } } (_, key!(Esc) | ctrl!('c')) => {} _ => { @@ -778,7 +788,8 @@ impl Explorer { fd.create_new(true).write(true).open(&path)?; FileInfo::new(path, FileType::File) }; - self.tree.add_child(parent_index, file)?; + self.tree + .add_child(parent_index, file, &self.state.filter)?; Ok(()) } @@ -868,17 +879,18 @@ impl Component for Explorer { key!(']') => self.change_root(cx, self.tree.current_item().path.clone()), ctrl!('o') => self.go_to_previous_root(), key!('d') => self.new_remove_prompt(cx), - key!('r') => self.new_rename_prompt(), + key!('r') => self.new_rename_prompt(cx), shift!('R') => { - if let Err(error) = self.tree.refresh() { + if let Err(error) = self.tree.refresh(&self.state.filter) { cx.editor.set_error(error.to_string()) } } key!('-') => self.decrease_size(), key!('+') => self.increase_size(), _ => { + let filter = self.state.filter.clone(); self.tree - .handle_event(Event::Key(key_event), cx, &mut self.state); + .handle_event(Event::Key(key_event), cx, &mut self.state, &filter); } } diff --git a/helix-term/src/ui/tree.rs b/helix-term/src/ui/tree.rs index 7763fb63..229b92bd 100644 --- a/helix-term/src/ui/tree.rs +++ b/helix-term/src/ui/tree.rs @@ -1,4 +1,4 @@ -use std::cmp::Ordering; +use std::{borrow::Cow, cmp::Ordering}; use anyhow::Result; use helix_view::theme::Modifier; @@ -156,19 +156,31 @@ impl Tree { } impl Tree { - fn open(&mut self) -> Result<()> { - self.children = vec_to_tree(self.item.get_children()?); - if !self.children.is_empty() { + fn open(&mut self, filter: &String) -> Result<()> { + if self.item.is_parent() { + self.children = vec_to_tree( + self.item + .get_children()? + .into_iter() + .filter(|item| item.name().to_lowercase().contains(&filter.to_lowercase())) + .collect(), + ); self.is_opened = true; } Ok(()) } - fn refresh(&mut self) -> Result<()> { + fn refresh(&mut self, filter: &String) -> Result<()> { if !self.is_opened { return Ok(()); } - let latest_children = vec_to_tree(self.item.get_children()?); + let latest_children = vec_to_tree( + self.item + .get_children()? + .into_iter() + .filter(|item| item.name().to_lowercase().contains(&filter.to_lowercase())) + .collect(), + ); let filtered = std::mem::replace(&mut self.children, vec![]) .into_iter() // Remove children that does not exists in latest_children @@ -178,7 +190,7 @@ impl Tree { .any(|child| tree.item.name().eq(&child.item.name())) }) .map(|mut tree| { - tree.refresh()?; + tree.refresh(filter)?; Ok(tree) }) .collect::>>()?; @@ -249,12 +261,47 @@ impl Tree { &self.item } - fn get(&self, index: usize) -> Option<&Tree> { + fn get<'a>(&'a self, index: usize) -> Option<&'a Tree> { if self.index == index { Some(self) } else { self.children.iter().find_map(|elem| elem.get(index)) } + // self.traverse(None, &|result, current_index, tree| { + // result.or_else(|| { + // if index == current_index { + // Some(tree) + // } else { + // None + // } + // }) + // }) + } + fn traverse<'a, U, F>(&'a self, init: U, f: &F) -> U + where + F: Fn(U, usize, &'a Tree) -> U, + { + fn traverse<'a, T, U, F>( + tree: &'a Tree, + current_index: usize, + init: U, + f: &F, + ) -> (usize, U) + where + F: Fn(U, usize, &'a Tree) -> U, + { + let mut result = f(init, current_index, &tree); + let mut current_index = current_index; + for tree in &tree.children { + (current_index, result) = traverse(tree, current_index + 1, result, f) + } + (current_index, result) + // tree.children.iter().fold( + // (current_index, f(init, 0, &tree)), + // |(current_index, result), tree| traverse(tree, current_index + 1, result, &f), + // ) + } + traverse(self, 0, init, f).1 } fn get_mut(&mut self, index: usize) -> Option<&mut Tree> { @@ -384,8 +431,8 @@ impl TreeView { /// ``` /// vec!["helix-term", "src", "ui", "tree.rs"] /// ``` - pub fn reveal_item(&mut self, segments: Vec<&str>) -> Result<()> { - self.tree.refresh()?; + pub fn reveal_item(&mut self, segments: Vec<&str>, filter: &String) -> Result<()> { + self.tree.refresh(filter)?; // Expand the tree segments.iter().fold( @@ -400,7 +447,7 @@ impl TreeView { { Some(tree) => { if !tree.is_opened { - tree.open()?; + tree.open(filter)?; } Ok(tree) } @@ -452,13 +499,13 @@ impl TreeView { } } - fn go_to_children(&mut self) -> Result<()> { + fn go_to_children(&mut self, filter: &String) -> Result<()> { let current = self.current_mut(); if current.is_opened { self.selected += 1; Ok(()) } else { - current.open()?; + current.open(filter)?; if !current.children.is_empty() { self.selected += 1; self.regenerate_index(); @@ -467,8 +514,8 @@ impl TreeView { } } - pub fn refresh(&mut self) -> Result<()> { - self.tree.refresh() + pub fn refresh(&mut self, filter: &String) -> Result<()> { + self.tree.refresh(filter) } } @@ -492,7 +539,13 @@ pub fn tree_view_help() -> Vec { } impl TreeView { - pub fn on_enter(&mut self, cx: &mut Context, params: &mut T::Params, selected_index: usize) { + pub fn on_enter( + &mut self, + cx: &mut Context, + params: &mut T::Params, + selected_index: usize, + filter: &String, + ) { // if let Some(next_level) = self.next_item().map(|elem| elem.level) { // let current = self.find_by_index(selected_index); // let current_level = current.level; @@ -516,15 +569,12 @@ impl TreeView { if let Some(mut on_open_fn) = self.on_opened_fn.take() { let mut f = || { - let current = &mut self.get_mut(selected_index); + let current = self.current_mut(); match on_open_fn(&mut current.item, cx, params) { TreeOp::GetChildsAndInsert => { - let items = match current.item.get_children() { - Ok(items) => items, - Err(e) => return cx.editor.set_error(format!("{e}")), - }; - current.is_opened = true; - current.children = vec_to_tree(items); + if let Err(err) = current.open(filter) { + cx.editor.set_error(format!("{err}")) + } } TreeOp::Noop => {} }; @@ -675,7 +725,7 @@ impl TreeView { self.selected = selected } - pub fn add_child(&mut self, index: usize, item: T) -> Result<()> { + pub fn add_child(&mut self, index: usize, item: T, filter: &String) -> Result<()> { match self.tree.get_mut(index) { None => Err(anyhow::anyhow!(format!( "No item found at index = {}", @@ -684,7 +734,7 @@ impl TreeView { Some(tree) => { let item_name = item.name(); if !tree.is_opened { - tree.open()?; + tree.open(filter)?; } tree.children.push(Tree::new(item, vec![])); tree.children @@ -707,14 +757,8 @@ impl TreeView { } } -impl TreeView { - pub fn render( - &mut self, - area: Rect, - surface: &mut Surface, - cx: &mut Context, - params: &mut T::Params, - ) { +impl TreeView { + pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context, filter: &String) { if let Some(pre_render) = self.pre_render.take() { pre_render(self, area); } @@ -732,6 +776,7 @@ impl TreeView { is_last: true, level: 0, selected: self.selected, + filter, }; let rendered = render_tree(params); @@ -755,6 +800,7 @@ impl TreeView { is_last: bool, level: u16, selected: usize, + filter: &'a str, } fn render_tree( @@ -764,6 +810,7 @@ impl TreeView { is_last, level, selected, + filter, }: RenderElemParams, ) -> Vec<(Indent, Node)> { let indent = if level > 0 { @@ -805,6 +852,7 @@ impl TreeView { is_last, level: level + 1, selected, + filter, }) }), ) @@ -843,6 +891,7 @@ impl TreeView { event: Event, cx: &mut Context, params: &mut T::Params, + filter: &String, ) -> EventResult { let key_event = match event { Event::Key(event) => event, @@ -868,11 +917,11 @@ impl TreeView { })); } key!('h') => self.go_to_parent(), - key!('l') => match self.go_to_children() { + key!('l') => match self.go_to_children(filter) { Ok(_) => {} Err(err) => cx.editor.set_error(err.to_string()), }, - key!(Enter) => self.on_enter(cx, params, self.selected), + key!(Enter) => self.on_enter(cx, params, self.selected, filter), ctrl!('d') => self.move_down_half_page(), ctrl!('u') => self.move_up_half_page(), key!('g') => { @@ -976,7 +1025,7 @@ mod test_tree { use super::Tree; #[test] - fn test_indexs_elems() { + fn test_get() { let result = Tree::new( "root", vec![