From 44b46dda6aedabe76b13de406f9c9a9784b2640c Mon Sep 17 00:00:00 2001 From: wongjiahau Date: Mon, 13 Feb 2023 15:51:02 +0800 Subject: [PATCH] feat(explore): rename file/folder --- helix-term/src/ui/explore.rs | 81 ++++++++++++++++++++++-------------- helix-term/src/ui/tree.rs | 47 ++++++++++++++++++++- 2 files changed, 95 insertions(+), 33 deletions(-) diff --git a/helix-term/src/ui/explore.rs b/helix-term/src/ui/explore.rs index a04a605f..d4d3ae69 100644 --- a/helix-term/src/ui/explore.rs +++ b/helix-term/src/ui/explore.rs @@ -233,6 +233,7 @@ enum PromptAction { }, RemoveDir, RemoveFile, + RenameFile, Filter, } @@ -289,32 +290,34 @@ impl Explorer { } } + fn reveal_file(&mut self, cx: &mut Context, path: PathBuf) { + let current_root = &self.state.current_root; + let current_path = path.as_path().to_string_lossy().to_string(); + let current_root = current_root.as_path().to_string_lossy().to_string() + "/"; + let segments = current_path + .strip_prefix(current_root.as_str()) + .expect( + format!( + "Failed to strip prefix '{}' from '{}'", + current_root, current_path + ) + .as_str(), + ) + .split(std::path::MAIN_SEPARATOR) + .collect::>(); + match self.tree.reveal_item(segments) { + Ok(_) => { + self.focus(); + } + Err(error) => cx.editor.set_error(error.to_string()), + } + } + pub fn reveal_current_file(&mut self, cx: &mut Context) { let current_document_path = doc!(cx.editor).path().cloned(); match current_document_path { None => cx.editor.set_error("No opened document."), - Some(current_path) => { - let current_root = &self.state.current_root; - let current_path = current_path.as_path().to_string_lossy().to_string(); - let current_root = current_root.as_path().to_string_lossy().to_string() + "/"; - let segments = current_path - .strip_prefix(current_root.as_str()) - .expect( - format!( - "Failed to strip prefix '{}' from '{}'", - current_root, current_path - ) - .as_str(), - ) - .split(std::path::MAIN_SEPARATOR) - .collect::>(); - match self.tree.reveal_item(segments) { - Ok(_) => { - self.focus(); - } - Err(error) => cx.editor.set_error(error.to_string()), - } - } + Some(current_path) => self.reveal_file(cx, current_path), } } @@ -467,6 +470,20 @@ impl Explorer { } } + fn new_rename_prompt(&mut self) { + let name = self.tree.current_item().path.to_string_lossy(); + self.prompt = Some(( + PromptAction::RenameFile, + Prompt::new( + format!(" Rename to ").into(), + None, + ui::completers::none, + |_, _, _| {}, + ) + .with_line(name.to_string()), + )); + } + fn new_remove_file_prompt(&mut self, cx: &mut Context) { let item = self.tree.current_item(); let check = || { @@ -807,6 +824,15 @@ impl Explorer { } } } + (PromptAction::RenameFile, key!(Enter)) => { + let item = self.tree.current_item(); + if let Err(e) = std::fs::rename(&item.path, line) { + cx.editor.set_error(format!("{e}")); + } else { + self.tree.remove_current(); + self.reveal_file(cx, PathBuf::from(line)) + } + } (_, key!(Esc) | ctrl!('c')) => {} _ => { prompt.handle_event(Event::Key(event), cx); @@ -907,16 +933,7 @@ impl Component for Explorer { } key!(']') => self.change_root(cx, self.tree.current_item().path.clone()), key!('d') => self.new_remove_prompt(cx), - key!('r') => { - self.on_next_key = Some(Box::new(|cx, explorer, event| { - match event.into() { - key!('d') => explorer.new_remove_dir_prompt(cx), - key!('f') => explorer.new_remove_file_prompt(cx), - _ => return EventResult::Ignored(None), - }; - EventResult::Consumed(None) - })); - } + key!('r') => self.new_rename_prompt(), _ => { self.tree .handle_event(Event::Key(key_event), cx, &mut self.state); diff --git a/helix-term/src/ui/tree.rs b/helix-term/src/ui/tree.rs index 16251669..c147b1e3 100644 --- a/helix-term/src/ui/tree.rs +++ b/helix-term/src/ui/tree.rs @@ -174,18 +174,61 @@ impl Tree { } Ok(()) } + + fn refresh(&mut self) -> Result<()> { + if !self.is_opened { + return Ok(()); + } + let latest_children = vec_to_tree(self.item.get_children()?); + let filtered = std::mem::replace(&mut self.children, vec![]) + .into_iter() + // Remove children that does not exists in latest_children + .filter(|tree| { + latest_children + .iter() + .any(|child| tree.item.name().eq(&child.item.name())) + }) + .map(|mut tree| { + tree.refresh()?; + Ok(tree) + }) + .collect::>>()?; + + // Add new children + let new_nodes = latest_children + .into_iter() + .filter(|child| { + !filtered + .iter() + .any(|child_| child.item.name().eq(&child_.item.name())) + }) + .collect::>(); + + self.children = filtered.into_iter().chain(new_nodes).collect(); + + self.sort(); + + Ok(()) + } + + fn sort(&mut self) { + self.children + .sort_by(|a, b| tree_item_cmp(&a.item, &b.item)) + } } impl Tree { pub fn new(item: T, children: Vec>) -> Self { + let is_opened = !children.is_empty(); Self { item, index: 0, parent_index: None, children: index_elems(0, children), - is_opened: false, + is_opened, } } + fn iter(&self) -> TreeIter { TreeIter { tree: self, @@ -352,6 +395,8 @@ impl TreeView { /// vec!["helix-term", "src", "ui", "tree.rs"] /// ``` pub fn reveal_item(&mut self, segments: Vec<&str>) -> Result<()> { + self.tree.refresh()?; + // Expand the tree segments.iter().fold( Ok(&mut self.tree),