summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2021-09-17 21:08:55 +0200
committerCanop <cano.petrole@gmail.com>2021-09-17 21:08:55 +0200
commit21671ef69a92e562ccbf5a2521243663819e065c (patch)
treed17e75bd47cd818518525b2bca7188f661655abe /src
parentb45bb00dbb4b1df306faa32cd5d943bdd75f0610 (diff)
fix special-path/Enter for sym links
Fix #448
Diffstat (limited to 'src')
-rw-r--r--src/display/displayable_tree.rs2
-rw-r--r--src/tree/tree.rs70
-rw-r--r--src/tree/tree_line.rs54
-rw-r--r--src/tree_build/bid.rs6
-rw-r--r--src/tree_build/bline.rs32
-rw-r--r--src/tree_build/builder.rs8
-rw-r--r--src/tree_build/mod.rs5
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,
+};