summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorqkzk <qu3nt1n@gmail.com>2023-11-05 02:09:54 +0100
committerqkzk <qu3nt1n@gmail.com>2023-11-05 02:09:54 +0100
commitbd1cb2b3f325ddaa019972762aa32fd4db286eaf (patch)
tree93bc7c0761d88e783974fa5c8dd5c1e3d520befe /src
parentefe46e9abb71c6cfc6a4c56a15920ec73183f0a0 (diff)
doesn't work yet, is way quicker
Diffstat (limited to 'src')
-rw-r--r--src/fileinfo.rs2
-rw-r--r--src/lib.rs1
-rw-r--r--src/status.rs1
-rw-r--r--src/tab.rs16
-rw-r--r--src/term_manager.rs38
-rw-r--r--src/tree.rs2
-rw-r--r--src/trees.rs368
7 files changed, 424 insertions, 4 deletions
diff --git a/src/fileinfo.rs b/src/fileinfo.rs
index f4156c3..335ae9e 100644
--- a/src/fileinfo.rs
+++ b/src/fileinfo.rs
@@ -158,7 +158,7 @@ pub struct FileInfo {
}
impl FileInfo {
- fn new(path: &path::Path, users: &Users) -> Result<Self> {
+ pub fn new(path: &path::Path, users: &Users) -> Result<Self> {
let filename = extract_filename(path)?;
let metadata = symlink_metadata(path)?;
let path = path.to_owned();
diff --git a/src/lib.rs b/src/lib.rs
index 900c090..c1e0b65 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -45,5 +45,6 @@ pub mod tab;
pub mod term_manager;
pub mod trash;
pub mod tree;
+pub mod trees;
pub mod users;
pub mod utils;
diff --git a/src/status.rs b/src/status.rs
index 9aae2de..132ea1a 100644
--- a/src/status.rs
+++ b/src/status.rs
@@ -623,6 +623,7 @@ impl Status {
self.selected().set_mode(Mode::Normal)
} else {
self.display_full = true;
+ // self.selected().make_tree()?;
self.selected().make_tree()?;
self.selected().set_mode(Mode::Tree);
let len = self.selected_non_mut().directory.len();
diff --git a/src/tab.rs b/src/tab.rs
index 801c86e..31d0d6b 100644
--- a/src/tab.rs
+++ b/src/tab.rs
@@ -16,6 +16,7 @@ use crate::opener::execute_in_child;
use crate::preview::{Directory, Preview};
use crate::selectable_content::SelectableContent;
use crate::shortcut::Shortcut;
+use crate::trees::FileSystem;
use crate::users::Users;
use crate::utils::{filename_from_path, row_to_window_index, set_clipboard};
@@ -56,6 +57,7 @@ pub struct Tab {
pub history: History,
/// Users & groups
pub users: Users,
+ pub tree: FileSystem,
}
impl Tab {
@@ -89,6 +91,7 @@ impl Tab {
shortcut.extend_with_mount_points(mount_points);
let searched = None;
let index = path_content.select_file(&path);
+ let tree = FileSystem::empty();
window.scroll_to(index);
Ok(Self {
mode,
@@ -107,6 +110,7 @@ impl Tab {
show_hidden,
history,
users,
+ tree,
})
}
@@ -427,7 +431,15 @@ impl Tab {
pub fn make_tree(&mut self) -> Result<()> {
let path = self.path_content.path.clone();
let users = &self.users;
- self.directory = Directory::new(&path, users, &self.filter, self.show_hidden, None)?;
+ // self.directory = Directory::new(&path, users, &self.filter, self.show_hidden, None)?;
+ self.tree = FileSystem::new(
+ path,
+ 5,
+ self.path_content.sort_kind.clone(),
+ users,
+ self.show_hidden,
+ &self.filter,
+ );
Ok(())
}
@@ -530,11 +542,13 @@ impl Tab {
/// Select the next sibling of the current node.
pub fn select_next(&mut self) -> Result<()> {
+ self.tree.next();
self.tree_select_next()
}
/// Select the previous sibling of the current node.
pub fn select_prev(&mut self) -> Result<()> {
+ self.tree.prev();
self.tree_select_prev()
}
diff --git a/src/term_manager.rs b/src/term_manager.rs
index ce3c8c1..945745f 100644
--- a/src/term_manager.rs
+++ b/src/term_manager.rs
@@ -25,6 +25,7 @@ use crate::selectable_content::SelectableContent;
use crate::status::Status;
use crate::tab::Tab;
use crate::trash::TrashInfo;
+use crate::trees::calculate_tree_window;
/// Iter over the content, returning a triplet of `(index, line, attr)`.
macro_rules! enumerated_colored_iter {
@@ -138,7 +139,7 @@ impl<'a> Draw for WinMain<'a> {
}
match self.tab.mode {
Mode::Preview => self.preview(self.tab, &self.tab.window, canvas),
- Mode::Tree => self.tree(self.status, self.tab, canvas),
+ Mode::Tree => self.trees(self.status, self.tab, canvas),
Mode::Normal => self.files(self.status, self.tab, canvas),
_ => match self.tab.previous_mode {
Mode::Tree => self.tree(self.status, self.tab, canvas),
@@ -423,6 +424,41 @@ impl<'a> WinMain<'a> {
Ok(())
}
+ fn trees(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> {
+ let left_margin = if status.display_full { 1 } else { 3 };
+ let (_, height) = canvas.size()?;
+ let (selected_index, content) = tab.tree.into_navigable_content(&tab.users);
+ let (top, bottom, len) = calculate_tree_window(selected_index, canvas.size()?.1, height);
+
+ for (i, (metadata, prefix, colored_string)) in content
+ .iter()
+ .enumerate()
+ .skip(top)
+ .take(min(len, bottom + 1))
+ {
+ let row = i + ContentWindow::WINDOW_MARGIN_TOP - top;
+ let mut attr = colored_string.color_effect.attr();
+ if status.flagged.contains(&colored_string.path) {
+ attr.effect |= Effect::BOLD;
+ canvas.print_with_attr(row, 0, "█", ATTR_YELLOW_BOLD)?;
+ }
+
+ let col_metadata = if status.display_full {
+ canvas.print_with_attr(row, left_margin, metadata, attr)?
+ } else {
+ 0
+ };
+ let col_tree_prefix = canvas.print(row, left_margin + col_metadata, prefix)?;
+ canvas.print_with_attr(
+ row,
+ left_margin + col_metadata + col_tree_prefix,
+ &colored_string.text,
+ attr,
+ )?;
+ }
+ self.second_line(status, tab, canvas)?;
+ Ok(())
+ }
fn print_line_number(
row_position_in_canvas: usize,
line_number_to_print: usize,
diff --git a/src/tree.rs b/src/tree.rs
index a659191..cb63eba 100644
--- a/src/tree.rs
+++ b/src/tree.rs
@@ -21,7 +21,7 @@ pub struct ColoredString {
}
impl ColoredString {
- fn new(text: String, color_effect: ColorEffect, path: std::path::PathBuf) -> Self {
+ pub fn new(text: String, color_effect: ColorEffect, path: std::path::PathBuf) -> Self {
Self {
text,
color_effect,
diff --git a/src/trees.rs b/src/trees.rs
new file mode 100644
index 0000000..9cf5f16
--- /dev/null
+++ b/src/trees.rs
@@ -0,0 +1,368 @@
+use std::{
+ collections::HashMap,
+ path::{Path, PathBuf},
+};
+
+use anyhow::Result;
+
+use crate::{
+ content_window::ContentWindow,
+ fileinfo::{files_collection, ColorEffect, FileInfo},
+ filter::FilterKind,
+ preview::ColoredTriplet,
+ sort::SortKind,
+ tree::ColoredString,
+ users::Users,
+ utils::filename_from_path,
+};
+#[derive(Debug, Clone)]
+pub struct Node {
+ pub path: PathBuf,
+ pub children: Option<Vec<PathBuf>>,
+ pub folded: bool,
+ pub selected: bool,
+}
+
+impl Node {
+ pub fn new(path: &Path, children: Option<Vec<PathBuf>>) -> Self {
+ Self {
+ path: path.to_owned(),
+ children,
+ folded: false,
+ selected: false,
+ }
+ }
+
+ pub fn fold(&mut self) {
+ self.folded = true
+ }
+
+ pub fn unfold(&mut self) {
+ self.folded = false
+ }
+
+ pub fn toggle_fold(&mut self) {
+ self.folded = !self.folded
+ }
+
+ pub fn select(&mut self) {
+ self.selected = true
+ }
+
+ pub fn unselect(&mut self) {
+ self.selected = false
+ }
+
+ pub fn fileinfo(&self, users: &Users) -> Result<FileInfo> {
+ FileInfo::new(&self.path, users)
+ }
+
+ pub fn color_effect(&self, users: &Users) -> Result<ColorEffect> {
+ Ok(ColorEffect::new(&self.fileinfo(users)?))
+ }
+
+ pub fn set_children(&mut self, children: Option<Vec<PathBuf>>) {
+ self.children = children
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct FileSystem {
+ root_path: PathBuf,
+ selected: PathBuf,
+ nodes: HashMap<PathBuf, Node>,
+ required_height: usize,
+}
+
+impl FileSystem {
+ pub const REQUIRED_HEIGHT: usize = 80;
+
+ pub fn new(
+ root_path: PathBuf,
+ depth: usize,
+ sort_kind: SortKind,
+ users: &Users,
+ show_hidden: bool,
+ filter_kind: &FilterKind,
+ ) -> Self {
+ // keep track of the depth
+ let start_depth = root_path.components().collect::<Vec<_>>().len();
+ let mut stack = vec![root_path.to_owned()];
+ let mut nodes: HashMap<PathBuf, Node> = HashMap::new();
+
+ while let Some(path) = stack.pop() {
+ let reached_depth = path.components().collect::<Vec<_>>().len();
+ if reached_depth >= depth + start_depth {
+ continue;
+ }
+ let mut node = Node::new(&path, None);
+ if let Ok(fileinfo) = node.fileinfo(users) {
+ if path.is_dir() && !path.is_symlink() {
+ if let Some(mut files) =
+ files_collection(&fileinfo, users, show_hidden, filter_kind, true)
+ {
+ sort_kind.sort(&mut files);
+ let children = files
+ .iter()
+ .map(|fileinfo| {
+ stack.push(fileinfo.path.to_owned());
+ fileinfo
+ })
+ .map(|fileinfo| fileinfo.path.to_owned())
+ .collect();
+ node.set_children(Some(children));
+ };
+ }
+ }
+ nodes.insert(node.path.to_owned(), node);
+ }
+
+ if let Some(node) = nodes.get_mut(&root_path) {
+ node.select()
+ }
+
+ Self {
+ selected: root_path.clone(),
+ root_path,
+ nodes,
+ required_height: Self::REQUIRED_HEIGHT,
+ }
+ }
+
+ pub fn empty() -> Self {
+ Self {
+ root_path: PathBuf::default(),
+ selected: PathBuf::default(),
+ nodes: HashMap::new(),
+ required_height: 0,
+ }
+ }
+
+ pub fn selected(&self) -> &Path {
+ self.selected.as_path()
+ }
+
+ pub fn root_path(&self) -> &Path {
+ self.root_path.as_path()
+ }
+
+ pub fn selected_node(&self) -> Option<&Node> {
+ self.nodes.get(self.selected())
+ }
+
+ pub fn sort(&mut self, sort_kind: SortKind) -> Result<()> {
+ todo!()
+ }
+
+ pub fn select<P>(&mut self, path: P)
+ where
+ P: AsRef<Path> + Into<PathBuf>,
+ {
+ self.unselect_current();
+ self.selected = path.into();
+ self.select_current();
+ }
+
+ fn unselect_current(&mut self) {
+ if let Some(node) = self.nodes.get_mut(&self.selected) {
+ node.unselect()
+ }
+ }
+
+ fn select_current(&mut self) {
+ if let Some(node) = self.nodes.get_mut(&self.selected) {
+ node.select()
+ }
+ }
+
+ // TODO: remove indentation with let ... else
+ /// Select next sibling or the next sibling of the parent
+ pub fn next(&mut self) {
+ if let Some(current) = self.selected_node() {
+ if let Some(children) = &current.children {
+ if let Some(child_path) = children.get(0) {
+ log::info!("first child {sel}", sel = self.selected.display());
+ self.select(child_path)
+ }
+ }
+ }
+
+ if let Some(parent) = self.selected.parent() {
+ if let Some(parent_node) = self.nodes.get(parent) {
+ if let Some(siblings) = &parent_node.children {
+ if let Some(index_selected) =
+ siblings.iter().position(|path| path == &self.selected)
+ {
+ if let Some(next_sibling) = siblings.get(index_selected + 1) {
+ self.selected = next_sibling.to_owned();
+ log::info!("next sibling {sel}", sel = self.selected.display());
+ self.select_current()
+ } else {
+ if let Some(parent_parent) = parent.parent() {
+ if let Some(parent_parent_node) = self.nodes.get(parent_parent) {
+ if let Some(parent_siblings) = &parent_parent_node.children {
+ if let Some(index_parent) =
+ parent_siblings.iter().position(|path| path == &parent)
+ {
+ if let Some(next_parent_sibling) =
+ parent_siblings.get(index_parent + 1)
+ {
+ self.selected = next_parent_sibling.to_owned();
+ log::info!(
+ "parent sibling {sel}",
+ sel = self.selected.display()
+ );
+ self.select_current()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // TODO: remove indentation with let ... else
+ /// Select previous sibling or parent if it's the first.
+ pub fn prev(&mut self) {
+ if let Some(parent) = self.selected.parent() {
+ if let Some(parent_node) = self.nodes.get(parent) {
+ if let Some(siblings) = &parent_node.children {
+ if let Some(index_selected) =
+ siblings.iter().position(|path| path == &self.selected)
+ {
+ if index_selected == 0 {
+ self.selected = parent.to_owned();
+ } else if let Some(prev_sibling) = siblings.get(index_selected - 1) {
+ self.selected = prev_sibling.to_owned();
+ }
+ self.select_current()
+ }
+ }
+ }
+ }
+ }
+
+ /// Fold selected node
+ pub fn toggle_fold(&mut self) {
+ if let Some(node) = self.nodes.get_mut(&self.selected) {
+ node.toggle_fold();
+ }
+ }
+
+ pub fn toggle_fold_all(&mut self) {
+ for (_, node) in self.nodes.iter_mut() {
+ node.toggle_fold()
+ }
+ }
+
+ pub fn search_first_match(&mut self, pattern: &str) {
+ if let Some(filename) = self.selected.file_name() {
+ let filename = filename.to_string_lossy();
+ if filename.contains(pattern) {
+ return;
+ }
+ }
+ todo!()
+ }
+
+ pub fn len(&mut self) -> usize {
+ self.nodes.len()
+ }
+
+ pub fn is_empty(self) -> bool {
+ self.nodes.is_empty()
+ }
+
+ pub fn into_navigable_content(&self, users: &Users) -> (usize, Vec<ColoredTriplet>) {
+ let required_height = self.required_height;
+ let mut stack = vec![("".to_owned(), self.root_path())];
+ let mut content = vec![];
+ let mut selected_index = 0;
+
+ while let Some((prefix, current)) = stack.pop() {
+ let Some(node) = &self.nodes.get(current) else {
+ continue;
+ };
+
+ if node.selected {
+ selected_index = content.len();
+ }
+
+ let Ok(fileinfo) = FileInfo::new(current, users) else {
+ continue;
+ };
+ let filename = filename_from_path(current).unwrap_or_default().to_owned();
+
+ let mut color_effect = ColorEffect::new(&fileinfo);
+ if node.selected {
+ color_effect.effect |= tuikit::attr::Effect::REVERSE;
+ }
+ content.push((
+ fileinfo.format_no_filename().unwrap_or_default(),
+ prefix.to_owned(),
+ ColoredString::new(filename, color_effect, current.to_owned()),
+ ));
+
+ if !node.folded {
+ let first_prefix = first_prefix(prefix.clone());
+ let other_prefix = other_prefix(prefix);
+
+ if let Some(children) = &node.children {
+ let mut leaves = children.iter();
+ let Some(first_leaf) = leaves.next() else {
+ continue;
+ };
+ stack.push((first_prefix.clone(), first_leaf));
+
+ for leaf in leaves {
+ stack.push((other_prefix.clone(), leaf));
+ }
+ }
+ }
+ if content.len() > required_height {
+ break;
+ }
+ }
+ (selected_index, content)
+ }
+}
+
+fn first_prefix(mut prefix: String) -> String {
+ prefix.push(' ');
+ prefix = prefix.replace("└──", " ");
+ prefix = prefix.replace("├──", "│ ");
+ prefix.push_str("└──");
+ prefix
+}
+
+fn other_prefix(mut prefix: String) -> String {
+ prefix.push(' ');
+ prefix = prefix.replace("└──", " ");
+ prefix = prefix.replace("├──", "│ ");
+ prefix.push_str("├──");
+ prefix
+}
+
+pub fn calculate_tree_window(
+ selected_index: usize,
+ terminal_height: usize,
+ length: usize,
+) -> (usize, usize, usize) {
+ let top: usize;
+ let bottom: usize;
+ let window_height = terminal_height - ContentWindow::WINDOW_MARGIN_TOP;
+ if selected_index < terminal_height - 1 {
+ top = 0;
+ bottom = window_height;
+ } else {
+ let padding = std::cmp::max(10, terminal_height / 2);
+ top = selected_index - padding;
+ bottom = top + window_height;
+ }
+
+ (top, bottom, length)
+}