Skip to content

Commit

Permalink
feat(ui/explore): implement "focus current file"
Browse files Browse the repository at this point in the history
  • Loading branch information
wongjiahau committed Feb 1, 2023
1 parent b652f96 commit d9d4daa
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 32 deletions.
19 changes: 13 additions & 6 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,8 @@ impl MappableCommand {
record_macro, "Record macro",
replay_macro, "Replay macro",
command_palette, "Open command pallete",
toggle_or_focus_explorer, "toggle or focus explorer",
open_explorer_recursion, "open explorer recursion",
toggle_or_focus_explorer, "Toggle or focus explorer",
focus_current_file, "Focus current file in explorer",
close_explorer, "close explorer",
);
}
Expand Down Expand Up @@ -2232,13 +2232,20 @@ fn toggle_or_focus_explorer(cx: &mut Context) {
));
}

fn open_explorer_recursion(cx: &mut Context) {
fn focus_current_file(cx: &mut Context) {
cx.callback = Some(Box::new(
|compositor: &mut Compositor, cx: &mut compositor::Context| {
if let Some(editor) = compositor.find::<ui::EditorView>() {
match ui::Explorer::new_explorer_recursion() {
Ok(explore) => editor.explorer = Some(overlayed(explore)),
Err(err) => cx.editor.set_error(format!("{}", err)),
match editor.explorer.as_mut() {
Some(explore) => explore.content.focus_current_file(cx),
None => match ui::Explorer::new(cx) {
Ok(explore) => {
let mut explorer = overlayed(explore);
explorer.content.focus_current_file(cx);
editor.explorer = Some(explorer);
}
Err(err) => cx.editor.set_error(format!("{}", err)),
},
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ pub fn default() -> HashMap<Mode, Keymap> {
"h" => select_references_to_symbol_under_cursor,
"?" => command_palette,
"e" => toggle_or_focus_explorer,
"E" => open_explorer_recursion,
"E" => focus_current_file,
},
"z" => { "View"
"z" | "c" => align_view_center,
Expand Down
31 changes: 15 additions & 16 deletions helix-term/src/ui/explore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ impl FileInfo {

impl TreeItem for FileInfo {
type Params = State;

fn text(&self, cx: &mut Context, selected: bool, state: &mut State) -> Spans {
let text = self.get_text();
let theme = &cx.editor.theme;
Expand Down Expand Up @@ -179,6 +180,10 @@ impl TreeItem for FileInfo {
self.get_text().contains(s)
}
}

fn text_string(&self) -> String {
self.get_text().to_string()
}
}

// #[derive(Default, Debug, Clone)]
Expand Down Expand Up @@ -262,21 +267,15 @@ impl Explorer {
})
}

pub fn new_explorer_recursion() -> Result<Self> {
let current_root = std::env::current_dir().unwrap_or_else(|_| "./".into());
let parent = FileInfo::parent(&current_root);
let root = FileInfo::root(current_root.clone());
let mut tree =
Tree::build_from_root(root, usize::MAX / 2)?.with_enter_fn(Self::toggle_current);
tree.insert_current_level(parent);
Ok(Self {
tree,
state: State::new(true, current_root),
repeat_motion: None,
prompt: None,
on_next_key: None,
})
// let mut root = vec![, FileInfo::root(p)];
pub fn focus_current_file(&mut self, cx: &mut Context) {
let current_document_path = doc!(cx.editor).path().cloned();
match current_document_path {
None => cx.editor.set_error("No opened document."),
Some(path) => {
self.tree.focus_path(cx, path, &self.state.current_root);
self.focus();
}
}
}

// pub fn new_with_uri(uri: String) -> Result<Self> {
Expand All @@ -289,7 +288,7 @@ impl Explorer {
// }

pub fn focus(&mut self) {
self.state.focus = true
self.state.focus = true;
}

pub fn unfocus(&mut self) {
Expand Down
84 changes: 75 additions & 9 deletions helix-term/src/ui/tree.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cmp::Ordering;
use std::iter::Peekable;
use std::{cmp::Ordering, path::PathBuf};

use anyhow::Result;

Expand All @@ -18,6 +18,7 @@ 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 is_child(&self, other: &Self) -> bool;
fn cmp(&self, other: &Self) -> Ordering;

Expand Down Expand Up @@ -253,15 +254,76 @@ impl<T: TreeItem> Tree<T> {
iter.skip(start).position(f).map(|p| p + start)
}
}

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();
let current_root = current_root.as_path().to_string_lossy().to_string() + "/";
let nodes = 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)
.enumerate()
.collect::<Vec<(usize, &str)>>();

let len = nodes.len();

// `preivous_item_index` is necessary to avoid choosing the first file
// that is not the current file.
// For example, consider a project that contains multiple `Cargo.toml`.
// Without `previous_item_index`, the first `Cargo.toml` will always be chosen,
// regardless of which `Cargo.toml` the user wishes to find in the explorer.
let mut previous_item_index = 0;
for (index, node) in nodes {
let current_level = index + 1;
let is_last = index == len - 1;
match self
.items
.iter()
.enumerate()
.position(|(item_index, item)| {
item_index >= previous_item_index
&& item.item.text_string().eq(node)
&& item.level == current_level
}) {
Some(index) => {
if is_last {
self.selected = index
} else {
let item = &self.items[index];
let items = match item.item.get_childs() {
Ok(items) => items,
Err(e) => return cx.editor.set_error(format!("{e}")),
};
let inserts = vec_to_tree(items, current_level + 1);
previous_item_index = index;
let _: Vec<_> = self.items.splice(index + 1..index + 1, inserts).collect();
}
}
None => cx.editor.set_error(format!(
"The following file does not exist anymore: '{}'. node = {}",
current_path, node
)),
}
}

// Center the selection
self.winline = self.max_len / 2;
}
}

impl<T: TreeItem> Tree<T> {
pub fn on_enter(&mut self, cx: &mut Context, params: &mut T::Params) {
pub fn on_enter(&mut self, cx: &mut Context, params: &mut T::Params, selected_index: usize) {
if self.items.is_empty() {
return;
}
if let Some(next_level) = self.next_item().map(|elem| elem.level) {
let current = &mut self.items[self.selected];
let current = &mut self.items[selected_index];
let current_level = current.level;
if next_level > current_level {
if let Some(mut on_folded_fn) = self.on_folded_fn.take() {
Expand All @@ -275,13 +337,13 @@ impl<T: TreeItem> Tree<T> {

if let Some(mut on_open_fn) = self.on_opened_fn.take() {
let mut f = || {
let current = &mut self.items[self.selected];
let current = &mut self.items[selected_index];
let items = match on_open_fn(&mut current.item, cx, params) {
TreeOp::Restore => {
let inserts = std::mem::take(&mut current.folded);
let _: Vec<_> = self
.items
.splice(self.selected + 1..self.selected + 1, inserts)
.splice(selected_index + 1..selected_index + 1, inserts)
.collect();
return;
}
Expand All @@ -297,17 +359,17 @@ impl<T: TreeItem> Tree<T> {
let inserts = vec_to_tree(items, current.level + 1);
let _: Vec<_> = self
.items
.splice(self.selected + 1..self.selected + 1, inserts)
.splice(selected_index + 1..selected_index + 1, inserts)
.collect();
};
f();
self.on_opened_fn = Some(on_open_fn)
} else {
let current = &mut self.items[self.selected];
let current = &mut self.items[selected_index];
let inserts = std::mem::take(&mut current.folded);
let _: Vec<_> = self
.items
.splice(self.selected + 1..self.selected + 1, inserts)
.splice(selected_index + 1..selected_index + 1, inserts)
.collect();
}
}
Expand Down Expand Up @@ -430,6 +492,10 @@ impl<T: TreeItem> Tree<T> {
self.items[self.selected].item = item;
}

pub fn set_selected(&mut self, selected: usize) {
self.selected = selected
}

pub fn insert_current_level(&mut self, item: T) {
let current = self.current();
let level = current.level;
Expand Down Expand Up @@ -567,7 +633,7 @@ impl<T: TreeItem> Tree<T> {
key!('h') => self.move_left(1.max(count)),
key!('l') => self.move_right(1.max(count)),
shift!('G') => self.move_down(usize::MAX / 2),
key!(Enter) => self.on_enter(cx, params),
key!(Enter) => self.on_enter(cx, params, self.selected),
ctrl!('d') => self.move_down_half_page(),
ctrl!('u') => self.move_up_half_page(),
shift!('D') => self.move_down_page(),
Expand Down

0 comments on commit d9d4daa

Please sign in to comment.