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},
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<DocumentId>),
RenameFile(Option<DocumentId>),
Filter,
}
@ -232,7 +232,7 @@ impl Explorer {
)
.split(std::path::MAIN_SEPARATOR)
.collect::<Vec<_>>();
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);
}
}

@ -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<T: Clone + TreeItem> Tree<T> {
}
impl<T: TreeItem> Tree<T> {
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<T: TreeItem> Tree<T> {
.any(|child| tree.item.name().eq(&child.item.name()))
})
.map(|mut tree| {
tree.refresh()?;
tree.refresh(filter)?;
Ok(tree)
})
.collect::<Result<Vec<_>>>()?;
@ -249,12 +261,47 @@ impl<T> Tree<T> {
&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 {
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<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>> {
@ -384,8 +431,8 @@ impl<T: TreeItem> TreeView<T> {
/// ```
/// 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<T: TreeItem> TreeView<T> {
{
Some(tree) => {
if !tree.is_opened {
tree.open()?;
tree.open(filter)?;
}
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();
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<T: TreeItem> TreeView<T> {
}
}
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<String> {
}
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) {
// let current = self.find_by_index(selected_index);
// 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() {
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<T: TreeItem> TreeView<T> {
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<T: TreeItem> TreeView<T> {
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<T: TreeItem> TreeView<T> {
}
}
impl<T: TreeItem> TreeView<T> {
pub fn render(
&mut self,
area: Rect,
surface: &mut Surface,
cx: &mut Context,
params: &mut T::Params,
) {
impl<T: TreeItem + Clone> TreeView<T> {
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<T: TreeItem> TreeView<T> {
is_last: true,
level: 0,
selected: self.selected,
filter,
};
let rendered = render_tree(params);
@ -755,6 +800,7 @@ impl<T: TreeItem> TreeView<T> {
is_last: bool,
level: u16,
selected: usize,
filter: &'a str,
}
fn render_tree<T: TreeItem>(
@ -764,6 +810,7 @@ impl<T: TreeItem> TreeView<T> {
is_last,
level,
selected,
filter,
}: RenderElemParams<T>,
) -> Vec<(Indent, Node)> {
let indent = if level > 0 {
@ -805,6 +852,7 @@ impl<T: TreeItem> TreeView<T> {
is_last,
level: level + 1,
selected,
filter,
})
}),
)
@ -843,6 +891,7 @@ impl<T: TreeItem> TreeView<T> {
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<T: TreeItem> TreeView<T> {
}));
}
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![

Loading…
Cancel
Save