feat(explore): add folder/file

pull/9/head
wongjiahau 1 year ago
parent 0f8b641a5d
commit 458fa1ca58

@ -175,7 +175,7 @@ impl TreeItem for FileInfo {
Ok(ret)
}
fn text_string(&self) -> String {
fn name(&self) -> String {
self.get_text().to_string()
}
@ -220,11 +220,11 @@ impl TreeItem for FileInfo {
// }
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
enum PromptAction {
Search(bool), // search next/search pre
Mkdir,
CreateFile,
CreateFolder { folder_path: PathBuf },
CreateFile { folder_path: PathBuf },
RemoveDir,
RemoveFile,
Filter,
@ -258,10 +258,8 @@ pub struct Explorer {
impl Explorer {
pub fn new(cx: &mut Context) -> Result<Self> {
let current_root = std::env::current_dir().unwrap_or_else(|_| "./".into());
let root = FileInfo::root(current_root.clone());
let children = root.get_children()?;
Ok(Self {
tree: TreeView::build_tree(root, children).with_enter_fn(Self::toggle_current),
tree: Self::new_tree(current_root.clone())?,
state: State::new(true, current_root),
repeat_motion: None,
prompt: None,
@ -269,6 +267,22 @@ impl Explorer {
})
}
fn new_tree(root: PathBuf) -> Result<TreeView<FileInfo>> {
let root = FileInfo::root(root.clone());
let children = root.get_children()?;
Ok(TreeView::build_tree(root, children).with_enter_fn(Self::toggle_current))
}
fn change_root(&mut self, cx: &mut Context, root: PathBuf) {
match Self::new_tree(root.clone()) {
Ok(tree) => {
self.state.current_root = root;
self.tree = tree;
}
Err(e) => cx.editor.set_error(format!("{e}")),
}
}
pub fn reveal_current_file(&mut self, cx: &mut Context) {
let current_document_path = doc!(cx.editor).path().cloned();
match current_document_path {
@ -381,23 +395,50 @@ impl Explorer {
))
}
fn new_mkdir_prompt(&mut self) {
fn new_create_folder_prompt(&mut self) -> Result<()> {
let folder_path = self.current_parent_folder_path()?;
self.prompt = Some((
PromptAction::Mkdir,
Prompt::new("mkdir: ".into(), None, ui::completers::none, |_, _, _| {}),
PromptAction::CreateFolder {
folder_path: folder_path.clone(),
},
Prompt::new(
format!(" New folder: {}/", folder_path.to_string_lossy()).into(),
None,
ui::completers::none,
|_, _, _| {},
),
));
Ok(())
}
fn new_create_file_prompt(&mut self) {
fn new_create_file_prompt(&mut self) -> Result<()> {
let folder_path = self.current_parent_folder_path()?;
self.prompt = Some((
PromptAction::CreateFile,
PromptAction::CreateFile {
folder_path: folder_path.clone(),
},
Prompt::new(
"create file: ".into(),
format!(" New file: {}/", folder_path.to_string_lossy()).into(),
None,
ui::completers::none,
|_, _, _| {},
),
));
Ok(())
}
fn current_parent_folder_path(&self) -> Result<PathBuf> {
let current_item = self.tree.current_item();
Ok(current_item
.path
.parent()
.ok_or_else(|| {
anyhow::anyhow!(format!(
"Unable to get parent directory of '{}'",
current_item.path.to_string_lossy()
))
})?
.to_path_buf())
}
fn new_remove_file_prompt(&mut self, cx: &mut Context) {
@ -696,14 +737,14 @@ impl Explorer {
_ => return EventResult::Ignored(None),
};
let line = prompt.line();
match (action, event.into()) {
(PromptAction::Mkdir, key!(Enter)) => {
if let Err(e) = self.new_path(line, true) {
match (&action, event.into()) {
(PromptAction::CreateFolder { folder_path }, key!(Enter)) => {
if let Err(e) = self.new_path(folder_path.clone(), line, true) {
cx.editor.set_error(format!("{e}"))
}
}
(PromptAction::CreateFile, key!(Enter)) => {
if let Err(e) = self.new_path(line, false) {
(PromptAction::CreateFile { folder_path }, key!(Enter)) => {
if let Err(e) = self.new_path(folder_path.clone(), line, false) {
cx.editor.set_error(format!("{e}"))
}
}
@ -735,23 +776,15 @@ impl Explorer {
EventResult::Consumed(None)
}
fn new_path(&mut self, file_name: &str, is_dir: bool) -> Result<()> {
fn new_path(&mut self, current_parent: PathBuf, file_name: &str, is_dir: bool) -> Result<()> {
let current = self.tree.current_item();
let current_parent = if current.file_type == FileType::Placeholder {
&current.path
} else {
current
.path
.parent()
.ok_or_else(|| anyhow::anyhow!("can not get parent dir"))?
};
let p = helix_core::path::get_normalized_path(&current_parent.join(file_name));
match p.parent() {
Some(p) if p == current_parent => {}
_ => bail!("The file name is not illegal"),
};
let f = if is_dir {
let file = if is_dir {
std::fs::create_dir(&p)?;
FileInfo::new(p, FileType::Dir)
} else {
@ -760,9 +793,9 @@ impl Explorer {
FileInfo::new(p, FileType::File)
};
if current.file_type == FileType::Placeholder {
self.tree.replace_current(f);
self.tree.replace_current(file);
} else {
self.tree.insert_current_level(f);
self.tree.add_sibling_to_current_item(file)?;
}
Ok(())
}
@ -808,33 +841,25 @@ impl Component for Explorer {
self.repeat_motion = Some(repeat_motion);
}
}
// key!('b') => {
// if let Some(p) = self.state.current_root.parent() {
// match Self::get_items(p.to_path_buf(), cx) {
// Ok(items) => {
// self.state.current_root = p.to_path_buf();
// let root = FileInfo::root(self.state.current_root.clone());
// let children = root.get_children().expect("TODO: handle error");
// self.tree = TreeView::build_tree(root, children)
// .with_enter_fn(Self::toggle_current);
// }
// Err(e) => cx.editor.set_error(format!("{e}")),
// }
// }
// }
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),
key!('m') => {
self.on_next_key = Some(Box::new(|_, explorer, event| {
match event.into() {
key!('d') => explorer.new_mkdir_prompt(),
key!('f') => explorer.new_create_file_prompt(),
_ => return EventResult::Ignored(None),
};
EventResult::Consumed(None)
}));
key!('a') => {
if let Err(error) = self.new_create_file_prompt() {
cx.editor.set_error(error.to_string())
}
}
shift!('A') => {
if let Err(error) = self.new_create_folder_prompt() {
cx.editor.set_error(error.to_string())
}
}
key!('o') => self.change_root(cx, self.tree.current_item().path.clone()),
key!('r') => {
self.on_next_key = Some(Box::new(|cx, explorer, event| {
match event.into() {

@ -18,15 +18,13 @@ pub trait TreeItem: Sized {
type Params;
// fn text(&self, cx: &mut Context, selected: bool, params: &mut Self::Params) -> Spans;
fn text_string(&self) -> String;
fn name(&self) -> String;
fn is_child(&self, other: &Self) -> bool;
fn is_parent(&self) -> bool;
fn cmp(&self, other: &Self) -> Ordering;
fn filter(&self, s: &str) -> bool {
self.text_string()
.to_lowercase()
.contains(&s.to_lowercase())
self.name().to_lowercase().contains(&s.to_lowercase())
}
fn get_children(&self) -> Result<Vec<Self>> {
@ -357,7 +355,7 @@ impl<T: TreeItem> TreeView<T> {
match current_tree
.children
.iter_mut()
.find(|tree| tree.item.text_string().eq(segment))
.find(|tree| tree.item.name().eq(segment))
{
Some(tree) => {
if !tree.is_opened {
@ -387,7 +385,7 @@ impl<T: TreeItem> TreeView<T> {
.fold(&self.tree, |tree, segment| {
tree.children
.iter()
.find(|tree| tree.item.text_string().eq(segment))
.find(|tree| tree.item.name().eq(segment))
.expect("Should be unreachable")
})
.index;
@ -656,13 +654,34 @@ impl<T: TreeItem> TreeView<T> {
self.selected = selected
}
pub fn insert_current_level(&mut self, item: T) {
let current = self.current_mut();
current.children.push(Tree::new(item, vec![]));
current
.children
.sort_by(|a, b| tree_item_cmp(&a.item, &b.item));
self.regenerate_index()
pub fn add_sibling_to_current_item(&mut self, item: T) -> Result<()> {
let current = self.current();
match current.parent_index {
None => Err(anyhow::anyhow!(format!(
"Current item = '{}' has no parent",
current.item.name()
))),
Some(parent_index) => {
let parent = self.get_mut(parent_index);
let item_name = item.name();
parent.children.push(Tree::new(item, vec![]));
parent
.children
.sort_by(|a, b| tree_item_cmp(&a.item, &b.item));
self.regenerate_index();
let parent = self.get_mut(parent_index);
// Focus the added sibling
if let Some(tree) = parent
.children
.iter()
.find(|tree| tree.item.name().eq(&item_name))
{
self.selected = tree.index
};
Ok(())
}
}
}
}
@ -738,7 +757,7 @@ impl<T: TreeItem> TreeView<T> {
selected: selected == tree.index,
name: format!(
"{}{}",
tree.item.text_string(),
tree.item.name(),
if tree.item.is_parent() {
format!("{}", std::path::MAIN_SEPARATOR)
} else {
@ -910,9 +929,7 @@ impl<T: TreeItem + Clone> TreeView<T> {
}
let new_tree = Tree::filter(&self.tree, &|item: &T| {
item.text_string()
.to_lowercase()
.contains(&s.to_lowercase())
item.name().to_lowercase().contains(&s.to_lowercase())
})
.unwrap_or_else(|| Tree {
item: self.tree.item.clone(),

Loading…
Cancel
Save