feat(explore):

- filter
- close document if the file is deleted or renamed
pull/9/head
wongjiahau 1 year ago
parent a079477a23
commit 85fa1c56b7

@ -9,7 +9,7 @@ use helix_view::{
editor::{Action, ExplorerPositionEmbed}, editor::{Action, ExplorerPositionEmbed},
graphics::{CursorKind, Rect}, graphics::{CursorKind, Rect},
input::{Event, KeyEvent}, input::{Event, KeyEvent},
Editor, DocumentId, Editor,
}; };
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -137,8 +137,8 @@ enum PromptAction {
parent_index: usize, parent_index: usize,
}, },
RemoveDir, RemoveDir,
RemoveFile, RemoveFile(Option<DocumentId>),
RenameFile, RenameFile(Option<DocumentId>),
Filter, Filter,
} }
@ -232,7 +232,7 @@ impl Explorer {
) )
.split(std::path::MAIN_SEPARATOR) .split(std::path::MAIN_SEPARATOR)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.tree.reveal_item(segments)?; self.tree.reveal_item(segments, &self.state.filter)?;
self.focus(); self.focus();
Ok(()) Ok(())
} }
@ -332,7 +332,8 @@ impl Explorer {
self.tree.save_view(); self.tree.save_view();
self.prompt = Some(( self.prompt = Some((
PromptAction::Filter, 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) { fn new_rename_prompt(&mut self, cx: &mut Context) {
let name = self.tree.current_item().path.to_string_lossy(); let path = self.tree.current_item().path.clone();
self.prompt = Some(( self.prompt = Some((
PromptAction::RenameFile, PromptAction::RenameFile(cx.editor.document_by_path(&path).map(|doc| doc.id())),
Prompt::new( Prompt::new(
format!(" Rename to ").into(), format!(" Rename to ").into(),
None, None,
ui::completers::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 = || { let check = || {
ensure!(item.path.is_file(), "The path is not a file"); ensure!(item.path.is_file(), "The path is not a file");
let doc = cx.editor.document_by_path(&item.path); let doc = cx.editor.document_by_path(&item.path);
ensure!(doc.is_none(), "The file is opened"); Ok(doc.map(|doc| doc.id()))
Ok(())
}; };
if let Err(e) = check() { match check() {
cx.editor.set_error(format!("{e}")); Err(err) => cx.editor.set_error(format!("{err}")),
return; 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) { fn new_remove_dir_prompt(&mut self, cx: &mut Context) {
@ -506,7 +507,7 @@ impl Explorer {
cx.editor.theme.get("ui.text"), cx.editor.theme.get("ui.text"),
); );
self.tree 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( pub fn render_embed(
@ -553,7 +554,7 @@ impl Explorer {
cx.editor.theme.get("ui.text"), cx.editor.theme.get("ui.text"),
); );
self.tree 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() { let statusline = if self.is_focus() {
@ -618,9 +619,10 @@ impl Explorer {
match event.into() { match event.into() {
key!(Tab) | key!(Down) | ctrl!('j') => { key!(Tab) | key!(Down) | ctrl!('j') => {
self.tree.clean_recycle(); self.tree.clean_recycle();
let filter = self.state.filter.clone();
return self return self
.tree .tree
.handle_event(Event::Key(event), cx, &mut self.state); .handle_event(Event::Key(event), cx, &mut self.state, &filter);
} }
key!(Enter) => { key!(Enter) => {
if let EventResult::Consumed(_) = prompt.handle_event(Event::Key(event), cx) { 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) { if let EventResult::Consumed(_) = prompt.handle_event(Event::Key(event), cx) {
self.tree.filter(prompt.line()); self.tree.filter(prompt.line());
} }
self.state.filter = prompt.line().clone();
self.prompt = Some((action, prompt)); self.prompt = Some((action, prompt));
} }
}; };
@ -646,9 +649,10 @@ impl Explorer {
}; };
match event.into() { match event.into() {
key!(Tab) | key!(Down) | ctrl!('j') => { key!(Tab) | key!(Down) | ctrl!('j') => {
let filter = self.state.filter.clone();
return self return self
.tree .tree
.handle_event(Event::Key(event), cx, &mut self.state) .handle_event(Event::Key(event), cx, &mut self.state, &filter);
} }
key!(Enter) => { key!(Enter) => {
let search_str = prompt.line().clone(); let search_str = prompt.line().clone();
@ -727,18 +731,24 @@ impl Explorer {
explorer.tree.remove_current(); explorer.tree.remove_current();
} }
} }
(PromptAction::RemoveFile, key!(Enter)) => { (PromptAction::RemoveFile(document_id), key!(Enter)) => {
if line == "y" { if line == "y" {
let item = explorer.tree.current_item(); let item = explorer.tree.current_item();
std::fs::remove_file(&item.path).map_err(anyhow::Error::from)?; std::fs::remove_file(&item.path).map_err(anyhow::Error::from)?;
explorer.tree.remove_current(); 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(); let item = explorer.tree.current_item();
std::fs::rename(&item.path, line)?; std::fs::rename(&item.path, line)?;
explorer.tree.remove_current(); explorer.tree.remove_current();
explorer.reveal_file(PathBuf::from(line))?; explorer.reveal_file(PathBuf::from(line))?;
if let Some(id) = document_id {
cx.editor.close_document(*id, true)?
}
} }
(_, key!(Esc) | ctrl!('c')) => {} (_, key!(Esc) | ctrl!('c')) => {}
_ => { _ => {
@ -778,7 +788,8 @@ impl Explorer {
fd.create_new(true).write(true).open(&path)?; fd.create_new(true).write(true).open(&path)?;
FileInfo::new(path, FileType::File) FileInfo::new(path, FileType::File)
}; };
self.tree.add_child(parent_index, file)?; self.tree
.add_child(parent_index, file, &self.state.filter)?;
Ok(()) Ok(())
} }
@ -868,17 +879,18 @@ impl Component for Explorer {
key!(']') => self.change_root(cx, self.tree.current_item().path.clone()), key!(']') => self.change_root(cx, self.tree.current_item().path.clone()),
ctrl!('o') => self.go_to_previous_root(), ctrl!('o') => self.go_to_previous_root(),
key!('d') => self.new_remove_prompt(cx), key!('d') => self.new_remove_prompt(cx),
key!('r') => self.new_rename_prompt(), key!('r') => self.new_rename_prompt(cx),
shift!('R') => { 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()) cx.editor.set_error(error.to_string())
} }
} }
key!('-') => self.decrease_size(), key!('-') => self.decrease_size(),
key!('+') => self.increase_size(), key!('+') => self.increase_size(),
_ => { _ => {
let filter = self.state.filter.clone();
self.tree self.tree
.handle_event(Event::Key(key_event), cx, &mut self.state); .handle_event(Event::Key(key_event), cx, &mut self.state, &filter);
} }
} }

@ -1,4 +1,4 @@
use std::cmp::Ordering; use std::{borrow::Cow, cmp::Ordering};
use anyhow::Result; use anyhow::Result;
use helix_view::theme::Modifier; use helix_view::theme::Modifier;
@ -156,19 +156,31 @@ impl<T: Clone + TreeItem> Tree<T> {
} }
impl<T: TreeItem> Tree<T> { impl<T: TreeItem> Tree<T> {
fn open(&mut self) -> Result<()> { fn open(&mut self, filter: &String) -> Result<()> {
self.children = vec_to_tree(self.item.get_children()?); if self.item.is_parent() {
if !self.children.is_empty() { 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; self.is_opened = true;
} }
Ok(()) Ok(())
} }
fn refresh(&mut self) -> Result<()> { fn refresh(&mut self, filter: &String) -> Result<()> {
if !self.is_opened { if !self.is_opened {
return Ok(()); 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![]) let filtered = std::mem::replace(&mut self.children, vec![])
.into_iter() .into_iter()
// Remove children that does not exists in latest_children // Remove children that does not exists in latest_children
@ -178,7 +190,7 @@ impl<T: TreeItem> Tree<T> {
.any(|child| tree.item.name().eq(&child.item.name())) .any(|child| tree.item.name().eq(&child.item.name()))
}) })
.map(|mut tree| { .map(|mut tree| {
tree.refresh()?; tree.refresh(filter)?;
Ok(tree) Ok(tree)
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
@ -249,12 +261,47 @@ impl<T> Tree<T> {
&self.item &self.item
} }
fn get(&self, index: usize) -> Option<&Tree<T>> { fn get<'a>(&'a self, index: usize) -> Option<&'a Tree<T>> {
if self.index == index { if self.index == index {
Some(self) Some(self)
} else { } else {
self.children.iter().find_map(|elem| elem.get(index)) 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<T>) -> U,
{
fn traverse<'a, T, U, F>(
tree: &'a Tree<T>,
current_index: usize,
init: U,
f: &F,
) -> (usize, U)
where
F: Fn(U, usize, &'a Tree<T>) -> 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<T>> { fn get_mut(&mut self, index: usize) -> Option<&mut Tree<T>> {
@ -384,8 +431,8 @@ impl<T: TreeItem> TreeView<T> {
/// ``` /// ```
/// vec!["helix-term", "src", "ui", "tree.rs"] /// vec!["helix-term", "src", "ui", "tree.rs"]
/// ``` /// ```
pub fn reveal_item(&mut self, segments: Vec<&str>) -> Result<()> { pub fn reveal_item(&mut self, segments: Vec<&str>, filter: &String) -> Result<()> {
self.tree.refresh()?; self.tree.refresh(filter)?;
// Expand the tree // Expand the tree
segments.iter().fold( segments.iter().fold(
@ -400,7 +447,7 @@ impl<T: TreeItem> TreeView<T> {
{ {
Some(tree) => { Some(tree) => {
if !tree.is_opened { if !tree.is_opened {
tree.open()?; tree.open(filter)?;
} }
Ok(tree) Ok(tree)
} }
@ -452,13 +499,13 @@ impl<T: TreeItem> TreeView<T> {
} }
} }
fn go_to_children(&mut self) -> Result<()> { fn go_to_children(&mut self, filter: &String) -> Result<()> {
let current = self.current_mut(); let current = self.current_mut();
if current.is_opened { if current.is_opened {
self.selected += 1; self.selected += 1;
Ok(()) Ok(())
} else { } else {
current.open()?; current.open(filter)?;
if !current.children.is_empty() { if !current.children.is_empty() {
self.selected += 1; self.selected += 1;
self.regenerate_index(); self.regenerate_index();
@ -467,8 +514,8 @@ impl<T: TreeItem> TreeView<T> {
} }
} }
pub fn refresh(&mut self) -> Result<()> { pub fn refresh(&mut self, filter: &String) -> Result<()> {
self.tree.refresh() self.tree.refresh(filter)
} }
} }
@ -492,7 +539,13 @@ pub fn tree_view_help() -> Vec<String> {
} }
impl<T: TreeItem> TreeView<T> { impl<T: TreeItem> TreeView<T> {
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) { // if let Some(next_level) = self.next_item().map(|elem| elem.level) {
// let current = self.find_by_index(selected_index); // let current = self.find_by_index(selected_index);
// let current_level = current.level; // let current_level = current.level;
@ -516,15 +569,12 @@ impl<T: TreeItem> TreeView<T> {
if let Some(mut on_open_fn) = self.on_opened_fn.take() { if let Some(mut on_open_fn) = self.on_opened_fn.take() {
let mut f = || { 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) { match on_open_fn(&mut current.item, cx, params) {
TreeOp::GetChildsAndInsert => { TreeOp::GetChildsAndInsert => {
let items = match current.item.get_children() { if let Err(err) = current.open(filter) {
Ok(items) => items, cx.editor.set_error(format!("{err}"))
Err(e) => return cx.editor.set_error(format!("{e}")), }
};
current.is_opened = true;
current.children = vec_to_tree(items);
} }
TreeOp::Noop => {} TreeOp::Noop => {}
}; };
@ -675,7 +725,7 @@ impl<T: TreeItem> TreeView<T> {
self.selected = selected 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) { match self.tree.get_mut(index) {
None => Err(anyhow::anyhow!(format!( None => Err(anyhow::anyhow!(format!(
"No item found at index = {}", "No item found at index = {}",
@ -684,7 +734,7 @@ impl<T: TreeItem> TreeView<T> {
Some(tree) => { Some(tree) => {
let item_name = item.name(); let item_name = item.name();
if !tree.is_opened { if !tree.is_opened {
tree.open()?; tree.open(filter)?;
} }
tree.children.push(Tree::new(item, vec![])); tree.children.push(Tree::new(item, vec![]));
tree.children tree.children
@ -707,14 +757,8 @@ impl<T: TreeItem> TreeView<T> {
} }
} }
impl<T: TreeItem> TreeView<T> { impl<T: TreeItem + Clone> TreeView<T> {
pub fn render( pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context, filter: &String) {
&mut self,
area: Rect,
surface: &mut Surface,
cx: &mut Context,
params: &mut T::Params,
) {
if let Some(pre_render) = self.pre_render.take() { if let Some(pre_render) = self.pre_render.take() {
pre_render(self, area); pre_render(self, area);
} }
@ -732,6 +776,7 @@ impl<T: TreeItem> TreeView<T> {
is_last: true, is_last: true,
level: 0, level: 0,
selected: self.selected, selected: self.selected,
filter,
}; };
let rendered = render_tree(params); let rendered = render_tree(params);
@ -755,6 +800,7 @@ impl<T: TreeItem> TreeView<T> {
is_last: bool, is_last: bool,
level: u16, level: u16,
selected: usize, selected: usize,
filter: &'a str,
} }
fn render_tree<T: TreeItem>( fn render_tree<T: TreeItem>(
@ -764,6 +810,7 @@ impl<T: TreeItem> TreeView<T> {
is_last, is_last,
level, level,
selected, selected,
filter,
}: RenderElemParams<T>, }: RenderElemParams<T>,
) -> Vec<(Indent, Node)> { ) -> Vec<(Indent, Node)> {
let indent = if level > 0 { let indent = if level > 0 {
@ -805,6 +852,7 @@ impl<T: TreeItem> TreeView<T> {
is_last, is_last,
level: level + 1, level: level + 1,
selected, selected,
filter,
}) })
}), }),
) )
@ -843,6 +891,7 @@ impl<T: TreeItem> TreeView<T> {
event: Event, event: Event,
cx: &mut Context, cx: &mut Context,
params: &mut T::Params, params: &mut T::Params,
filter: &String,
) -> EventResult { ) -> EventResult {
let key_event = match event { let key_event = match event {
Event::Key(event) => event, Event::Key(event) => event,
@ -868,11 +917,11 @@ impl<T: TreeItem> TreeView<T> {
})); }));
} }
key!('h') => self.go_to_parent(), key!('h') => self.go_to_parent(),
key!('l') => match self.go_to_children() { key!('l') => match self.go_to_children(filter) {
Ok(_) => {} Ok(_) => {}
Err(err) => cx.editor.set_error(err.to_string()), 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!('d') => self.move_down_half_page(),
ctrl!('u') => self.move_up_half_page(), ctrl!('u') => self.move_up_half_page(),
key!('g') => { key!('g') => {
@ -976,7 +1025,7 @@ mod test_tree {
use super::Tree; use super::Tree;
#[test] #[test]
fn test_indexs_elems() { fn test_get() {
let result = Tree::new( let result = Tree::new(
"root", "root",
vec![ vec![

Loading…
Cancel
Save