mirror of
https://github.com/Llewellynvdm/exa.git
synced 2025-02-09 05:58:25 +00:00
List files and directories separately
This finally fixes the issue where trying to list a file causes a crash. Also, tidy up some of the uses of references.
This commit is contained in:
parent
c7417e0bca
commit
cbd2f1fa37
@ -24,7 +24,7 @@ impl<'a> Dir<'a> {
|
|||||||
let mut files = vec![];
|
let mut files = vec![];
|
||||||
|
|
||||||
for path in self.contents.iter() {
|
for path in self.contents.iter() {
|
||||||
match File::from_path(path, self) {
|
match File::from_path(path.clone(), Some(self)) {
|
||||||
Ok(file) => files.push(file),
|
Ok(file) => files.push(file),
|
||||||
Err(e) => println!("{}: {}", path.display(), e),
|
Err(e) => println!("{}: {}", path.display(), e),
|
||||||
}
|
}
|
||||||
|
46
src/exa.rs
46
src/exa.rs
@ -5,6 +5,8 @@ extern crate ansi_term;
|
|||||||
extern crate unicode;
|
extern crate unicode;
|
||||||
|
|
||||||
use std::os;
|
use std::os;
|
||||||
|
use std::io::fs;
|
||||||
|
use std::io::FileType::TypeDirectory;
|
||||||
|
|
||||||
use file::File;
|
use file::File;
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
@ -34,14 +36,24 @@ fn main() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exa(opts: &Options) {
|
fn exa(opts: &Options) {
|
||||||
let mut first = true;
|
|
||||||
|
|
||||||
// It's only worth printing out directory names if the user supplied
|
// It's only worth printing out directory names if the user supplied
|
||||||
// more than one of them.
|
// more than one of them.
|
||||||
let print_dir_names = opts.dirs.len() > 1;
|
let print_dir_names = opts.path_strs.len() > 1;
|
||||||
|
let (dir_strs, file_strs) = opts.path_strs.clone().partition(|n| fs::stat(&Path::new(n)).unwrap().kind == TypeDirectory);
|
||||||
|
|
||||||
for dir_name in opts.dirs.clone().into_iter() {
|
let mut first = file_strs.is_empty();
|
||||||
|
let mut files = vec![];
|
||||||
|
for f in file_strs.iter() {
|
||||||
|
match File::from_path(Path::new(f), None) {
|
||||||
|
Ok(file) => files.push(file),
|
||||||
|
Err(e) => println!("{}: {}", f, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view(opts, files);
|
||||||
|
|
||||||
|
for dir_name in dir_strs.into_iter() {
|
||||||
if first {
|
if first {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@ -52,17 +64,13 @@ fn exa(opts: &Options) {
|
|||||||
match Dir::readdir(Path::new(dir_name.clone())) {
|
match Dir::readdir(Path::new(dir_name.clone())) {
|
||||||
Ok(dir) => {
|
Ok(dir) => {
|
||||||
let unsorted_files = dir.files();
|
let unsorted_files = dir.files();
|
||||||
let files: Vec<&File> = opts.transform_files(&unsorted_files);
|
let files: Vec<File> = opts.transform_files(unsorted_files);
|
||||||
|
|
||||||
if print_dir_names {
|
if print_dir_names {
|
||||||
println!("{}:", dir_name);
|
println!("{}:", dir_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
match opts.view {
|
view(opts, files);
|
||||||
View::Details(ref cols) => details_view(opts, cols, files),
|
|
||||||
View::Lines => lines_view(files),
|
|
||||||
View::Grid(across, width) => grid_view(across, width, files),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}: {}", dir_name, e);
|
println!("{}: {}", dir_name, e);
|
||||||
@ -72,13 +80,21 @@ fn exa(opts: &Options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lines_view(files: Vec<&File>) {
|
fn view(options: &Options, files: Vec<File>) {
|
||||||
|
match options.view {
|
||||||
|
View::Details(ref cols) => details_view(options, cols, files),
|
||||||
|
View::Lines => lines_view(files),
|
||||||
|
View::Grid(across, width) => grid_view(across, width, files),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lines_view(files: Vec<File>) {
|
||||||
for file in files.iter() {
|
for file in files.iter() {
|
||||||
println!("{}", file.file_name());
|
println!("{}", file.file_name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grid_view(across: bool, console_width: uint, files: Vec<&File>) {
|
fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
|
||||||
let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0);
|
let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0);
|
||||||
let num_columns = (console_width + 1) / (max_column_length + 1);
|
let num_columns = (console_width + 1) / (max_column_length + 1);
|
||||||
let count = files.len();
|
let count = files.len();
|
||||||
@ -101,7 +117,7 @@ fn grid_view(across: bool, console_width: uint, files: Vec<&File>) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = files[num];
|
let ref file = files[num];
|
||||||
let file_name = file.name.clone();
|
let file_name = file.name.clone();
|
||||||
let styled_name = file.file_colour().paint(file_name.as_slice());
|
let styled_name = file.file_colour().paint(file_name.as_slice());
|
||||||
if x == num_columns - 1 {
|
if x == num_columns - 1 {
|
||||||
@ -115,7 +131,7 @@ fn grid_view(across: bool, console_width: uint, files: Vec<&File>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn details_view(options: &Options, columns: &Vec<Column>, files: Vec<&File>) {
|
fn details_view(options: &Options, columns: &Vec<Column>, files: Vec<File>) {
|
||||||
// The output gets formatted into columns, which looks nicer. To
|
// The output gets formatted into columns, which looks nicer. To
|
||||||
// do this, we have to write the results into a table, instead of
|
// do this, we have to write the results into a table, instead of
|
||||||
// displaying each file immediately, then calculating the maximum
|
// displaying each file immediately, then calculating the maximum
|
||||||
|
19
src/file.rs
19
src/file.rs
@ -23,15 +23,15 @@ pub static GREY: Colour = Fixed(244);
|
|||||||
|
|
||||||
pub struct File<'a> {
|
pub struct File<'a> {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub dir: &'a Dir<'a>,
|
pub dir: Option<&'a Dir<'a>>,
|
||||||
pub ext: Option<String>,
|
pub ext: Option<String>,
|
||||||
pub path: &'a Path,
|
pub path: Path,
|
||||||
pub stat: io::FileStat,
|
pub stat: io::FileStat,
|
||||||
pub parts: Vec<SortPart>,
|
pub parts: Vec<SortPart>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> File<'a> {
|
impl<'a> File<'a> {
|
||||||
pub fn from_path(path: &'a Path, parent: &'a Dir) -> IoResult<File<'a>> {
|
pub fn from_path(path: Path, parent: Option<&'a Dir<'a>>) -> IoResult<File<'a>> {
|
||||||
let v = path.filename().unwrap(); // fails if / or . or ..
|
let v = path.filename().unwrap(); // fails if / or . or ..
|
||||||
let filename = String::from_utf8(v.to_vec()).unwrap_or_else(|_| panic!("Name was not valid UTF-8"));
|
let filename = String::from_utf8(v.to_vec()).unwrap_or_else(|_| panic!("Name was not valid UTF-8"));
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ impl<'a> File<'a> {
|
|||||||
// symbolic links. Otherwise, the stat() call will fail if it
|
// symbolic links. Otherwise, the stat() call will fail if it
|
||||||
// encounters a link that's target is non-existent.
|
// encounters a link that's target is non-existent.
|
||||||
|
|
||||||
fs::lstat(path).map(|stat| File {
|
fs::lstat(&path).map(|stat| File {
|
||||||
path: path,
|
path: path.clone(),
|
||||||
dir: parent,
|
dir: parent,
|
||||||
stat: stat,
|
stat: stat,
|
||||||
name: filename.clone(),
|
name: filename.clone(),
|
||||||
@ -158,9 +158,12 @@ impl<'a> File<'a> {
|
|||||||
let name = self.name.as_slice();
|
let name = self.name.as_slice();
|
||||||
let displayed_name = self.file_colour().paint(name);
|
let displayed_name = self.file_colour().paint(name);
|
||||||
if self.stat.kind == io::TypeSymlink {
|
if self.stat.kind == io::TypeSymlink {
|
||||||
match fs::readlink(self.path) {
|
match fs::readlink(&self.path) {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
let target_path = if path.is_absolute() { path } else { self.dir.path.join(path) };
|
let target_path = match self.dir {
|
||||||
|
Some(dir) => dir.path.join(path),
|
||||||
|
None => path,
|
||||||
|
};
|
||||||
format!("{} {}", displayed_name, self.target_file_name_and_arrow(target_path))
|
format!("{} {}", displayed_name, self.target_file_name_and_arrow(target_path))
|
||||||
}
|
}
|
||||||
Err(_) => displayed_name,
|
Err(_) => displayed_name,
|
||||||
@ -180,7 +183,7 @@ impl<'a> File<'a> {
|
|||||||
let filename = String::from_utf8_lossy(v).to_string();
|
let filename = String::from_utf8_lossy(v).to_string();
|
||||||
|
|
||||||
let link_target = fs::stat(&target_path).map(|stat| File {
|
let link_target = fs::stat(&target_path).map(|stat| File {
|
||||||
path: &target_path,
|
path: target_path.clone(),
|
||||||
dir: self.dir,
|
dir: self.dir,
|
||||||
stat: stat,
|
stat: stat,
|
||||||
name: filename.clone(),
|
name: filename.clone(),
|
||||||
|
@ -126,7 +126,7 @@ impl<'a> HasType for File<'a> {
|
|||||||
if source_files.len() == 0 {
|
if source_files.len() == 0 {
|
||||||
return Normal;
|
return Normal;
|
||||||
}
|
}
|
||||||
else if source_files.iter().any(|path| self.dir.contains(path)) {
|
else if source_files.iter().any(|path| self.dir.map(|d| d.contains(path)).unwrap_or(false)) {
|
||||||
return Temp;
|
return Temp;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -31,12 +31,12 @@ pub enum View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
pub header: bool,
|
||||||
|
pub path_strs: Vec<String>,
|
||||||
|
pub reverse: bool,
|
||||||
pub show_invisibles: bool,
|
pub show_invisibles: bool,
|
||||||
pub sort_field: SortField,
|
pub sort_field: SortField,
|
||||||
pub reverse: bool,
|
|
||||||
pub dirs: Vec<String>,
|
|
||||||
pub view: View,
|
pub view: View,
|
||||||
pub header: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -60,11 +60,11 @@ impl Options {
|
|||||||
match getopts::getopts(args.tail(), opts) {
|
match getopts::getopts(args.tail(), opts) {
|
||||||
Err(f) => Err(f),
|
Err(f) => Err(f),
|
||||||
Ok(ref matches) => Ok(Options {
|
Ok(ref matches) => Ok(Options {
|
||||||
show_invisibles: matches.opt_present("all"),
|
|
||||||
reverse: matches.opt_present("reverse"),
|
|
||||||
header: matches.opt_present("header"),
|
header: matches.opt_present("header"),
|
||||||
|
path_strs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
|
||||||
|
reverse: matches.opt_present("reverse"),
|
||||||
|
show_invisibles: matches.opt_present("all"),
|
||||||
sort_field: matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(SortField::Name),
|
sort_field: matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(SortField::Name),
|
||||||
dirs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
|
|
||||||
view: Options::view(matches),
|
view: Options::view(matches),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -111,8 +111,7 @@ impl Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
columns.push(FileName);
|
columns.push(FileName);
|
||||||
|
columns
|
||||||
return columns;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_display(&self, f: &File) -> bool {
|
fn should_display(&self, f: &File) -> bool {
|
||||||
@ -124,9 +123,9 @@ impl Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_files<'a>(&self, unordered_files: &'a Vec<File<'a>>) -> Vec<&'a File<'a>> {
|
pub fn transform_files<'a>(&self, unordered_files: Vec<File<'a>>) -> Vec<File<'a>> {
|
||||||
let mut files: Vec<&'a File<'a>> = unordered_files.iter()
|
let mut files: Vec<File<'a>> = unordered_files.into_iter()
|
||||||
.filter(|&f| self.should_display(f))
|
.filter(|f| self.should_display(f))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match self.sort_field {
|
match self.sort_field {
|
||||||
@ -145,6 +144,6 @@ impl Options {
|
|||||||
files.reverse();
|
files.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user