Skip to content

Commit

Permalink
Fix search broken on root
Browse files Browse the repository at this point in the history
Fix #904
  • Loading branch information
Canop committed Jul 16, 2024
1 parent efa073c commit 5574397
Showing 1 changed file with 116 additions and 92 deletions.
208 changes: 116 additions & 92 deletions src/tree_build/builder.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
use {
super::{
bid::{BId, SortableBId},
BuildReport,
bid::{
BId,
SortableBId,
},
bline::BLine,
BuildReport,
},
crate::{
app::AppContext,
errors::TreeBuildError,
git::{GitIgnoreChain, GitIgnorer, LineStatusComputer},
pattern::Candidate,
git::{
GitIgnoreChain,
GitIgnorer,
LineStatusComputer,
},
path::Directive,
task_sync::ComputationResult,
task_sync::Dam,
pattern::Candidate,
task_sync::{
ComputationResult,
Dam,
},
tree::*,
},
git2::Repository,
id_arena::Arena,
std::{
collections::{BinaryHeap, VecDeque},
fs,
path::{
PathBuf,
collections::{
BinaryHeap,
VecDeque,
},
fs,
path::PathBuf,
result::Result,
time::{Duration, Instant},
time::{
Duration,
Instant,
},
},
};

Expand Down Expand Up @@ -71,19 +84,14 @@ pub struct TreeBuilder<'c> {
report: BuildReport,
}
impl<'c> TreeBuilder<'c> {

pub fn from(
path: PathBuf,
options: TreeOptions,
targeted_size: usize,
con: &'c AppContext,
) -> Result<TreeBuilder<'c>, TreeBuildError> {
let mut blines = Arena::new();
let subpath_offset = path
.as_os_str()
.as_bytes()
.len()
+ 1; // +1 for the separator
let subpath_offset = path.components().count();
let mut git_ignorer = time!(GitIgnorer::default());
let root_ignore_chain = git_ignorer.root_chain(&path);
let line_status_computer = if options.filter_by_git_status || options.show_git_file_info {
Expand All @@ -97,7 +105,11 @@ impl<'c> TreeBuilder<'c> {
None
};
let root_id = BLine::from_root(&mut blines, path, root_ignore_chain, &options)?;
let trim_root = match (options.trim_root, options.pattern.is_some(), options.sort.prevent_deep_display()) {
let trim_root = match (
options.trim_root,
options.pattern.is_some(),
options.sort.prevent_deep_display(),
) {
// we never want to trim the root if there's a sort
(_, _, true) => false,
// if the user don't want root trimming, we don't trim
Expand Down Expand Up @@ -164,27 +176,25 @@ impl<'c> TreeBuilder<'c> {
return None;
}
};

let subpath_bytes = &path
.as_os_str()
.as_bytes()[self.subpath_offset..];
let subpath = unsafe {
std::str::from_utf8_unchecked(subpath_bytes)
};
let subpath = path
.components()
.skip(self.subpath_offset)
.collect::<PathBuf>();
let candidate = Candidate {
name,
subpath,
subpath: &subpath.to_string_lossy(),
path: &path,
regular_file: file_type.is_file(),
};
let direct_match = if let Some(pattern_score) = self.options.pattern.pattern.score_of(candidate) {
// we dope direct matches to compensate for depth doping of parent folders
score += pattern_score + 10;
true
} else {
has_match = false;
false
};
let direct_match =
if let Some(pattern_score) = self.options.pattern.pattern.score_of(candidate) {
// we dope direct matches to compensate for depth doping of parent folders
score += pattern_score + 10;
true
} else {
has_match = false;
false
};
if has_match && self.options.filter_by_git_status {
if let Some(line_status_computer) = &self.line_status_computer {
if !line_status_computer.is_interesting(&path) {
Expand Down Expand Up @@ -239,7 +249,10 @@ impl<'c> TreeBuilder<'c> {
/// Fill the bline's children vec of blines.
///
/// Return true when there are direct matches among children
fn load_children(&mut self, bid: BId) -> bool {
fn load_children(
&mut self,
bid: BId,
) -> bool {
let mut has_child_match = false;
match self.blines[bid].read_dir() {
Ok(entries) => {
Expand Down Expand Up @@ -285,7 +298,10 @@ impl<'c> TreeBuilder<'c> {

/// return the next child.
/// load_children must have been called before on parent_id
fn next_child(&mut self, parent_id: BId) -> Option<BId> {
fn next_child(
&mut self,
parent_id: BId,
) -> Option<BId> {
let bline = &mut self.blines[parent_id];
if let Some(children) = &bline.children {
if bline.next_child_idx < children.len() {
Expand All @@ -303,7 +319,11 @@ impl<'c> TreeBuilder<'c> {
/// first step of the build: we explore the directories and gather lines.
/// If there's no search pattern we stop when we have enough lines to fill the screen.
/// If there's a pattern, we try to gather more lines that will be sorted afterwards.
fn gather_lines(&mut self, total_search: bool, dam: &Dam) -> Result<Vec<BId>, TreeBuildError> {
fn gather_lines(
&mut self,
total_search: bool,
dam: &Dam,
) -> Result<Vec<BId>, TreeBuildError> {
let start = Instant::now();
let mut out_blines: Vec<BId> = Vec::new(); // the blines we want to display
let optimal_size = if self.options.pattern.pattern.has_real_scores() {
Expand All @@ -319,16 +339,16 @@ impl<'c> TreeBuilder<'c> {
open_dirs.push_back(self.root_id);
let deep = self.deep && self.options.show_tree && !self.options.sort.prevent_deep_display();
loop {
if !total_search && (
(nb_lines_ok > optimal_size)
|| (nb_lines_ok >= self.targeted_size && start.elapsed() > NOT_LONG)
) {
if !total_search
&& ((nb_lines_ok > optimal_size)
|| (nb_lines_ok >= self.targeted_size && start.elapsed() > NOT_LONG))
{
self.total_search = false;
break;
}
if let Some(max) = self.matches_max {
if nb_lines_ok > max {
return Err(TreeBuildError::TooManyMatches{max});
return Err(TreeBuildError::TooManyMatches { max });
}
}
if let Some(open_dir_id) = open_dirs.pop_front() {
Expand Down Expand Up @@ -381,7 +401,7 @@ impl<'c> TreeBuilder<'c> {
}
if let Some(max) = self.matches_max {
if nb_lines_ok > max {
return Err(TreeBuildError::TooManyMatches{max});
return Err(TreeBuildError::TooManyMatches { max });
}
}
if !self.trim_root {
Expand All @@ -399,7 +419,10 @@ impl<'c> TreeBuilder<'c> {
/// strictly necessary to fill the screen.
/// This function keeps only the best ones while taking care of not
/// removing a parent before its children.
fn trim_excess(&mut self, out_blines: &[BId]) {
fn trim_excess(
&mut self,
out_blines: &[BId],
) {
let mut count = 1;
for id in out_blines[1..].iter() {
if self.blines[*id].has_match {
Expand All @@ -413,9 +436,10 @@ impl<'c> TreeBuilder<'c> {
let mut remove_queue: BinaryHeap<SortableBId> = BinaryHeap::new();
for id in out_blines[1..].iter() {
let bline = &self.blines[*id];
if bline.has_match && bline.nb_kept_children == 0 && (bline.depth > 1 || self.trim_root)
if bline.has_match
&& bline.nb_kept_children == 0
&& (bline.depth > 1 || self.trim_root)
{
//debug!("in list: {:?} score: {}", &bline.path, bline.score);
remove_queue.push(SortableBId {
id: *id,
score: bline.score,
Expand Down Expand Up @@ -449,40 +473,38 @@ impl<'c> TreeBuilder<'c> {
) -> Result<TreeLine, TreeBuildError> {
let bline = &self.blines[bid];
let line_type = TreeLineType::new(&bline.path, &bline.file_type);
let unlisted = bline.children.as_ref()
let unlisted = bline
.children
.as_ref()
.map_or(0, |children| children.len() - bline.next_child_idx);
let metadata = fs::symlink_metadata(&bline.path)
.map_err(|_| TreeBuildError::FileNotFound {
let metadata =
fs::symlink_metadata(&bline.path).map_err(|_| TreeBuildError::FileNotFound {
path: bline.path.to_string_lossy().to_string(),
})?;
let subpath_bytes = bline.path.as_os_str().as_bytes();
let subpath = if self.subpath_offset > subpath_bytes.len() {
// This should be the root line
let subpath = if bline.depth == 0 {
bline.path.to_string_lossy().to_string()
} else {
String::from_utf8(subpath_bytes[self.subpath_offset..].to_vec())
.map_err(|_| TreeBuildError::InvalidUtf8 {
path: bline.path.to_string_lossy().to_string(),
})?
bline
.path
.components()
.skip(self.subpath_offset)
.map(|c| c.as_os_str())
.collect::<PathBuf>()
.to_string_lossy()
.to_string()
};
let name = bline
.path
.file_name()
.and_then(|os_str| os_str.to_str())
.unwrap_or("")
.replace('\n', "");
let icon = self.con.icons.as_ref()
.map(|icon_plugin| {
let extension = TreeLine::extension_from_name(&name);
let double_extension = extension
.and_then(|_| TreeLine::double_extension_from_name(&name));
icon_plugin.get_icon(
&line_type,
&name,
double_extension,
extension,
)
});
let icon = self.con.icons.as_ref().map(|icon_plugin| {
let extension = TreeLine::extension_from_name(&name);
let double_extension =
extension.and_then(|_| TreeLine::double_extension_from_name(&name));
icon_plugin.get_icon(&line_type, &name, double_extension, extension)
});

Ok(TreeLine {
bid,
Expand All @@ -503,12 +525,13 @@ impl<'c> TreeBuilder<'c> {
metadata,
git_status: None,
})


}

/// make a tree from the builder's specific structure
fn take_as_tree(mut self, out_blines: &[BId]) -> Tree {
fn take_as_tree(
mut self,
out_blines: &[BId],
) -> Tree {
let mut lines: Vec<TreeLine> = Vec::new();
for id in out_blines.iter() {
if self.blines[*id].has_match {
Expand Down Expand Up @@ -554,34 +577,35 @@ impl<'c> TreeBuilder<'c> {
///
/// Return None if the lifetime expires before end of computation
/// (usually because the user hit a key)
pub fn build_tree(mut self, total_search: bool, dam: &Dam) -> Result<Tree, TreeBuildError> {
self.gather_lines(total_search, dam)
.map(|blines_ids| {
debug!("blines before trimming: {}", blines_ids.len());
if !self.total_search {
self.trim_excess(&blines_ids);
}
self.take_as_tree(&blines_ids)
})
pub fn build_tree(
mut self,
total_search: bool,
dam: &Dam,
) -> Result<Tree, TreeBuildError> {
self.gather_lines(total_search, dam).map(|blines_ids| {
debug!("blines before trimming: {}", blines_ids.len());
if !self.total_search {
self.trim_excess(&blines_ids);
}
self.take_as_tree(&blines_ids)
})
}

pub fn build_paths<F>(
mut self,
total_search: bool,
dam: &Dam,
filter: F
filter: F,
) -> Result<Vec<PathBuf>, TreeBuildError>
where F: Fn(&BLine) -> bool
where
F: Fn(&BLine) -> bool,
{
self.gather_lines(total_search, dam)
.map(|mut blines_ids| {
blines_ids
.drain(..)
.filter(|&bid| {
self.blines[bid].direct_match && filter(&self.blines[bid])
})
.map(|id| self.blines[id].path.clone())
.collect()
})
self.gather_lines(total_search, dam).map(|mut blines_ids| {
blines_ids
.drain(..)
.filter(|&bid| self.blines[bid].direct_match && filter(&self.blines[bid]))
.map(|id| self.blines[id].path.clone())
.collect()
})
}
}

0 comments on commit 5574397

Please sign in to comment.