diff options
author | Canop <cano.petrole@gmail.com> | 2021-09-17 21:08:55 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2021-09-17 21:08:55 +0200 |
commit | 21671ef69a92e562ccbf5a2521243663819e065c (patch) | |
tree | d17e75bd47cd818518525b2bca7188f661655abe /src | |
parent | b45bb00dbb4b1df306faa32cd5d943bdd75f0610 (diff) |
fix special-path/Enter for sym links
Fix #448
Diffstat (limited to 'src')
-rw-r--r-- | src/display/displayable_tree.rs | 2 | ||||
-rw-r--r-- | src/tree/tree.rs | 70 | ||||
-rw-r--r-- | src/tree/tree_line.rs | 54 | ||||
-rw-r--r-- | src/tree_build/bid.rs | 6 | ||||
-rw-r--r-- | src/tree_build/bline.rs | 32 | ||||
-rw-r--r-- | src/tree_build/builder.rs | 8 | ||||
-rw-r--r-- | src/tree_build/mod.rs | 5 |
7 files changed, 97 insertions, 80 deletions
diff --git a/src/display/displayable_tree.rs b/src/display/displayable_tree.rs index 6bd5b72..8a27aa6 100644 --- a/src/display/displayable_tree.rs +++ b/src/display/displayable_tree.rs @@ -25,7 +25,7 @@ use { file_size, git2::Status, std::io::Write, - termimad::{Area, CompoundStyle, ProgressBar}, + termimad::{CompoundStyle, ProgressBar}, }; /// A tree wrapper which can be used either diff --git a/src/tree/tree.rs b/src/tree/tree.rs index b1d2ab0..b147490 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -7,8 +7,9 @@ use { git::TreeGitStatus, task_sync::ComputationResult, task_sync::Dam, - tree_build::TreeBuilder, + tree_build::{BId, TreeBuilder}, }, + fnv::FnvHashMap, std::{ cmp::Ord, mem, @@ -68,8 +69,44 @@ impl Tree { /// - sort the lines /// - compute left branchs pub fn after_lines_changed(&mut self) { - // we sort the lines (this is mandatory to avoid crashes) - self.lines[1..].sort(); + + // we need to order the lines to build the tree. + // It's a little complicated because + // - we want a case insensitive sort + // - we still don't want to confuse the children of AA and Aa + // - a node can come from a not parent node, when we followed a link + let mut bid_parents: FnvHashMap<BId, BId> = FnvHashMap::default(); + let mut bid_lines: FnvHashMap<BId, &TreeLine> = FnvHashMap::default(); + for line in self.lines[..].iter() { + if let Some(parent_bid) = line.parent_bid { + bid_parents.insert(line.bid, parent_bid); + } + bid_lines.insert(line.bid, &line); + } + let mut sort_paths: FnvHashMap<BId, String> = FnvHashMap::default(); + for line in self.lines[1..].iter() { + let mut sort_path = String::new(); + let mut bid = line.bid; + loop { + if let Some(l) = bid_lines.get(&bid) { + sort_path = format!( + "{}-{}/{}", + l.path.to_string_lossy().to_lowercase(), + bid.index(), // to be sure to separate paths having the same lowercase + sort_path, + ); + } else { + break; + } + if let Some(&parent_bid) = bid_parents.get(&bid) { + bid = parent_bid; + } else { + break; + } + } + sort_paths.insert(line.bid, sort_path); + } + self.lines[1..].sort_by_key(|line| sort_paths.get(&line.bid).unwrap()); let mut best_index = 0; // index of the line with the best score for i in 1..self.lines.len() { @@ -86,24 +123,21 @@ impl Tree { for end_index in (1..self.lines.len()).rev() { let depth = (self.lines[end_index].depth - 1) as usize; let start_index = { - let parent_index = { - let parent_path = &self.lines[end_index].path.parent(); - match parent_path { - Some(parent_path) => { - let mut index = end_index; - loop { - index -= 1; - if self.lines[index].path == *parent_path { - break; - } - if index == 0 { - break; - } + let parent_index = match self.lines[end_index].parent_bid { + Some(parent_bid) => { + let mut index = end_index; + loop { + index -= 1; + if self.lines[index].bid == parent_bid { + break; + } + if index == 0 { + break; } - index } - None => end_index, // Should not happen + index } + None => end_index, // Should not happen }; if parent_index != last_parent_index { // the line at end_index is the last listed child of the line at parent_index diff --git a/src/tree/tree_line.rs b/src/tree/tree_line.rs index 3742f53..612e271 100644 --- a/src/tree/tree_line.rs +++ b/src/tree/tree_line.rs @@ -4,10 +4,10 @@ use { app::{Selection, SelectionType}, file_sum::FileSum, git::LineGitStatus, + tree_build::BId, }, lazy_regex::regex_captures, std::{ - cmp::{self, Ord, Ordering, PartialOrd}, fs, path::{Path, PathBuf}, }, @@ -22,6 +22,8 @@ use is_executable::IsExecutable; /// a line in the representation of the file hierarchy #[derive(Debug, Clone)] pub struct TreeLine { + pub bid: BId, + pub parent_bid: Option<BId>, pub left_branchs: Box<[bool]>, // a depth-sized array telling whether a branch pass pub depth: u16, pub path: PathBuf, @@ -133,55 +135,5 @@ impl TreeLine { } } } -impl PartialEq for TreeLine { - fn eq(&self, other: &TreeLine) -> bool { - self.path == other.path - } -} - -impl Eq for TreeLine {} -impl Ord for TreeLine { - // paths are sorted in a complete ignore case way - // (A<a<B<b) - fn cmp(&self, other: &TreeLine) -> Ordering { - let mut sci = self.path.components(); - let mut oci = other.path.components(); - loop { - match sci.next() { - Some(sc) => { - match oci.next() { - Some(oc) => { - let scs = sc.as_os_str().to_string_lossy(); - let ocs = oc.as_os_str().to_string_lossy(); - let lower_ordering = scs.to_lowercase().cmp(&ocs.to_lowercase()); - if lower_ordering != Ordering::Equal { - return lower_ordering; - } - let ordering = scs.cmp(&ocs); - if ordering != Ordering::Equal { - return ordering; - } - } - None => { - return Ordering::Greater; - } - }; - } - None => { - if oci.next().is_some() { - return Ordering::Less; - } else { - return Ordering::Equal; - } - } - }; - } - } -} -impl PartialOrd for TreeLine { - fn partial_cmp(&self, other: &TreeLine) -> Option<cmp::Ordering> { - Some(self.cmp(other)) - } -} diff --git a/src/tree_build/bid.rs b/src/tree_build/bid.rs index 951fe8d..055c125 100644 --- a/src/tree_build/bid.rs +++ b/src/tree_build/bid.rs @@ -1,4 +1,8 @@ -use {super::bline::BLine, id_arena::Id, std::cmp::Ordering}; +use { + super::bline::BLine, + id_arena::Id, + std::cmp::Ordering, +}; pub type BId = Id<BLine>; diff --git a/src/tree_build/bline.rs b/src/tree_build/bline.rs index 306aafd..e38e4da 100644 --- a/src/tree_build/bline.rs +++ b/src/tree_build/bline.rs @@ -4,11 +4,16 @@ use { app::AppContext, errors::TreeBuildError, git::GitIgnoreChain, - path::SpecialHandling, + path::{normalize_path, SpecialHandling}, tree::*, }, id_arena::Arena, - std::{fs, path::PathBuf, result::Result}, + std::{ + fs, + io, + path::PathBuf, + result::Result, + }, }; /// like a tree line, but with the info needed during the build @@ -68,6 +73,23 @@ impl BLine { }) } } + /// execute read_dir either on the link if we're a link, + /// or on the path otherwise. + /// + /// Assume the can_enter check has already be done. + pub(crate) fn read_dir(&self) -> io::Result<fs::ReadDir> { + if self.file_type.is_symlink() { + if let Ok(target) = fs::read_link(&self.path) { + let mut target_path = PathBuf::from(&target); + if target_path.is_relative() { + target_path = self.path.parent().unwrap().join(target_path); + target_path = normalize_path(target_path); + } + return fs::read_dir(&target_path); + } + } + fs::read_dir(&self.path) + } /// tell whether we should list the childs of the present line pub fn can_enter(&self) -> bool { if self.file_type.is_dir() && self.special_handling != SpecialHandling::NoEnter { @@ -79,7 +101,7 @@ impl BLine { if let Ok(target) = fs::read_link(&self.path) { let mut target_path = PathBuf::from(&target); if target_path.is_relative() { - target_path = self.path.parent().unwrap().join(target_path) + target_path = self.path.parent().unwrap().join(target_path); } if let Ok(target_metadata) = fs::symlink_metadata(&target_path) { if target_metadata.file_type().is_dir() { @@ -96,7 +118,7 @@ impl BLine { } false } - pub fn to_tree_line(&self, con: &AppContext) -> std::io::Result<TreeLine> { + pub fn to_tree_line(&self, bid: BId, con: &AppContext) -> std::io::Result<TreeLine> { let has_error = self.has_error; let line_type = TreeLineType::new(&self.path, &self.file_type); let unlisted = if let Some(children) = &self.children { @@ -122,6 +144,8 @@ impl BLine { }); Ok(TreeLine { + bid, + parent_bid: self.parent_id, left_branchs: vec![false; self.depth as usize].into_boxed_slice(), depth: self.depth, icon, diff --git a/src/tree_build/builder.rs b/src/tree_build/builder.rs index d96039a..f5e8752 100644 --- a/src/tree_build/builder.rs +++ b/src/tree_build/builder.rs @@ -155,7 +155,7 @@ impl<'c> TreeBuilder<'c> { } } } - if file_type.is_file() || file_type.is_symlink() { + if file_type.is_file() { if !has_match { return None; } @@ -198,7 +198,7 @@ impl<'c> TreeBuilder<'c> { /// returns true when there are direct matches among children fn load_children(&mut self, bid: BId) -> bool { let mut has_child_match = false; - match fs::read_dir(&self.blines[bid].path) { + match self.blines[bid].read_dir() { Ok(entries) => { let mut children: Vec<BId> = Vec::new(); let child_depth = self.blines[bid].depth + 1; @@ -395,10 +395,10 @@ impl<'c> TreeBuilder<'c> { for id in out_blines.iter() { if self.blines[*id].has_match { // we need to count the children, so we load them - if self.blines[*id].file_type.is_dir() && self.blines[*id].children.is_none() { + if self.blines[*id].can_enter() && self.blines[*id].children.is_none() { self.load_children(*id); } - if let Ok(tree_line) = self.blines[*id].to_tree_line(self.con) { + if let Ok(tree_line) = self.blines[*id].to_tree_line(*id, self.con) { lines.push(tree_line); } else { // I guess the file went missing during tree computation diff --git a/src/tree_build/mod.rs b/src/tree_build/mod.rs index f854800..b1e0677 100644 --- a/src/tree_build/mod.rs +++ b/src/tree_build/mod.rs @@ -2,4 +2,7 @@ mod bid; mod bline; mod builder; -pub use builder::TreeBuilder; +pub use { + builder::TreeBuilder, + bid::BId, +}; |