feat(explore): reveal current file

pull/9/head
wongjiahau 2 years ago
parent d04a1ce214
commit aa397ef801

@ -434,7 +434,7 @@ impl MappableCommand {
replay_macro, "Replay macro", replay_macro, "Replay macro",
command_palette, "Open command pallete", command_palette, "Open command pallete",
toggle_or_focus_explorer, "Toggle or focus explorer", toggle_or_focus_explorer, "Toggle or focus explorer",
focus_current_file, "Focus current file in explorer", reveal_current_file, "Reveal current file in explorer",
close_explorer, "close explorer", close_explorer, "close explorer",
); );
} }
@ -2232,16 +2232,16 @@ fn toggle_or_focus_explorer(cx: &mut Context) {
)); ));
} }
fn focus_current_file(cx: &mut Context) { fn reveal_current_file(cx: &mut Context) {
cx.callback = Some(Box::new( cx.callback = Some(Box::new(
|compositor: &mut Compositor, cx: &mut compositor::Context| { |compositor: &mut Compositor, cx: &mut compositor::Context| {
if let Some(editor) = compositor.find::<ui::EditorView>() { if let Some(editor) = compositor.find::<ui::EditorView>() {
match editor.explorer.as_mut() { match editor.explorer.as_mut() {
Some(explore) => explore.content.focus_current_file(cx), Some(explore) => explore.content.reveal_current_file(cx),
None => match ui::Explorer::new(cx) { None => match ui::Explorer::new(cx) {
Ok(explore) => { Ok(explore) => {
let mut explorer = overlayed(explore); let mut explorer = overlayed(explore);
explorer.content.focus_current_file(cx); explorer.content.reveal_current_file(cx);
editor.explorer = Some(explorer); editor.explorer = Some(explorer);
} }
Err(err) => cx.editor.set_error(format!("{}", err)), Err(err) => cx.editor.set_error(format!("{}", err)),

@ -265,7 +265,7 @@ pub fn default() -> HashMap<Mode, Keymap> {
"h" => select_references_to_symbol_under_cursor, "h" => select_references_to_symbol_under_cursor,
"?" => command_palette, "?" => command_palette,
"e" => toggle_or_focus_explorer, "e" => toggle_or_focus_explorer,
"E" => focus_current_file, "E" => reveal_current_file,
}, },
"z" => { "View" "z" => { "View"
"z" | "c" => align_view_center, "z" | "c" => align_view_center,

@ -277,14 +277,32 @@ impl Explorer {
}) })
} }
pub fn focus_current_file(&mut self, cx: &mut Context) { pub fn reveal_current_file(&mut self, cx: &mut Context) {
let current_document_path = doc!(cx.editor).path().cloned(); let current_document_path = doc!(cx.editor).path().cloned();
match current_document_path { match current_document_path {
None => cx.editor.set_error("No opened document."), None => cx.editor.set_error("No opened document."),
Some(path) => { Some(current_path) => {
self.tree.focus_path(cx, path, &self.state.current_root); 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::<Vec<_>>();
match self.tree.reveal_item(segments) {
Ok(_) => {
self.focus(); self.focus();
} }
Err(error) => cx.editor.set_error(error),
}
}
} }
} }
@ -798,20 +816,20 @@ impl Component for Explorer {
self.repeat_motion = Some(repeat_motion); self.repeat_motion = Some(repeat_motion);
} }
} }
key!('b') => { // key!('b') => {
if let Some(p) = self.state.current_root.parent() { // if let Some(p) = self.state.current_root.parent() {
match Self::get_items(p.to_path_buf(), cx) { // match Self::get_items(p.to_path_buf(), cx) {
Ok(items) => { // Ok(items) => {
self.state.current_root = p.to_path_buf(); // self.state.current_root = p.to_path_buf();
let root = FileInfo::root(self.state.current_root.clone()); // let root = FileInfo::root(self.state.current_root.clone());
let children = root.get_children().expect("TODO: handle error"); // let children = root.get_children().expect("TODO: handle error");
self.tree = TreeView::build_tree(root, children) // self.tree = TreeView::build_tree(root, children)
.with_enter_fn(Self::toggle_current); // .with_enter_fn(Self::toggle_current);
} // }
Err(e) => cx.editor.set_error(format!("{e}")), // Err(e) => cx.editor.set_error(format!("{e}")),
} // }
} // }
} // }
key!('f') => self.new_filter_prompt(), key!('f') => self.new_filter_prompt(),
key!('/') => self.new_search_prompt(true), key!('/') => self.new_search_prompt(true),
key!('?') => self.new_search_prompt(false), key!('?') => self.new_search_prompt(false),

@ -1,5 +1,3 @@
use std::iter::Peekable;
use std::slice::Iter;
use std::{cmp::Ordering, path::PathBuf}; use std::{cmp::Ordering, path::PathBuf};
use anyhow::Result; use anyhow::Result;
@ -210,9 +208,16 @@ impl<T> Tree<T> {
pub struct TreeView<T: TreeItem> { pub struct TreeView<T: TreeItem> {
tree: Tree<T>, tree: Tree<T>,
recycle: Option<(String, Vec<Tree<T>>)>, recycle: Option<(String, Vec<Tree<T>>)>,
selected: usize, // select item index /// Selected item idex
save_view: (usize, usize), // (selected, row) selected: usize,
winline: usize, // view row
/// (selected, row)
save_view: (usize, usize),
/// View row
winline: usize,
area_height: usize,
col: usize, col: usize,
max_len: usize, max_len: usize,
count: usize, count: usize,
@ -239,6 +244,7 @@ impl<T: TreeItem> TreeView<T> {
col: 0, col: 0,
max_len: 0, max_len: 0,
count: 0, count: 0,
area_height: 0,
tree_symbol_style: "ui.text".into(), tree_symbol_style: "ui.text".into(),
pre_render: None, pre_render: None,
on_opened_fn: None, on_opened_fn: None,
@ -292,66 +298,73 @@ impl<T: TreeItem> TreeView<T> {
} }
} }
/// TODO: current_path should not be PathBuf, but Vec<String> so that Tree can be generic /// Reveal item in the tree based on the given `segments`.
pub fn focus_path(&mut self, cx: &mut Context, current_path: PathBuf, current_root: &PathBuf) { ///
let current_path = current_path.as_path().to_string_lossy().to_string(); /// The name of the root should be excluded.
let current_root = current_root.as_path().to_string_lossy().to_string() + "/"; ///
let nodes = current_path /// Example `segments`:
.strip_prefix(current_root.as_str()) /// ```
.expect( /// vec!["helix-term", "src", "ui", "tree.rs"]
format!( /// ```
"Failed to strip prefix '{}' from '{}'", pub fn reveal_item(&mut self, segments: Vec<&str>) -> Result<(), String> {
current_root, current_path // Expand the tree
) segments.iter().fold(
.as_str(), Ok(&mut self.tree),
) |current_tree, segment| match current_tree {
.split(std::path::MAIN_SEPARATOR) Err(err) => Err(err),
.enumerate() Ok(current_tree) => {
.collect::<Vec<(usize, &str)>>(); match current_tree
.children
let len = nodes.len(); .iter_mut()
.find(|tree| tree.item.text_string().eq(segment))
// `preivous_item_index` is necessary to avoid choosing the first file {
// that is not the current file. Some(tree) => {
// For example, consider a project that contains multiple `Cargo.toml`. if !tree.is_opened {
// Without `previous_item_index`, the first `Cargo.toml` will always be chosen, tree.children = vec_to_tree(
// regardless of which `Cargo.toml` the user wishes to find in the explorer. tree.item.get_children().map_err(|err| err.to_string())?,
// let mut previous_item_index = 0; );
// for (index, node) in nodes { if !tree.children.is_empty() {
// let current_level = index + 1; tree.is_opened = true;
// let is_last = index == len - 1; }
// match self }
// .items Ok(tree)
// .iter() }
// .enumerate() None => Err(format!(
// .position(|(item_index, item)| { "Unable to find path: '{}'. current_segment = {}",
// item_index >= previous_item_index segments.join("/"),
// && item.item.text_string().eq(node) segment
// && item.level == current_level )),
// }) { }
// Some(index) => { }
// if is_last { },
// self.selected = index )?;
// } else {
// let item = &self.items[index]; // Locate the item
// let items = match item.item.get_childs() { self.regenerate_index();
// Ok(items) => items, self.selected = segments
// Err(e) => return cx.editor.set_error(format!("{e}")), .iter()
// }; .fold(&self.tree, |tree, segment| {
// let inserts = vec_to_tree(items, current_level + 1); tree.children
// previous_item_index = index; .iter()
// let _: Vec<_> = self.items.splice(index + 1..index + 1, inserts).collect(); .find(|tree| tree.item.text_string().eq(segment))
// } .expect("Should be unreachable")
// } })
// None => cx.editor.set_error(format!( .index;
// "The following file does not exist anymore: '{}'. node = {}",
// current_path, node
// )),
// }
// }
// Center the selection self.align_view_center();
self.winline = self.max_len / 2; Ok(())
}
fn align_view_center(&mut self) {
self.winline = self.area_height / 2
}
fn align_view_top(&mut self) {
self.winline = 0
}
fn align_view_bottom(&mut self) {
self.winline = self.area_height
} }
fn regenerate_index(&mut self) { fn regenerate_index(&mut self) {
@ -626,11 +639,14 @@ impl<T: TreeItem> TreeView<T> {
} }
self.max_len = 0; self.max_len = 0;
self.winline = std::cmp::min(self.winline, area.height.saturating_sub(1) as usize); self.area_height = area.height.saturating_sub(1) as usize;
self.winline = std::cmp::min(self.winline, self.area_height);
let style = cx.editor.theme.get(&self.tree_symbol_style); let style = cx.editor.theme.get(&self.tree_symbol_style);
let last_item_index = self.tree.len().saturating_sub(1); let last_item_index = self.tree.len().saturating_sub(1);
let skip = self.selected.saturating_sub(self.winline); let skip = self.selected.saturating_sub(self.winline);
cx.editor.set_error(format!("winline = {}", self.winline));
let params = RenderElemParams { let params = RenderElemParams {
tree: &self.tree, tree: &self.tree,
prefix: &"".to_string(), prefix: &"".to_string(),
@ -661,8 +677,6 @@ impl<T: TreeItem> TreeView<T> {
selected: usize, selected: usize,
} }
cx.editor.set_error(format!("seleted = {}", self.selected));
fn render_tree<T: TreeItem>( fn render_tree<T: TreeItem>(
RenderElemParams { RenderElemParams {
tree, tree,
@ -822,7 +836,15 @@ impl<T: TreeItem> TreeView<T> {
key!(i @ '0'..='9') => self.count = i.to_digit(10).unwrap() as usize + count * 10, key!(i @ '0'..='9') => self.count = i.to_digit(10).unwrap() as usize + count * 10,
key!('k') | shift!(Tab) | key!(Up) | ctrl!('k') => self.move_up(1.max(count)), key!('k') | shift!(Tab) | key!(Up) | ctrl!('k') => self.move_up(1.max(count)),
key!('j') | key!(Tab) | key!(Down) | ctrl!('j') => self.move_down(1.max(count)), key!('j') | key!(Tab) | key!(Down) | ctrl!('j') => self.move_down(1.max(count)),
key!('z') => self.fold_current_child(), key!('z') => {
self.on_next_key = Some(Box::new(|_, tree, event| match event.into() {
key!('f') => tree.fold_current_child(),
key!('z') => tree.align_view_center(),
key!('t') => tree.align_view_top(),
key!('b') => tree.align_view_bottom(),
_ => {}
}));
}
key!('h') => self.go_to_parent(), key!('h') => self.go_to_parent(),
key!('l') => self.go_to_children(cx), key!('l') => self.go_to_children(cx),
key!(Enter) => self.on_enter(cx, params, self.selected), key!(Enter) => self.on_enter(cx, params, self.selected),

Loading…
Cancel
Save