summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqkzk <qkzk@users.noreply.github.com>2023-11-11 15:20:20 +0100
committerGitHub <noreply@github.com>2023-11-11 15:20:20 +0100
commitd758cda05909152821c0880a0efe7a9dc996f5de (patch)
treeb1ae4781765375071d71c8c0b0f54e8d2ba8a063
parentefe46e9abb71c6cfc6a4c56a15920ec73183f0a0 (diff)
parent22ae92fb93de3e31803708a4adc511df38f58f5c (diff)
Merge pull request #84 from qkzk/V0.1.23-tree-redo
V0.1.23 tree redo
-rw-r--r--development.md22
-rw-r--r--src/completion.rs14
-rw-r--r--src/cryptsetup.rs4
-rw-r--r--src/event_exec.rs148
-rw-r--r--src/fileinfo.rs24
-rw-r--r--src/opener.rs31
-rw-r--r--src/preview.rs220
-rw-r--r--src/sort.rs24
-rw-r--r--src/status.rs63
-rw-r--r--src/tab.rs197
-rw-r--r--src/term_manager.rs122
-rw-r--r--src/tree.rs884
12 files changed, 834 insertions, 919 deletions
diff --git a/development.md b/development.md
index 7cd376b..abb1ce5 100644
--- a/development.md
+++ b/development.md
@@ -602,12 +602,34 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally.
- [x] FIX: exploring root folder leads to wrong first line display.
- [x] allow seveval palettes for normal file colors
- [x] move every lazy_static configuration into config.
+- [x] FIX: encrypted are never shown as mounted
- [ ] mount usb key - should be merged with mtp
- [ ] document filepicking (from my config etc.).
- [ ] avoid multiple refreshs if we edit files ourself
+- [ ] Tree remade without recursion. Use an `HashMap<PathBuf, Node>`
+ - [x] FIX: folders are max depth hangs the app
+ - [x] FIX: rename renames the root path
+ - [x] FIX: scrolling to bottom of tree is bugged
+ - [x] FIX: scrolling starts 1 row to low
+ - [x] FIX: filename in first line
+ - [x] FIX: can't "open" a folder to redo the tree there
+ - [x] FIX: move back from root should redo the parent tree
+ - [x] FIX: move up from to go to last and vice versa
+ - [x] FIX: enter a dir from normal mode shouldn't set mode tree
+ - [x] Use a generic trait for movements
+ - [x] FIX: first line position for tree
+ - [x] FIX: searching for file very low don't scroll there
+ - [x] FIX: search can only find the first match
+ - [x] FIX: leaving preview doesn't reset tree
+ - [x] Add a link to previous and next node in Node. Simplify navigation, increase ram usage :/
+ - [x] test everything
+ - [x] refactor
+ - [x] document
## TODO
+- [ ] FIX: tab.selected() should return Option<&FileInfo>
+- [ ] use widget for every drawable element. First line should be a collection of widget which are drawned
- [ ] while second window is opened, if the selection is below half screen, it's not shown anymore.
Scroll to second element if needed
- [ ] remote control
diff --git a/src/completion.rs b/src/completion.rs
index fca4559..5e5f469 100644
--- a/src/completion.rs
+++ b/src/completion.rs
@@ -3,8 +3,7 @@ use std::fs::{self, ReadDir};
use anyhow::Result;
use strum::IntoEnumIterator;
-use crate::fileinfo::PathContent;
-use crate::preview::ColoredTriplet;
+use crate::{fileinfo::PathContent, tree::Filenames};
/// Different kind of completions
#[derive(Clone, Default, Copy)]
@@ -213,16 +212,11 @@ impl Completion {
}
/// Looks for file within tree completing what the user typed.
- pub fn search_from_tree(
- &mut self,
- input_string: &str,
- content: &[ColoredTriplet],
- ) -> Result<()> {
+ pub fn search_from_tree(&mut self, input_string: &str, content: Filenames) -> Result<()> {
self.update(
content
- .iter()
- .filter(|(_, _, s)| s.text.contains(input_string))
- .map(|(_, _, s)| s.text.replace("▸ ", "").replace("▾ ", ""))
+ .filter(|&p| p.contains(input_string))
+ .map(|p| p.replace("▸ ", "").replace("▾ ", ""))
.collect(),
);
diff --git a/src/cryptsetup.rs b/src/cryptsetup.rs
index 59b37c4..261fa2a 100644
--- a/src/cryptsetup.rs
+++ b/src/cryptsetup.rs
@@ -102,7 +102,9 @@ impl CryptoDevice {
}
pub fn mount_point(&self) -> Option<String> {
- System::new_with_specifics(RefreshKind::new().with_disks())
+ let mut system = System::new_with_specifics(RefreshKind::new().with_disks());
+ system.refresh_disks_list();
+ system
.disks()
.iter()
.map(|d| d.mount_point())
diff --git a/src/event_exec.rs b/src/event_exec.rs
index f908b66..95e95e7 100644
--- a/src/event_exec.rs
+++ b/src/event_exec.rs
@@ -19,8 +19,9 @@ use crate::log_line;
use crate::mocp::Mocp;
use crate::mocp::MOCP;
use crate::mode::{InputSimple, MarkAction, Mode, Navigate, NeedConfirmation};
+use crate::opener::execute_and_capture_output_with_path;
use crate::opener::{
- execute_and_capture_output, execute_and_capture_output_without_check, execute_in_child,
+ execute_and_capture_output_without_check, execute_in_child,
execute_in_child_without_output_with_path,
};
use crate::password::{PasswordKind, PasswordUsage};
@@ -73,18 +74,14 @@ impl EventAction {
let tab = status.selected_non_mut();
match tab.mode {
- Mode::Normal => {
- let Some(file) = tab.path_content.selected() else {
+ Mode::Normal | Mode::Tree => {
+ let Ok(file) = tab.selected() else {
return Ok(());
};
let path = file.path.clone();
status.toggle_flag_on_path(&path);
status.selected().down_one_row();
}
- Mode::Tree => {
- let path = tab.directory.tree.current_node.filepath();
- status.toggle_flag_on_path(&path);
- }
_ => (),
}
Ok(())
@@ -241,11 +238,10 @@ impl EventAction {
/// Display the help which can be navigated and displays the configrable
/// binds.
pub fn help(status: &mut Status) -> Result<()> {
- let help = status.help.clone();
- let tab = status.selected();
- tab.set_mode(Mode::Preview);
- tab.preview = Preview::help(&help);
- tab.window.reset(tab.preview.len());
+ status.selected().set_mode(Mode::Preview);
+ status.selected().preview = Preview::help(&status.help);
+ let len = status.selected_non_mut().preview.len();
+ status.selected().window.reset(len);
Ok(())
}
@@ -286,12 +282,7 @@ impl EventAction {
/// Once a quit event is received, we change a flag and break the main loop.
/// It's usefull to reset the cursor before leaving the application.
pub fn quit(tab: &mut Tab) -> Result<()> {
- if let Mode::Tree = tab.mode {
- tab.refresh_view()?;
- tab.set_mode(Mode::Normal)
- } else {
- tab.must_quit = true;
- }
+ tab.must_quit = true;
Ok(())
}
/// Toggle the display of hidden files.
@@ -302,7 +293,7 @@ impl EventAction {
.reset_files(&tab.filter, tab.show_hidden, &tab.users)?;
tab.window.reset(tab.path_content.content.len());
if let Mode::Tree = tab.mode {
- tab.make_tree()?
+ tab.make_tree(None)?
}
Ok(())
}
@@ -378,7 +369,7 @@ impl EventAction {
/// Basic folders (/, /dev... $HOME) and mount points (even impossible to
/// visit ones) are proposed.
pub fn shortcut(tab: &mut Tab) -> Result<()> {
- std::env::set_current_dir(tab.current_directory_path())?;
+ std::env::set_current_dir(tab.directory_of_selected()?)?;
tab.shortcut.update_git_root();
tab.set_mode(Mode::Navigate(Navigate::Shortcut));
Ok(())
@@ -396,7 +387,7 @@ impl EventAction {
};
let nvim_server = status.nvim_server.clone();
if status.flagged.is_empty() {
- let Some(fileinfo) = status.selected_non_mut().selected() else {
+ let Ok(fileinfo) = status.selected_non_mut().selected() else {
return Ok(());
};
let Some(path_str) = fileinfo.path.to_str() else {
@@ -460,7 +451,7 @@ impl EventAction {
log_line!("{DEFAULT_DRAGNDROP} must be installed.");
return Ok(());
}
- let Some(file) = status.selected_non_mut().selected() else {
+ let Ok(file) = status.selected_non_mut().selected() else {
return Ok(());
};
let path_str = file
@@ -474,12 +465,12 @@ impl EventAction {
pub fn search_next(status: &mut Status) -> Result<()> {
let tab = status.selected();
+ let Some(searched) = tab.searched.clone() else {
+ return Ok(());
+ };
match tab.mode {
- Mode::Tree => (),
+ Mode::Tree => tab.tree.search_first_match(&searched),
_ => {
- let Some(searched) = tab.searched.clone() else {
- return Ok(());
- };
let next_index = (tab.path_content.index + 1) % tab.path_content.content.len();
tab.search_from(&searched, next_index);
status.update_second_pane_for_preview()?;
@@ -527,7 +518,7 @@ impl EventAction {
Mode::Navigate(Navigate::CliInfo) => status.cli_info.next(),
Mode::Navigate(Navigate::EncryptedDrive) => status.encrypted_devices.next(),
Mode::InputCompleted(_) => status.selected().completion.next(),
- Mode::Tree => status.selected().select_next()?,
+ Mode::Tree => status.selected().tree_select_next()?,
_ => (),
};
status.update_second_pane_for_preview()
@@ -556,7 +547,11 @@ impl EventAction {
match tab.mode {
Mode::Normal => LeaveMode::open_file(status),
Mode::Tree => {
- tab.select_first_child()?;
+ if tab.tree.selected_path().is_file() {
+ tab.tree_select_next()?;
+ } else {
+ LeaveMode::open_file(status)?;
+ };
status.update_second_pane_for_preview()
}
Mode::InputSimple(_) | Mode::InputCompleted(_) => {
@@ -622,7 +617,7 @@ impl EventAction {
}
Mode::Preview => tab.page_up(),
Mode::Tree => {
- tab.tree_page_up()?;
+ tab.tree_page_up();
status.update_second_pane_for_preview()?;
}
_ => (),
@@ -706,7 +701,10 @@ impl EventAction {
}
Mode::InputCompleted(InputCompleted::Goto) => LeaveMode::goto(status)?,
Mode::InputCompleted(InputCompleted::Command) => LeaveMode::command(status)?,
- Mode::Normal => LeaveMode::open_file(status)?,
+ Mode::Normal => {
+ LeaveMode::open_file(status)?;
+ must_reset_mode = false;
+ }
Mode::Tree => LeaveMode::tree(status)?,
Mode::NeedConfirmation(_)
| Mode::Preview
@@ -799,7 +797,7 @@ impl EventAction {
return Ok(());
}
if let Mode::Normal | Mode::Tree = tab.mode {
- let Some(file_info) = tab.selected() else {
+ let Ok(file_info) = tab.selected() else {
return Ok(());
};
info!("selected {:?}", file_info);
@@ -895,18 +893,15 @@ impl EventAction {
/// Fold the current node of the tree.
/// Has no effect on "file" nodes.
pub fn tree_fold(tab: &mut Tab) -> Result<()> {
- let (tree, _, _) = tab.directory.tree.explore_position(false);
- tree.node.toggle_fold();
- tab.directory.make_preview();
- tab.select_next()
+ tab.tree.toggle_fold();
+ Ok(())
}
/// Unfold every child node in the tree.
/// Recursively explore the tree and unfold every node.
/// Reset the display.
pub fn tree_unfold_all(tab: &mut Tab) -> Result<()> {
- tab.directory.tree.unfold_children();
- tab.directory.make_preview();
+ tab.tree.unfold_all();
Ok(())
}
@@ -914,8 +909,7 @@ impl EventAction {
/// Recursively explore the tree and fold every node.
/// Reset the display.
pub fn tree_fold_all(tab: &mut Tab) -> Result<()> {
- tab.directory.tree.fold_children();
- tab.directory.make_preview();
+ tab.tree.fold_all();
Ok(())
}
@@ -1103,7 +1097,11 @@ impl Display for NodeCreation {
impl NodeCreation {
fn create(&self, tab: &mut Tab) -> Result<()> {
let root_path = match tab.previous_mode {
- Mode::Tree => tab.directory.tree.directory_of_selected()?.to_owned(),
+ Mode::Tree => tab
+ .tree
+ .directory_of_selected()
+ .context("no parent")?
+ .to_owned(),
_ => tab.path_content.path.clone(),
};
log::info!("root_path: {root_path:?}");
@@ -1141,11 +1139,14 @@ impl LeaveMode {
/// Open the file with configured opener or enter the directory.
pub fn open_file(status: &mut Status) -> Result<()> {
let tab = status.selected();
+ if matches!(tab.mode, Mode::Tree) {
+ return EventAction::open_file(status);
+ };
if tab.path_content.is_empty() {
return Ok(());
}
if tab.path_content.is_selected_dir()? {
- tab.go_to_child()
+ tab.go_to_selected_dir()
} else {
EventAction::open_file(status)
}
@@ -1296,15 +1297,15 @@ impl LeaveMode {
/// We only try to rename in the same directory, so it shouldn't be a problem.
/// Filename is sanitized before processing.
pub fn rename(tab: &mut Tab) -> Result<()> {
- let fileinfo = match tab.previous_mode {
- Mode::Tree => &tab.directory.tree.current_node.fileinfo,
- _ => tab
- .path_content
+ let original_path = if let Mode::Tree = tab.previous_mode {
+ tab.tree.selected_path()
+ } else {
+ tab.path_content
.selected()
- .context("rename: couldnt parse selected")?,
+ .context("rename: couldn't parse selected file")?
+ .path
+ .as_path()
};
-
- let original_path = &fileinfo.path;
if let Some(parent) = original_path.parent() {
let new_path = parent.join(sanitize_filename::sanitize(tab.input.string()));
info!(
@@ -1365,7 +1366,7 @@ impl LeaveMode {
/// The current order of files is used.
pub fn search(status: &mut Status) -> Result<()> {
let tab = status.selected();
- let searched = tab.input.string();
+ let searched = &tab.input.string();
tab.input.reset();
if searched.is_empty() {
tab.searched = None;
@@ -1374,19 +1375,12 @@ impl LeaveMode {
tab.searched = Some(searched.clone());
match tab.previous_mode {
Mode::Tree => {
- tab.directory.tree.unselect_children();
- if let Some(position) = tab.directory.tree.select_first_match(&searched) {
- tab.directory.tree.position = position;
- (_, _, tab.directory.tree.current_node) =
- tab.directory.tree.select_from_position()?;
- } else {
- tab.directory.tree.select_root()
- };
- tab.directory.make_preview();
+ log::info!("searching in tree");
+ tab.tree.search_first_match(searched);
}
_ => {
let next_index = tab.path_content.index;
- tab.search_from(&searched, next_index);
+ tab.search_from(searched, next_index);
}
};
status.update_second_pane_for_preview()
@@ -1443,14 +1437,15 @@ impl LeaveMode {
/// Execute the selected node if it's a file else enter the directory.
pub fn tree(status: &mut Status) -> Result<()> {
- let tab = status.selected();
- let node = tab.directory.tree.current_node.clone();
- if !node.is_dir {
- EventAction::open_file(status)
- } else {
- tab.set_pathcontent(&node.filepath())?;
- tab.make_tree()?;
+ let path = status.selected_non_mut().selected()?.path;
+ let is_dir = path.is_dir();
+ if is_dir {
+ status.selected().set_pathcontent(&path)?;
+ status.selected().make_tree(None)?;
+ status.selected().set_mode(Mode::Tree);
Ok(())
+ } else {
+ EventAction::open_file(status)
}
}
@@ -1520,7 +1515,7 @@ impl LeaveMode {
tab.path_content
.reset_files(&tab.filter, tab.show_hidden, &tab.users)?;
if let Mode::Tree = tab.previous_mode {
- tab.make_tree()?;
+ tab.make_tree(None)?;
}
tab.window.reset(tab.path_content.content.len());
Ok(())
@@ -1549,15 +1544,18 @@ impl LeaveMode {
};
let (username, hostname, remote_path) = (strings[0], strings[1], strings[2]);
- let current_path: &str = tab
- .current_directory_path()
- .to_str()
- .context("couldn't parse the path")?;
+ let current_path: &str = &tab
+ .directory_of_selected_previous_mode()?
+ .display()
+ .to_string();
let first_arg = &format!("{username}@{hostname}:{remote_path}");
- let command_output =
- execute_and_capture_output(SSHFS_EXECUTABLE, &[first_arg, current_path]);
- info!("{SSHFS_EXECUTABLE} output {command_output:?}");
- log_line!("{SSHFS_EXECUTABLE} output {command_output:?}");
+ let command_output = execute_and_capture_output_with_path(
+ SSHFS_EXECUTABLE,
+ current_path,
+ &[first_arg, current_path],
+ );
+ info!("{SSHFS_EXECUTABLE} {strings:?} output {command_output:?}");
+ log_line!("{SSHFS_EXECUTABLE} {strings:?} output {command_output:?}");
Ok(())
}
}
diff --git a/src/fileinfo.rs b/src/fileinfo.rs
index f4156c3..27ad61d 100644
--- a/src/fileinfo.rs
+++ b/src/fileinfo.rs
@@ -16,6 +16,7 @@ use crate::filter::FilterKind;
use crate::git::git;
use crate::impl_selectable_content;
use crate::sort::SortKind;
+use crate::tree::Node;
use crate::users::Users;
use crate::utils::filename_from_path;
@@ -158,7 +159,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();
@@ -359,7 +360,7 @@ impl PathContent {
let fileinfo = FileInfo::from_path_with_name(path, filename_from_path(path)?, users)?;
if let Some(true_files) =
- files_collection(&fileinfo, users, show_hidden, filter_kind, false)
+ files_collection(&fileinfo.path, users, show_hidden, filter_kind, false)
{
files.extend(true_files);
}
@@ -564,6 +565,7 @@ pub struct ColorEffect {
impl ColorEffect {
/// Calculates a color and an effect from `fm::file_info::FileInfo`.
+ #[inline]
pub fn new(fileinfo: &FileInfo) -> ColorEffect {
let color = fileinfo_color(fileinfo);
@@ -576,6 +578,15 @@ impl ColorEffect {
Self { color, effect }
}
+ #[inline]
+ pub fn node(fileinfo: &FileInfo, current_node: &Node) -> Self {
+ let mut color_effect = Self::new(fileinfo);
+ if current_node.selected() {
+ color_effect.effect |= Effect::REVERSE;
+ }
+ color_effect
+ }
+
/// Makes a new `tuikit::attr::Attr` where `bg` is default.
pub fn attr(&self) -> Attr {
Attr {
@@ -697,13 +708,13 @@ fn filekind_and_filename(filename: &str, file_kind: &FileKind<Valid>) -> String
/// Files are filtered by filterkind and the display hidden flag.
/// Returns None if there's no file.
pub fn files_collection(
- fileinfo: &FileInfo,
+ path: &path::Path,
users: &Users,
show_hidden: bool,
filter_kind: &FilterKind,
keep_dir: bool,
) -> Option<Vec<FileInfo>> {
- match read_dir(&fileinfo.path) {
+ match read_dir(path) {
Ok(read_dir) => Some(
read_dir
.filter_map(|direntry| direntry.ok())
@@ -714,10 +725,7 @@ pub fn files_collection(
.collect(),
),
Err(error) => {
- info!(
- "Couldn't read path {path} - {error}",
- path = fileinfo.path.display(),
- );
+ info!("Couldn't read path {path} - {error}", path = path.display(),);
None
}
}
diff --git a/src/opener.rs b/src/opener.rs
index 1b77ad7..7e1e186 100644
--- a/src/opener.rs
+++ b/src/opener.rs
@@ -499,6 +499,37 @@ pub fn execute_and_capture_output<S: AsRef<std::ffi::OsStr> + fmt::Debug>(
}
/// Execute a command with options in a fork.
+/// Wait for termination and return either :
+/// `Ok(stdout)` if the status code is 0
+/// an Error otherwise
+/// Branch stdin and stderr to /dev/null
+pub fn execute_and_capture_output_with_path<
+ S: AsRef<std::ffi::OsStr> + fmt::Debug,
+ P: AsRef<Path>,
+>(
+ exe: S,
+ path: P,
+ args: &[&str],
+) -> Result<String> {
+ info!("execute_and_capture_output. executable: {exe:?}, arguments: {args:?}",);
+ let child = Command::new(exe)
+ .args(args)
+ .stdin(Stdio::null())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::null())
+ .current_dir(path)
+ .spawn()?;
+ let output = child.wait_with_output()?;
+ if output.status.success() {
+ Ok(String::from_utf8(output.stdout)?)
+ } else {
+ Err(anyhow!(
+ "execute_and_capture_output: command didn't finish properly",
+ ))
+ }
+}
+
+/// Execute a command with options in a fork.
/// Wait for termination and return either `Ok(stdout)`.
/// Branch stdin and stderr to /dev/null
pub fn execute_and_capture_output_without_check<S: AsRef<std::ffi::OsStr> + fmt::Debug>(
diff --git a/src/preview.rs b/src/preview.rs
index 89d656d..e82a541 100644
--- a/src/preview.rs
+++ b/src/preview.rs
@@ -21,9 +21,10 @@ use crate::constant_strings_paths::{
};
use crate::content_window::ContentWindow;
use crate::decompress::{list_files_tar, list_files_zip};
-use crate::fileinfo::{FileInfo, FileKind};
+use crate::fileinfo::{ColorEffect, FileInfo, FileKind};
use crate::filter::FilterKind;
use crate::opener::execute_and_capture_output_without_check;
+use crate::sort::SortKind;
use crate::tree::{ColoredString, Tree};
use crate::users::Users;
use crate::utils::{clear_tmp_file, filename_from_path, is_program_in_path};
@@ -114,19 +115,11 @@ impl Preview {
/// Creates a new `Directory` from the file_info
/// It explores recursivelly the directory and creates a tree.
/// The recursive exploration is limited to depth 2.
- pub fn directory(
- file_info: &FileInfo,
- users: &Users,
- filter: &FilterKind,
- show_hidden: bool,
- ) -> Result<Self> {
+ pub fn directory(file_info: &FileInfo, users: &Users) -> Result<Self> {
Ok(Self::Directory(Directory::new(
- &file_info.path,
+ file_info.path.to_owned(),
users,
- filter,
- show_hidden,
- Some(2),
- )?))
+ )))
}
/// Creates a new preview instance based on the filekind and the extension of
@@ -1049,181 +1042,34 @@ impl ColoredText {
#[derive(Clone, Debug)]
pub struct Directory {
pub content: Vec<ColoredTriplet>,
- pub tree: Tree,
- len: usize,
- pub selected_index: usize,
}
impl Directory {
- /// Creates a new tree view of the directory.
- /// We only hold the result here, since the tree itself has now usage atm.
- pub fn new(
- path: &Path,
- users: &Users,
- filter_kind: &FilterKind,
- show_hidden: bool,
- max_depth: Option<usize>,
- ) -> Result<Self> {
- let max_depth = match max_depth {
- Some(max_depth) => max_depth,
- None => Tree::MAX_DEPTH,
- };
+ pub fn new(path: PathBuf, users: &Users) -> Self {
+ let tree = Tree::new(
+ path,
+ 4,
+ SortKind::tree_default(),
+ users,
+ false,
+ &FilterKind::All,
+ );
- let mut tree = Tree::from_path(path, max_depth, users, filter_kind, show_hidden, vec![0])?;
- tree.select_root();
- let (selected_index, content) = tree.into_navigable_content();
- Ok(Self {
- tree,
- len: content.len(),
- content,
- selected_index,
- })
- }
+ let (_selected_index, content) = tree.into_navigable_content(users);
- /// Creates an empty directory preview.
- pub fn empty(path: &Path, users: &Users) -> Result<Self> {
- Ok(Self {
- tree: Tree::empty(path, users)?,
- len: 0,
- content: vec![],
- selected_index: 0,
- })
+ Self { content }
}
- /// Reset the attributes to default one and free some unused memory.
- pub fn clear(&mut self) {
- self.len = 0;
- self.content = vec![];
- self.selected_index = 0;
- self.tree.clear();
+ pub fn empty() -> Self {
+ Self { content: vec![] }
}
- /// Number of displayed lines.
pub fn len(&self) -> usize {
- self.len
+ self.content.len()
}
- /// True if there's no lines in preview.
pub fn is_empty(&self) -> bool {
- self.len == 0
- }
-
- /// Select the root node and reset the view.
- pub fn select_root(&mut self) -> Result<()> {
- self.tree.select_root();
- (self.selected_index, self.content) = self.tree.into_navigable_content();
- self.update_tree_position_from_index()?;
- Ok(())
- }
-
- /// Unselect every child node.
- pub fn unselect_children(&mut self) {
- self.tree.unselect_children()
- }
-
- /// Select the "next" element of the tree if any.
- /// This is the element immediatly below the current one.
- pub fn select_next(&mut self) -> Result<()> {
- if self.selected_index < self.content.len() {
- self.tree.increase_required_height();
- self.unselect_children();
- self.selected_index += 1;
- self.update_tree_position_from_index()?;
- }
- Ok(())
- }
-
- /// Select the previous sibling if any.
- /// This is the element immediatly below the current one.
- pub fn select_prev(&mut self) -> Result<()> {
- if self.selected_index > 0 {
- self.tree.decrease_required_height();
- self.unselect_children();
- self.selected_index -= 1;
- self.update_tree_position_from_index()?;
- }
- Ok(())
- }
-
- /// Move up 10 times.
- pub fn page_up(&mut self) -> Result<()> {
- if self.selected_index > 10 {
- self.selected_index -= 10;
- } else {
- self.selected_index = 1;
- }
- self.update_tree_position_from_index()
- }
-
- /// Move down 10 times
- pub fn page_down(&mut self) -> Result<()> {
- self.selected_index += 10;
- if self.selected_index > self.content.len() {
- if !self.content.is_empty() {
- self.selected_index = self.content.len();
- } else {
- self.selected_index = 1;
- }
- }
- self.update_tree_position_from_index()
- }
-
- /// Update the position of the selected element from its index.
- pub fn update_tree_position_from_index(&mut self) -> Result<()> {
- self.tree.position = self.tree.position_from_index(self.selected_index);
- let (_, _, node) = self.tree.select_from_position()?;
- self.tree.current_node = node;
- (_, self.content) = self.tree.into_navigable_content();
- Ok(())
- }
-
- /// Select the first child, if any.
- pub fn select_first_child(&mut self) -> Result<()> {
- self.tree.select_first_child()?;
- (self.selected_index, self.content) = self.tree.into_navigable_content();
- self.update_tree_position_from_index()?;
- Ok(())
- }
-
- /// Select the parent of current node.
- pub fn select_parent(&mut self) -> Result<()> {
- self.tree.select_parent()?;
- (self.selected_index, self.content) = self.tree.into_navigable_content();
- self.update_tree_position_from_index()?;
- Ok(())
- }
-
- /// Select the last leaf of the tree (ie the last line.)
- pub fn go_to_bottom_leaf(&mut self) -> Result<()> {
- self.tree.go_to_bottom_leaf()?;
- (self.selected_index, self.content) = self.tree.into_navigable_content();
- self.update_tree_position_from_index()?;
- Ok(())
- }
-
- /// Make a preview of the tree.
- pub fn make_preview(&mut self) {
- (self.selected_index, self.content) = self.tree.into_navigable_content();
- }
-
- /// Calculates the top, bottom and lenght of the view, depending on which element
- /// is selected and the size of the window used to display.
- pub fn calculate_tree_window(&self, terminal_height: usize) -> (usize, usize, usize) {
- let length = self.content.len();
-
- let top: usize;
- let bottom: usize;
- let window_height = terminal_height - ContentWindow::WINDOW_MARGIN_TOP;
- if self.selected_index < terminal_height - 1 {
- top = 0;
- bottom = window_height;
- } else {
- let padding = std::cmp::max(10, terminal_height / 2);
- top = self.selected_index - padding;
- bottom = top + window_height;
- }
-
- (top, bottom, length)
+ self.content.is_empty()
}
}
@@ -1313,6 +1159,32 @@ macro_rules! impl_window {
/// Used to iter and impl window trait in tree mode.
pub type ColoredTriplet = (String, String, ColoredString);
+pub trait MakeTriplet {
+ fn make(
+ fileinfo: &FileInfo,
+ prefix: &str,
+ filename_text: String,
+ color_effect: ColorEffect,
+ current_path: &std::path::Path,
+ ) -> ColoredTriplet;
+}
+
+impl MakeTriplet for ColoredTriplet {
+ #[inline]
+ fn make(