feat(explore): remove files/folder

pull/9/head
wongjiahau 1 year ago
parent 458fa1ca58
commit 2af8b41007

@ -223,8 +223,14 @@ impl TreeItem for FileInfo {
#[derive(Clone, Debug)]
enum PromptAction {
Search(bool), // search next/search pre
CreateFolder { folder_path: PathBuf },
CreateFile { folder_path: PathBuf },
CreateFolder {
folder_path: PathBuf,
parent_index: usize,
},
CreateFile {
folder_path: PathBuf,
parent_index: usize,
},
RemoveDir,
RemoveFile,
Filter,
@ -306,7 +312,7 @@ impl Explorer {
Ok(_) => {
self.focus();
}
Err(error) => cx.editor.set_error(error),
Err(error) => cx.editor.set_error(error.to_string()),
}
}
}
@ -396,9 +402,10 @@ impl Explorer {
}
fn new_create_folder_prompt(&mut self) -> Result<()> {
let folder_path = self.current_parent_folder_path()?;
let (parent_index, folder_path) = self.nearest_folder()?;
self.prompt = Some((
PromptAction::CreateFolder {
parent_index,
folder_path: folder_path.clone(),
},
Prompt::new(
@ -412,9 +419,10 @@ impl Explorer {
}
fn new_create_file_prompt(&mut self) -> Result<()> {
let folder_path = self.current_parent_folder_path()?;
let (parent_index, folder_path) = self.nearest_folder()?;
self.prompt = Some((
PromptAction::CreateFile {
parent_index,
folder_path: folder_path.clone(),
},
Prompt::new(
@ -427,18 +435,36 @@ impl Explorer {
Ok(())
}
fn current_parent_folder_path(&self) -> Result<PathBuf> {
let current_item = self.tree.current_item();
Ok(current_item
.path
.parent()
.ok_or_else(|| {
fn nearest_folder(&self) -> Result<(usize, PathBuf)> {
let current = self.tree.current();
if current.item().is_parent() {
Ok((current.index(), current.item().path.to_path_buf()))
} else {
let parent_index = current.parent_index().ok_or_else(|| {
anyhow::anyhow!(format!(
"Unable to get parent index of '{}'",
current.item().path.to_string_lossy()
))
})?;
let parent_path = current.item().path.parent().ok_or_else(|| {
anyhow::anyhow!(format!(
"Unable to get parent directory of '{}'",
current_item.path.to_string_lossy()
"Unable to get parent path of '{}'",
current.item().path.to_string_lossy()
))
})?
.to_path_buf())
})?;
Ok((parent_index, parent_path.to_path_buf()))
}
}
fn new_remove_prompt(&mut self, cx: &mut Context) {
let item = self.tree.current().item();
match item.file_type {
FileType::Dir => self.new_remove_dir_prompt(cx),
FileType::Exe | FileType::File => self.new_remove_file_prompt(cx),
FileType::Placeholder => cx.editor.set_error("Placeholder is not removable."),
FileType::Parent => cx.editor.set_error("Parent is not removable."),
FileType::Root => cx.editor.set_error("Root is not removable"),
}
}
fn new_remove_file_prompt(&mut self, cx: &mut Context) {
@ -458,7 +484,7 @@ impl Explorer {
cx.editor.set_error(format!("{e}"));
return;
}
let p = format!("remove file: {}, YES? ", item.path.display());
let p = format!(" Delete file: '{}'? y/n: ", item.path.display());
self.prompt = Some((
PromptAction::RemoveFile,
Prompt::new(p.into(), None, ui::completers::none, |_, _, _| {}),
@ -486,7 +512,7 @@ impl Explorer {
cx.editor.set_error(format!("{e}"));
return;
}
let p = format!("remove dir: {}, YES? ", item.path.display());
let p = format!(" Delete folder: '{}'? y/n: ", item.path.display());
self.prompt = Some((
PromptAction::RemoveDir,
Prompt::new(p.into(), None, ui::completers::none, |_, _, _| {}),
@ -738,27 +764,41 @@ impl Explorer {
};
let line = prompt.line();
match (&action, event.into()) {
(PromptAction::CreateFolder { folder_path }, key!(Enter)) => {
if let Err(e) = self.new_path(folder_path.clone(), line, true) {
(
PromptAction::CreateFolder {
folder_path,
parent_index,
},
key!(Enter),
) => {
if let Err(e) = self.new_path(folder_path.clone(), line, true, *parent_index) {
cx.editor.set_error(format!("{e}"))
}
}
(PromptAction::CreateFile { folder_path }, key!(Enter)) => {
if let Err(e) = self.new_path(folder_path.clone(), line, false) {
(
PromptAction::CreateFile {
folder_path,
parent_index,
},
key!(Enter),
) => {
if let Err(e) = self.new_path(folder_path.clone(), line, false, *parent_index) {
cx.editor.set_error(format!("{e}"))
}
}
(PromptAction::RemoveDir, key!(Enter)) => {
let item = self.tree.current_item();
if let Err(e) = std::fs::remove_dir_all(&item.path) {
cx.editor.set_error(format!("{e}"));
} else {
self.tree.fold_current_child();
self.tree.remove_current();
if line == "y" {
let item = self.tree.current_item();
if let Err(e) = std::fs::remove_dir_all(&item.path) {
cx.editor.set_error(format!("{e}"));
} else {
self.tree.fold_current_child();
self.tree.remove_current();
}
}
}
(PromptAction::RemoveFile, key!(Enter)) => {
if line == "YES" {
if line == "y" {
let item = self.tree.current_item();
if let Err(e) = std::fs::remove_file(&item.path) {
cx.editor.set_error(format!("{e}"));
@ -776,7 +816,13 @@ impl Explorer {
EventResult::Consumed(None)
}
fn new_path(&mut self, current_parent: PathBuf, file_name: &str, is_dir: bool) -> Result<()> {
fn new_path(
&mut self,
current_parent: PathBuf,
file_name: &str,
is_dir: bool,
parent_index: usize,
) -> Result<()> {
let current = self.tree.current_item();
let p = helix_core::path::get_normalized_path(&current_parent.join(file_name));
match p.parent() {
@ -795,7 +841,7 @@ impl Explorer {
if current.file_type == FileType::Placeholder {
self.tree.replace_current(file);
} else {
self.tree.add_sibling_to_current_item(file)?;
self.tree.add_child(parent_index, file)?;
}
Ok(())
}
@ -841,11 +887,6 @@ impl Component for Explorer {
self.repeat_motion = Some(repeat_motion);
}
}
key!(Backspace) => {
if let Some(parent) = self.state.current_root.parent().clone() {
self.change_root(cx, parent.to_path_buf())
}
}
key!('f') => self.new_filter_prompt(),
key!('/') => self.new_search_prompt(true),
key!('?') => self.new_search_prompt(false),
@ -859,7 +900,13 @@ impl Component for Explorer {
cx.editor.set_error(error.to_string())
}
}
key!('o') => self.change_root(cx, self.tree.current_item().path.clone()),
key!('[') => {
if let Some(parent) = self.state.current_root.parent().clone() {
self.change_root(cx, parent.to_path_buf())
}
}
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() {

@ -54,37 +54,6 @@ fn vec_to_tree<T: TreeItem>(mut items: Vec<T>) -> Vec<Tree<T>> {
)
}
// return total elems's count contain self
fn get_elems_recursion<T: TreeItem>(t: &mut Tree<T>, depth: usize) -> Result<usize> {
let mut childs = t.item.get_children()?;
childs.sort_by(tree_item_cmp);
let mut elems = Vec::with_capacity(childs.len());
// let level = t.level + 1;
let level = todo!();
let mut total = 1;
for child in childs {
let mut elem = Tree::new(child, level);
let count = if depth > 0 {
get_elems_recursion(&mut elem, depth - 1)?
} else {
1
};
elems.push(elem);
total += count;
}
t.children = elems;
Ok(total)
}
fn expand_elems<T: TreeItem>(dist: &mut Vec<Tree<T>>, mut t: Tree<T>) {
let childs = std::mem::take(&mut t.children);
dist.push(t);
for child in childs {
expand_elems(dist, child)
}
}
pub enum TreeOp<T> {
Noop,
Restore,
@ -187,6 +156,24 @@ impl<T: Clone> Tree<T> {
None
}
}
pub fn parent_index(&self) -> Option<usize> {
self.parent_index
}
pub fn index(&self) -> usize {
self.index
}
}
impl<T: TreeItem> Tree<T> {
fn open(&mut self) -> Result<()> {
self.children = vec_to_tree(self.item.get_children()?);
if !self.children.is_empty() {
self.is_opened = true;
}
Ok(())
}
}
impl<T> Tree<T> {
@ -195,7 +182,7 @@ impl<T> Tree<T> {
item,
index: 0,
parent_index: None,
children: index_elems(1, children),
children: index_elems(0, children),
is_opened: false,
}
}
@ -252,7 +239,26 @@ impl<T> Tree<T> {
fn regenerate_index(&mut self) {
let items = std::mem::take(&mut self.children);
self.children = index_elems(1, items);
self.children = index_elems(0, items);
}
fn remove(&mut self, index: usize) {
let children = std::mem::replace(&mut self.children, vec![]);
self.children = children
.into_iter()
.filter_map(|tree| {
if tree.index == index {
None
} else {
Some(tree)
}
})
.map(|mut tree| {
tree.remove(index);
tree
})
.collect();
self.regenerate_index()
}
}
@ -345,7 +351,7 @@ impl<T: TreeItem> TreeView<T> {
/// ```
/// vec!["helix-term", "src", "ui", "tree.rs"]
/// ```
pub fn reveal_item(&mut self, segments: Vec<&str>) -> Result<(), String> {
pub fn reveal_item(&mut self, segments: Vec<&str>) -> Result<()> {
// Expand the tree
segments.iter().fold(
Ok(&mut self.tree),
@ -359,20 +365,15 @@ impl<T: TreeItem> TreeView<T> {
{
Some(tree) => {
if !tree.is_opened {
tree.children = vec_to_tree(
tree.item.get_children().map_err(|err| err.to_string())?,
);
if !tree.children.is_empty() {
tree.is_opened = true;
}
tree.open()?;
}
Ok(tree)
}
None => Err(format!(
None => Err(anyhow::anyhow!(format!(
"Unable to find path: '{}'. current_segment = {}",
segments.join("/"),
segment
)),
))),
}
}
},
@ -416,23 +417,19 @@ impl<T: TreeItem> TreeView<T> {
}
}
fn go_to_children(&mut self, cx: &mut Context) {
fn go_to_children(&mut self, cx: &mut Context) -> Result<()> {
let current = self.current_mut();
if current.is_opened {
self.selected += 1;
return;
}
let items = match current.item.get_children() {
Ok(items) => items,
Err(e) => return cx.editor.set_error(format!("{e}")),
};
if items.is_empty() {
return;
Ok(())
} else {
current.open()?;
if !current.children.is_empty() {
self.selected += 1;
self.regenerate_index();
}
Ok(())
}
current.is_opened = true;
current.children = vec_to_tree(items);
self.selected += 1;
self.regenerate_index()
}
}
@ -639,11 +636,8 @@ impl<T: TreeItem> TreeView<T> {
self.winline
}
pub fn remove_current(&mut self) -> T {
todo!()
// let elem = self.tree.remove(self.selected);
// self.selected = self.selected.saturating_sub(1);
// elem.item
pub fn remove_current(&mut self) {
self.tree.remove(self.selected)
}
pub fn replace_current(&mut self, item: T) {
@ -654,25 +648,26 @@ impl<T: TreeItem> TreeView<T> {
self.selected = selected
}
pub fn add_sibling_to_current_item(&mut self, item: T) -> Result<()> {
let current = self.current();
match current.parent_index {
pub fn add_child(&mut self, index: usize, item: T) -> Result<()> {
match self.tree.get_mut(index) {
None => Err(anyhow::anyhow!(format!(
"Current item = '{}' has no parent",
current.item.name()
"No item found at index = {}",
index
))),
Some(parent_index) => {
let parent = self.get_mut(parent_index);
Some(tree) => {
let item_name = item.name();
parent.children.push(Tree::new(item, vec![]));
parent
.children
if !tree.is_opened {
tree.open()?;
}
tree.children.push(Tree::new(item, vec![]));
tree.children
.sort_by(|a, b| tree_item_cmp(&a.item, &b.item));
self.regenerate_index();
let parent = self.get_mut(parent_index);
let tree = self.get_mut(index);
// Focus the added sibling
if let Some(tree) = parent
if let Some(tree) = tree
.children
.iter()
.find(|tree| tree.item.name().eq(&item_name))
@ -903,7 +898,10 @@ impl<T: TreeItem> TreeView<T> {
}));
}
key!('h') => self.go_to_parent(),
key!('l') => self.go_to_children(cx),
key!('l') => match self.go_to_children(cx) {
Ok(_) => {}
Err(err) => cx.editor.set_error(err.to_string()),
},
key!(Enter) => self.on_enter(cx, params, self.selected),
ctrl!('d') => self.move_down_half_page(),
ctrl!('u') => self.move_up_half_page(),
@ -971,25 +969,24 @@ impl<T: TreeItem + Clone> TreeView<T> {
/// jar (3)
/// yo (4)
/// ```
fn index_elems<T>(start_index: usize, elems: Vec<Tree<T>>) -> Vec<Tree<T>> {
fn index_elems<T>(parent_index: usize, elems: Vec<Tree<T>>) -> Vec<Tree<T>> {
fn index_elems<'a, T>(
current_index: usize,
elems: Vec<Tree<T>>,
parent_index: Option<usize>,
parent_index: usize,
) -> (usize, Vec<Tree<T>>) {
elems
.into_iter()
.fold((current_index, vec![]), |(current_index, trees), elem| {
let index = current_index;
let item = elem.item;
let (current_index, folded) =
index_elems(current_index + 1, elem.children, Some(index));
let (current_index, folded) = index_elems(current_index + 1, elem.children, index);
let tree = Tree {
item,
children: folded,
index,
is_opened: elem.is_opened,
parent_index,
parent_index: Some(parent_index),
};
(
current_index,
@ -997,7 +994,7 @@ fn index_elems<T>(start_index: usize, elems: Vec<Tree<T>>) -> Vec<Tree<T>> {
)
})
}
index_elems(start_index, elems, None).1
index_elems(parent_index + 1, elems, parent_index).1
}
#[cfg(test)]
@ -1008,8 +1005,8 @@ mod test_tree {
#[test]
fn test_indexs_elems() {
let result = index_elems(
0,
let result = Tree::new(
"root",
vec![
Tree::new("foo", vec![Tree::new("bar", vec![])]),
Tree::new(
@ -1018,43 +1015,12 @@ mod test_tree {
),
],
);
assert_eq!(
result,
vec![
Tree {
item: "foo",
is_opened: false,
index: 0,
parent_index: None,
children: vec![Tree {
item: "bar",
is_opened: false,
index: 1,
parent_index: Some(0),
children: vec![]
}]
},
Tree {
item: "spam",
is_opened: false,
index: 2,
parent_index: None,
children: vec![Tree {
item: "jar",
is_opened: false,
index: 3,
parent_index: Some(2),
children: vec![Tree {
item: "yo",
is_opened: false,
index: 4,
children: vec![],
parent_index: Some(3)
}]
}]
}
]
)
assert_eq!(result.get(0).unwrap().item, "root");
assert_eq!(result.get(1).unwrap().item, "foo");
assert_eq!(result.get(2).unwrap().item, "bar");
assert_eq!(result.get(3).unwrap().item, "spam");
assert_eq!(result.get(4).unwrap().item, "jar");
assert_eq!(result.get(5).unwrap().item, "yo");
}
#[test]
@ -1224,4 +1190,40 @@ mod test_tree {
let result = Tree::filter(&tree, &|item| item.to_lowercase().contains("helix"));
assert_eq!(result, None)
}
#[test]
fn test_remove() {
let mut tree = Tree::new(
".cargo",
vec![
Tree::new("spam", vec![Tree::new("Cargo.toml", vec![])]),
Tree::new("Cargo.toml", vec![Tree::new("pam", vec![])]),
Tree::new("hello", vec![]),
],
);
tree.remove(2);
assert_eq!(
tree,
Tree::new(
".cargo",
vec![
Tree::new("spam", vec![]),
Tree::new("Cargo.toml", vec![Tree::new("pam", vec![])]),
Tree::new("hello", vec![]),
],
)
);
tree.remove(2);
assert_eq!(
tree,
Tree::new(
".cargo",
vec![Tree::new("spam", vec![]), Tree::new("hello", vec![]),],
)
)
}
}

Loading…
Cancel
Save