Display errors inline in the tree

When tree mode is active, this will print out errors as another form of child node in the tree, instead of in one big block before any output.

The 'this' field now holds the io::Result of the readdir call, rather than only a *successful* result.
This commit is contained in:
Ben S 2015-08-25 15:04:15 +01:00
parent 7deb08644a
commit 2a9b6fe930
4 changed files with 60 additions and 15 deletions

View File

@ -78,9 +78,9 @@ pub struct Files<'dir> {
}
impl<'dir> Iterator for Files<'dir> {
type Item = io::Result<File<'dir>>;
type Item = Result<File<'dir>, (PathBuf, io::Error)>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|path| File::from_path(path, Some(self.dir), self.recurse))
self.inner.next().map(|path| File::from_path(path, Some(self.dir), self.recurse).map_err(|t| (path.clone(), t)))
}
}

View File

@ -27,23 +27,23 @@ use self::fields as f;
pub struct File<'dir> {
/// This file's name, as a UTF-8 encoded String.
pub name: String,
pub name: String,
/// The file's name's extension, if present, extracted from the name. This
/// is queried a lot, so it's worth being cached.
pub ext: Option<String>,
pub ext: Option<String>,
/// The path that begat this file. Even though the file's name is
/// extracted, the path needs to be kept around, as certain operations
/// involve looking up the file's absolute location (such as the Git
/// status, or searching for compiled files).
pub path: PathBuf,
pub path: PathBuf,
/// A cached `metadata` call for this file. This is queried multiple
/// times, and is *not* cached by the OS, as it could easily change
/// between invocations - but exa is so short-lived it's better to just
/// cache it.
pub metadata: fs::Metadata,
pub metadata: fs::Metadata,
/// List of this file's extended attributes. These are only loaded if the
/// `xattr` feature is in use.
@ -57,11 +57,11 @@ pub struct File<'dir> {
/// However, *directories* that get passed in will produce files that
/// contain a reference to it, which is used in certain operations (such
/// as looking up a file's Git status).
pub dir: Option<&'dir Dir>,
pub dir: Option<&'dir Dir>,
/// If this `File` is also a directory, then this field is the same file
/// as a `Dir`.
pub this: Option<Dir>,
pub this: Option<io::Result<Dir>>,
}
impl<'dir> File<'dir> {
@ -82,7 +82,7 @@ impl<'dir> File<'dir> {
// that represents the current File as a directory, if it is a
// directory. This is used for the --tree option.
let this = if recurse && metadata.is_dir() {
Dir::readdir(path, false).ok()
Some(Dir::readdir(path, false))
}
else {
None

View File

@ -1,3 +1,4 @@
#![feature(iter_arith)]
#![feature(convert, fs_mode)]
#![feature(slice_splits, vec_resize)]
@ -146,7 +147,15 @@ impl<'dir> Exa<'dir> {
match Dir::readdir(&dir_path, self.options.should_scan_for_git()) {
Ok(ref dir) => {
let mut files = dir.files(false).flat_map(|f| f).collect();
let mut files = Vec::new();
for file in dir.files(true) {
match file {
Ok(file) => files.push(file),
Err((path, e)) => println!("[{}: {}]", path.display(), e),
}
}
self.options.transform_files(&mut files);
// When recursing, add any directories to the dirs stack

View File

@ -1,4 +1,7 @@
use std::error::Error;
use std::io;
use std::iter::repeat;
use std::path::Path;
use std::string::ToString;
use colours::Colours;
@ -98,11 +101,24 @@ impl Details {
// Use the filter to remove unwanted files *before* expanding
// them, so we don't examine any directories that wouldn't
// have their contents listed anyway.
if let Some(ref dir) = file.this {
let mut files = dir.files(true).flat_map(|f| f).collect();
match file.this {
Some(Ok(ref dir)) => {
let mut files = Vec::new();
filter.transform_files(&mut files);
self.add_files_to_table(table, &files, depth + 1);
for file_to_add in dir.files(true) {
match file_to_add {
Ok(f) => files.push(f),
Err((path, e)) => table.add_error(&e, depth + 1, false, Some(&*path)),
}
}
filter.transform_files(&mut files);
self.add_files_to_table(table, &files, depth + 1);
},
Some(Err(ref e)) => {
table.add_error(e, depth + 1, true, None);
},
None => {},
}
}
}
@ -221,6 +237,24 @@ impl<U> Table<U> where U: Users {
self.rows.push(row);
}
pub fn add_error(&mut self, error: &io::Error, depth: usize, last: bool, path: Option<&Path>) {
let error_message = match path {
Some(path) => format!("<{}: {}>", path.display(), error),
None => format!("<{}>", error),
};
let row = Row {
depth: depth,
cells: None,
name: Cell::paint(self.colours.broken_arrow, &error_message),
last: last,
attrs: Vec::new(),
children: false,
};
self.rows.push(row);
}
/// Get the cells for the given file, and add the result to the table.
pub fn add_file(&mut self, file: &File, depth: usize, last: bool, links: bool) {
let cells = self.cells_for_file(file);
@ -420,6 +454,8 @@ impl<U> Table<U> where U: Users {
.map(|n| self.rows.iter().map(|row| row.column_width(n)).max().unwrap_or(0))
.collect();
let total_width: usize = self.columns.len() + column_widths.iter().sum::<usize>();
for row in self.rows.iter() {
let mut cell = Cell::empty();
@ -434,7 +470,7 @@ impl<U> Table<U> where U: Users {
}
}
else {
cell.add_spaces(column_widths.len())
cell.add_spaces(total_width)
}
let mut filename = String::new();