mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-17 17:55:11 +00:00
Merge pull request #653 from ariasuni/fix-gitignore-option
Use git2 instead of parsing .gitignore for --git-ignore
This commit is contained in:
commit
1fe06a7682
21
src/exa.rs
21
src/exa.rs
@ -11,7 +11,6 @@ use ansi_term::{ANSIStrings, Style};
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::fs::{Dir, File};
|
use crate::fs::{Dir, File};
|
||||||
use crate::fs::feature::ignore::IgnoreCache;
|
|
||||||
use crate::fs::feature::git::GitCache;
|
use crate::fs::feature::git::GitCache;
|
||||||
use crate::options::{Options, Vars};
|
use crate::options::{Options, Vars};
|
||||||
pub use crate::options::vars;
|
pub use crate::options::vars;
|
||||||
@ -44,10 +43,6 @@ pub struct Exa<'args, 'w, W: Write + 'w> {
|
|||||||
/// This has to last the lifetime of the program, because the user might
|
/// This has to last the lifetime of the program, because the user might
|
||||||
/// want to list several directories in the same repository.
|
/// want to list several directories in the same repository.
|
||||||
pub git: Option<GitCache>,
|
pub git: Option<GitCache>,
|
||||||
|
|
||||||
/// A cache of git-ignored files.
|
|
||||||
/// This lasts the lifetime of the program too, for the same reason.
|
|
||||||
pub ignore: Option<IgnoreCache>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The “real” environment variables type.
|
/// The “real” environment variables type.
|
||||||
@ -71,15 +66,6 @@ fn git_options(options: &Options, args: &[&OsStr]) -> Option<GitCache> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_cache(options: &Options) -> Option<IgnoreCache> {
|
|
||||||
use crate::fs::filter::GitIgnore;
|
|
||||||
|
|
||||||
match options.filter.git_ignore {
|
|
||||||
GitIgnore::CheckAndIgnore => Some(IgnoreCache::new()),
|
|
||||||
GitIgnore::Off => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
||||||
pub fn from_args<I>(args: I, writer: &'w mut W) -> Result<Exa<'args, 'w, W>, Misfire>
|
pub fn from_args<I>(args: I, writer: &'w mut W) -> Result<Exa<'args, 'w, W>, Misfire>
|
||||||
where I: Iterator<Item=&'args OsString> {
|
where I: Iterator<Item=&'args OsString> {
|
||||||
@ -95,8 +81,7 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let git = git_options(&options, &args);
|
let git = git_options(&options, &args);
|
||||||
let ignore = ignore_cache(&options);
|
Exa { options, writer, args, git }
|
||||||
Exa { options, writer, args, git, ignore }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +142,7 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
for file in dir.files(self.options.filter.dot_filter, self.ignore.as_ref()) {
|
for file in dir.files(self.options.filter.dot_filter, self.git.as_ref()) {
|
||||||
match file {
|
match file {
|
||||||
Ok(file) => children.push(file),
|
Ok(file) => children.push(file),
|
||||||
Err((path, e)) => writeln!(stderr(), "[{}: {}]", path.display(), e)?,
|
Err((path, e)) => writeln!(stderr(), "[{}: {}]", path.display(), e)?,
|
||||||
@ -217,7 +202,7 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
|||||||
let recurse = self.options.dir_action.recurse_options();
|
let recurse = self.options.dir_action.recurse_options();
|
||||||
|
|
||||||
let r = details::Render { dir, files, colours, style, opts, filter, recurse };
|
let r = details::Render { dir, files, colours, style, opts, filter, recurse };
|
||||||
r.render(self.git.as_ref(), self.ignore.as_ref(), self.writer)
|
r.render(self.git.as_ref(), self.writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
Mode::GridDetails(ref opts) => {
|
Mode::GridDetails(ref opts) => {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use crate::fs::feature::git::GitCache;
|
||||||
|
use crate::fs::fields::GitStatus;
|
||||||
use std::io::{self, Result as IOResult};
|
use std::io::{self, Result as IOResult};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -6,7 +8,6 @@ use std::slice::Iter as SliceIter;
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
use crate::fs::feature::ignore::IgnoreCache;
|
|
||||||
|
|
||||||
|
|
||||||
/// A **Dir** provides a cached list of the file paths in a directory that's
|
/// A **Dir** provides a cached list of the file paths in a directory that's
|
||||||
@ -46,15 +47,13 @@ impl Dir {
|
|||||||
|
|
||||||
/// Produce an iterator of IO results of trying to read all the files in
|
/// Produce an iterator of IO results of trying to read all the files in
|
||||||
/// this directory.
|
/// this directory.
|
||||||
pub fn files<'dir, 'ig>(&'dir self, dots: DotFilter, ignore: Option<&'ig IgnoreCache>) -> Files<'dir, 'ig> {
|
pub fn files<'dir, 'ig>(&'dir self, dots: DotFilter, git: Option<&'ig GitCache>) -> Files<'dir, 'ig> {
|
||||||
if let Some(i) = ignore { i.discover_underneath(&self.path); }
|
|
||||||
|
|
||||||
Files {
|
Files {
|
||||||
inner: self.contents.iter(),
|
inner: self.contents.iter(),
|
||||||
dir: self,
|
dir: self,
|
||||||
dotfiles: dots.shows_dotfiles(),
|
dotfiles: dots.shows_dotfiles(),
|
||||||
dots: dots.dots(),
|
dots: dots.dots(),
|
||||||
ignore,
|
git,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +85,7 @@ pub struct Files<'dir, 'ig> {
|
|||||||
/// any files have been listed.
|
/// any files have been listed.
|
||||||
dots: DotsNext,
|
dots: DotsNext,
|
||||||
|
|
||||||
ignore: Option<&'ig IgnoreCache>,
|
git: Option<&'ig GitCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dir, 'ig> Files<'dir, 'ig> {
|
impl<'dir, 'ig> Files<'dir, 'ig> {
|
||||||
@ -107,8 +106,9 @@ impl<'dir, 'ig> Files<'dir, 'ig> {
|
|||||||
let filename = File::filename(path);
|
let filename = File::filename(path);
|
||||||
if !self.dotfiles && filename.starts_with('.') { continue }
|
if !self.dotfiles && filename.starts_with('.') { continue }
|
||||||
|
|
||||||
if let Some(i) = self.ignore {
|
let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default();
|
||||||
if i.is_ignored(path) { continue }
|
if git_status.unstaged == GitStatus::Ignored {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(File::from_args(path.clone(), self.dir, filename)
|
return Some(File::from_args(path.clone(), self.dir, filename)
|
||||||
|
@ -1,198 +0,0 @@
|
|||||||
//! Ignoring globs in `.gitignore` files.
|
|
||||||
//!
|
|
||||||
//! This uses a cache because the file with the globs in might not be the same
|
|
||||||
//! directory that we’re listing!
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::sync::RwLock;
|
|
||||||
|
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
use crate::fs::filter::IgnorePatterns;
|
|
||||||
|
|
||||||
|
|
||||||
/// An **ignore cache** holds sets of glob patterns paired with the
|
|
||||||
/// directories that they should be ignored underneath. Believe it or not,
|
|
||||||
/// that’s a valid English sentence.
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct IgnoreCache {
|
|
||||||
entries: RwLock<Vec<(PathBuf, IgnorePatterns)>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IgnoreCache {
|
|
||||||
pub fn new() -> IgnoreCache {
|
|
||||||
IgnoreCache::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn discover_underneath(&self, path: &Path) {
|
|
||||||
let mut path = Some(path);
|
|
||||||
let mut entries = self.entries.write().unwrap();
|
|
||||||
|
|
||||||
while let Some(p) = path {
|
|
||||||
if p.components().next().is_none() { break }
|
|
||||||
|
|
||||||
let ignore_file = p.join(".gitignore");
|
|
||||||
if ignore_file.is_file() {
|
|
||||||
debug!("Found a .gitignore file: {:?}", ignore_file);
|
|
||||||
if let Ok(mut file) = File::open(ignore_file) {
|
|
||||||
let mut contents = String::new();
|
|
||||||
|
|
||||||
match file.read_to_string(&mut contents) {
|
|
||||||
Ok(_) => {
|
|
||||||
let patterns = file_lines_to_patterns(contents.lines());
|
|
||||||
entries.push((p.into(), patterns));
|
|
||||||
}
|
|
||||||
Err(e) => debug!("Failed to read a .gitignore: {:?}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug!("Found no .gitignore file at {:?}", ignore_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
path = p.parent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_ignored(&self, suspect: &Path) -> bool {
|
|
||||||
let entries = self.entries.read().unwrap();
|
|
||||||
entries.iter().any(|&(ref base_path, ref patterns)| {
|
|
||||||
if let Ok(suffix) = suspect.strip_prefix(&base_path) {
|
|
||||||
patterns.is_ignored_path(suffix)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn file_lines_to_patterns<'a, I>(iter: I) -> IgnorePatterns
|
|
||||||
where I: Iterator<Item=&'a str>
|
|
||||||
{
|
|
||||||
let iter = iter.filter(|el| !el.is_empty());
|
|
||||||
let iter = iter.filter(|el| !el.starts_with('#'));
|
|
||||||
|
|
||||||
// TODO: Figure out if this should trim whitespace or not
|
|
||||||
|
|
||||||
// Errors are currently being ignored... not a good look
|
|
||||||
IgnorePatterns::parse_from_iter(iter).0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_nothing() {
|
|
||||||
use std::iter::empty;
|
|
||||||
let (patterns, _) = IgnorePatterns::parse_from_iter(empty());
|
|
||||||
assert_eq!(patterns, file_lines_to_patterns(empty()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_some_globs() {
|
|
||||||
let stuff = vec![ "*.mp3", "README.md" ];
|
|
||||||
let reals = vec![ "*.mp3", "README.md" ];
|
|
||||||
let (patterns, _) = IgnorePatterns::parse_from_iter(reals.into_iter());
|
|
||||||
assert_eq!(patterns, file_lines_to_patterns(stuff.into_iter()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_some_comments() {
|
|
||||||
let stuff = vec![ "*.mp3", "# I am a comment!", "#", "README.md" ];
|
|
||||||
let reals = vec![ "*.mp3", "README.md" ];
|
|
||||||
let (patterns, _) = IgnorePatterns::parse_from_iter(reals.into_iter());
|
|
||||||
assert_eq!(patterns, file_lines_to_patterns(stuff.into_iter()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_some_blank_lines() {
|
|
||||||
let stuff = vec![ "*.mp3", "", "", "README.md" ];
|
|
||||||
let reals = vec![ "*.mp3", "README.md" ];
|
|
||||||
let (patterns, _) = IgnorePatterns::parse_from_iter(reals.into_iter());
|
|
||||||
assert_eq!(patterns, file_lines_to_patterns(stuff.into_iter()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_some_whitespacey_lines() {
|
|
||||||
let stuff = vec![ " *.mp3", " ", " a ", "README.md " ];
|
|
||||||
let reals = vec![ " *.mp3", " ", " a ", "README.md " ];
|
|
||||||
let (patterns, _) = IgnorePatterns::parse_from_iter(reals.into_iter());
|
|
||||||
assert_eq!(patterns, file_lines_to_patterns(stuff.into_iter()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn test_cache(dir: &'static str, pats: Vec<&str>) -> IgnoreCache {
|
|
||||||
IgnoreCache { entries: RwLock::new(vec![ (dir.into(), IgnorePatterns::parse_from_iter(pats.into_iter()).0) ]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn an_empty_cache_ignores_nothing() {
|
|
||||||
let ignores = IgnoreCache::default();
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("/usr/bin/drinking")));
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("target/debug/exa")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn a_nonempty_cache_ignores_some_things() {
|
|
||||||
let ignores = test_cache("/vagrant", vec![ "target" ]);
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("/vagrant/src")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("/vagrant/target")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ignore_some_globs() {
|
|
||||||
let ignores = test_cache("/vagrant", vec![ "*.ipr", "*.iws", ".docker" ]);
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("/vagrant/exa.ipr")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("/vagrant/exa.iws")));
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("/vagrant/exa.iwiwal")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("/vagrant/.docker")));
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("/vagrant/exa.docker")));
|
|
||||||
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("/srcode/exa.ipr")));
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("/srcode/exa.iws")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test] #[ignore]
|
|
||||||
fn ignore_relatively() {
|
|
||||||
let ignores = test_cache(".", vec![ "target" ]);
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/project/target")));
|
|
||||||
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("./.target")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test] #[ignore]
|
|
||||||
fn ignore_relatively_sometimes() {
|
|
||||||
let ignores = test_cache(".", vec![ "project/target" ]);
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("./target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/project/target")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test] #[ignore]
|
|
||||||
fn ignore_relatively_absolutely() {
|
|
||||||
let ignores = test_cache(".", vec![ "/project/target" ]);
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("./target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/project/target")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test] #[ignore] // not 100% sure if dot works this way...
|
|
||||||
fn ignore_relatively_absolutely_dot() {
|
|
||||||
let ignores = test_cache(".", vec![ "./project/target" ]);
|
|
||||||
assert_eq!(false, ignores.is_ignored(Path::new("./target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/target")));
|
|
||||||
assert_eq!(true, ignores.is_ignored(Path::new("./project/project/project/target")));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
pub mod xattr;
|
pub mod xattr;
|
||||||
pub mod ignore;
|
|
||||||
|
|
||||||
#[cfg(feature="git")] pub mod git;
|
#[cfg(feature="git")] pub mod git;
|
||||||
|
|
||||||
|
@ -177,6 +177,7 @@ pub struct Time {
|
|||||||
/// A file’s status in a Git repository. Whether a file is in a repository or
|
/// A file’s status in a Git repository. Whether a file is in a repository or
|
||||||
/// not is handled by the Git module, rather than having a “null” variant in
|
/// not is handled by the Git module, rather than having a “null” variant in
|
||||||
/// this enum.
|
/// this enum.
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum GitStatus {
|
pub enum GitStatus {
|
||||||
|
|
||||||
/// This file hasn’t changed since the last commit.
|
/// This file hasn’t changed since the last commit.
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
|
|
||||||
use crate::fs::dir_action::DirAction;
|
use crate::fs::dir_action::DirAction;
|
||||||
use crate::fs::filter::FileFilter;
|
use crate::fs::filter::{FileFilter,GitIgnore};
|
||||||
use crate::output::{View, Mode, details, grid_details};
|
use crate::output::{View, Mode, details, grid_details};
|
||||||
|
|
||||||
mod style;
|
mod style;
|
||||||
@ -146,6 +146,10 @@ impl Options {
|
|||||||
/// status column. It’s only worth trying to discover a repository if the
|
/// status column. It’s only worth trying to discover a repository if the
|
||||||
/// results will end up being displayed.
|
/// results will end up being displayed.
|
||||||
pub fn should_scan_for_git(&self) -> bool {
|
pub fn should_scan_for_git(&self) -> bool {
|
||||||
|
if self.filter.git_ignore == GitIgnore::CheckAndIgnore {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
match self.view.mode {
|
match self.view.mode {
|
||||||
Mode::Details(details::Options { table: Some(ref table), .. }) |
|
Mode::Details(details::Options { table: Some(ref table), .. }) |
|
||||||
Mode::GridDetails(grid_details::Options { details: details::Options { table: Some(ref table), .. }, .. }) => table.columns.git,
|
Mode::GridDetails(grid_details::Options { details: details::Options { table: Some(ref table), .. }, .. }) => table.columns.git,
|
||||||
|
@ -70,7 +70,6 @@ use ansi_term::{ANSIGenericString, Style};
|
|||||||
use crate::fs::{Dir, File};
|
use crate::fs::{Dir, File};
|
||||||
use crate::fs::dir_action::RecurseOptions;
|
use crate::fs::dir_action::RecurseOptions;
|
||||||
use crate::fs::filter::FileFilter;
|
use crate::fs::filter::FileFilter;
|
||||||
use crate::fs::feature::ignore::IgnoreCache;
|
|
||||||
use crate::fs::feature::git::GitCache;
|
use crate::fs::feature::git::GitCache;
|
||||||
use crate::fs::feature::xattr::{Attribute, FileAttributes};
|
use crate::fs::feature::xattr::{Attribute, FileAttributes};
|
||||||
use crate::style::Colours;
|
use crate::style::Colours;
|
||||||
@ -149,7 +148,7 @@ impl<'a> AsRef<File<'a>> for Egg<'a> {
|
|||||||
|
|
||||||
|
|
||||||
impl<'a> Render<'a> {
|
impl<'a> Render<'a> {
|
||||||
pub fn render<W: Write>(self, mut git: Option<&'a GitCache>, ignore: Option<&'a IgnoreCache>, w: &mut W) -> IOResult<()> {
|
pub fn render<W: Write>(self, mut git: Option<&'a GitCache>, w: &mut W) -> IOResult<()> {
|
||||||
let mut pool = Pool::new(num_cpus::get() as u32);
|
let mut pool = Pool::new(num_cpus::get() as u32);
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
|
|
||||||
@ -171,14 +170,14 @@ impl<'a> Render<'a> {
|
|||||||
// This is weird, but I can’t find a way around it:
|
// This is weird, but I can’t find a way around it:
|
||||||
// https://internals.rust-lang.org/t/should-option-mut-t-implement-copy/3715/6
|
// https://internals.rust-lang.org/t/should-option-mut-t-implement-copy/3715/6
|
||||||
let mut table = Some(table);
|
let mut table = Some(table);
|
||||||
self.add_files_to_table(&mut pool, &mut table, &mut rows, &self.files, ignore, TreeDepth::root());
|
self.add_files_to_table(&mut pool, &mut table, &mut rows, &self.files, git, TreeDepth::root());
|
||||||
|
|
||||||
for row in self.iterate_with_table(table.unwrap(), rows) {
|
for row in self.iterate_with_table(table.unwrap(), rows) {
|
||||||
writeln!(w, "{}", row.strings())?
|
writeln!(w, "{}", row.strings())?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.add_files_to_table(&mut pool, &mut None, &mut rows, &self.files, ignore, TreeDepth::root());
|
self.add_files_to_table(&mut pool, &mut None, &mut rows, &self.files, git, TreeDepth::root());
|
||||||
|
|
||||||
for row in self.iterate(rows) {
|
for row in self.iterate(rows) {
|
||||||
writeln!(w, "{}", row.strings())?
|
writeln!(w, "{}", row.strings())?
|
||||||
@ -190,7 +189,7 @@ impl<'a> Render<'a> {
|
|||||||
|
|
||||||
/// Adds files to the table, possibly recursively. This is easily
|
/// Adds files to the table, possibly recursively. This is easily
|
||||||
/// parallelisable, and uses a pool of threads.
|
/// parallelisable, and uses a pool of threads.
|
||||||
fn add_files_to_table<'dir, 'ig>(&self, pool: &mut Pool, table: &mut Option<Table<'a>>, rows: &mut Vec<Row>, src: &[File<'dir>], ignore: Option<&'ig IgnoreCache>, depth: TreeDepth) {
|
fn add_files_to_table<'dir, 'ig>(&self, pool: &mut Pool, table: &mut Option<Table<'a>>, rows: &mut Vec<Row>, src: &[File<'dir>], git: Option<&'ig GitCache>, depth: TreeDepth) {
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use log::error;
|
use log::error;
|
||||||
use crate::fs::feature::xattr;
|
use crate::fs::feature::xattr;
|
||||||
@ -304,7 +303,7 @@ impl<'a> Render<'a> {
|
|||||||
rows.push(row);
|
rows.push(row);
|
||||||
|
|
||||||
if let Some(ref dir) = egg.dir {
|
if let Some(ref dir) = egg.dir {
|
||||||
for file_to_add in dir.files(self.filter.dot_filter, ignore) {
|
for file_to_add in dir.files(self.filter.dot_filter, git) {
|
||||||
match file_to_add {
|
match file_to_add {
|
||||||
Ok(f) => files.push(f),
|
Ok(f) => files.push(f),
|
||||||
Err((path, e)) => errors.push((e, Some(path)))
|
Err((path, e)) => errors.push((e, Some(path)))
|
||||||
@ -322,7 +321,7 @@ impl<'a> Render<'a> {
|
|||||||
rows.push(self.render_error(&error, TreeParams::new(depth.deeper(), false), path));
|
rows.push(self.render_error(&error, TreeParams::new(depth.deeper(), false), path));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.add_files_to_table(pool, table, rows, &files, ignore, depth.deeper());
|
self.add_files_to_table(pool, table, rows, &files, git, depth.deeper());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ impl<'a> Render<'a> {
|
|||||||
write!(w, "{}", grid.fit_into_columns(width))
|
write!(w, "{}", grid.fit_into_columns(width))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.give_up().render(git, None, w)
|
self.give_up().render(git, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user