提交 2a9b6fe9 编写于 作者: B Ben S

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.
上级 7deb0864
......@@ -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)))
}
}
\ No newline at end of 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
......
#![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
......
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();
filter.transform_files(&mut files);
self.add_files_to_table(table, &files, depth + 1);
match file.this {
Some(Ok(ref dir)) => {
let mut files = Vec::new();
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();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册