mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-09-28 05:09:01 +00:00
Merge branch 'tree'
This commit is contained in:
commit
d5aa3208b6
@ -5,7 +5,6 @@ use ansi_term::Style;
|
|||||||
#[derive(PartialEq, Debug, Copy)]
|
#[derive(PartialEq, Debug, Copy)]
|
||||||
pub enum Column {
|
pub enum Column {
|
||||||
Permissions,
|
Permissions,
|
||||||
FileName,
|
|
||||||
FileSize(SizeFormat),
|
FileSize(SizeFormat),
|
||||||
Blocks,
|
Blocks,
|
||||||
User,
|
User,
|
||||||
@ -49,7 +48,6 @@ impl Column {
|
|||||||
pub fn header(&self) -> &'static str {
|
pub fn header(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Column::Permissions => "Permissions",
|
Column::Permissions => "Permissions",
|
||||||
Column::FileName => "Name",
|
|
||||||
Column::FileSize(_) => "Size",
|
Column::FileSize(_) => "Size",
|
||||||
Column::Blocks => "Blocks",
|
Column::Blocks => "Blocks",
|
||||||
Column::User => "User",
|
Column::User => "User",
|
||||||
|
@ -31,11 +31,14 @@ impl Dir {
|
|||||||
|
|
||||||
/// Produce a vector of File objects from an initialised directory,
|
/// Produce a vector of File objects from an initialised directory,
|
||||||
/// printing out an error if any of the Files fail to be created.
|
/// printing out an error if any of the Files fail to be created.
|
||||||
pub fn files(&self) -> Vec<File> {
|
///
|
||||||
|
/// Passing in `recurse` means that any directories will be scanned for
|
||||||
|
/// their contents, as well.
|
||||||
|
pub fn files(&self, recurse: bool) -> Vec<File> {
|
||||||
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, Some(self)) {
|
match File::from_path(path, Some(self), recurse) {
|
||||||
Ok(file) => files.push(file),
|
Ok(file) => files.push(file),
|
||||||
Err(e) => println!("{}: {}", path.display(), e),
|
Err(e) => println!("{}: {}", path.display(), e),
|
||||||
}
|
}
|
||||||
|
41
src/file.rs
41
src/file.rs
@ -32,6 +32,7 @@ pub struct File<'a> {
|
|||||||
pub ext: Option<String>,
|
pub ext: Option<String>,
|
||||||
pub path: Path,
|
pub path: Path,
|
||||||
pub stat: io::FileStat,
|
pub stat: io::FileStat,
|
||||||
|
pub this: Option<Dir>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> File<'a> {
|
impl<'a> File<'a> {
|
||||||
@ -39,12 +40,12 @@ impl<'a> File<'a> {
|
|||||||
/// appropriate. Paths specified directly on the command-line have no Dirs.
|
/// appropriate. Paths specified directly on the command-line have no Dirs.
|
||||||
///
|
///
|
||||||
/// This uses lstat instead of stat, which doesn't follow symbolic links.
|
/// This uses lstat instead of stat, which doesn't follow symbolic links.
|
||||||
pub fn from_path(path: &Path, parent: Option<&'a Dir>) -> IoResult<File<'a>> {
|
pub fn from_path(path: &Path, parent: Option<&'a Dir>, recurse: bool) -> IoResult<File<'a>> {
|
||||||
fs::lstat(path).map(|stat| File::with_stat(stat, path, parent))
|
fs::lstat(path).map(|stat| File::with_stat(stat, path, parent, recurse))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new File object from the given Stat result, and other data.
|
/// Create a new File object from the given Stat result, and other data.
|
||||||
pub fn with_stat(stat: io::FileStat, path: &Path, parent: Option<&'a Dir>) -> File<'a> {
|
pub fn with_stat(stat: io::FileStat, path: &Path, parent: Option<&'a Dir>, recurse: bool) -> File<'a> {
|
||||||
|
|
||||||
// The filename to display is the last component of the path. However,
|
// The filename to display is the last component of the path. However,
|
||||||
// the path has no components for `.`, `..`, and `/`, so in these
|
// the path has no components for `.`, `..`, and `/`, so in these
|
||||||
@ -58,12 +59,23 @@ impl<'a> File<'a> {
|
|||||||
// replacement characters.
|
// replacement characters.
|
||||||
let filename = String::from_utf8_lossy(bytes);
|
let filename = String::from_utf8_lossy(bytes);
|
||||||
|
|
||||||
|
// If we are recursing, then the `this` field contains a Dir object
|
||||||
|
// that represents the current File as a directory, if it is a
|
||||||
|
// directory. This is used for the --tree option.
|
||||||
|
let this = if recurse && stat.kind == io::FileType::Directory {
|
||||||
|
Dir::readdir(path).ok()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
File {
|
File {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
dir: parent,
|
dir: parent,
|
||||||
stat: stat,
|
stat: stat,
|
||||||
name: filename.to_string(),
|
name: filename.to_string(),
|
||||||
ext: ext(filename.as_slice()),
|
ext: ext(filename.as_slice()),
|
||||||
|
this: this,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +94,6 @@ impl<'a> File<'a> {
|
|||||||
pub fn display<U: Users>(&self, column: &Column, users_cache: &mut U) -> Cell {
|
pub fn display<U: Users>(&self, column: &Column, users_cache: &mut U) -> Cell {
|
||||||
match *column {
|
match *column {
|
||||||
Permissions => self.permissions_string(),
|
Permissions => self.permissions_string(),
|
||||||
FileName => self.file_name_view(),
|
|
||||||
FileSize(f) => self.file_size(f),
|
FileSize(f) => self.file_size(f),
|
||||||
HardLinks => self.hard_links(),
|
HardLinks => self.hard_links(),
|
||||||
Inode => self.inode(),
|
Inode => self.inode(),
|
||||||
@ -98,15 +109,12 @@ impl<'a> File<'a> {
|
|||||||
///
|
///
|
||||||
/// It consists of the file name coloured in the appropriate style,
|
/// It consists of the file name coloured in the appropriate style,
|
||||||
/// with special formatting for a symlink.
|
/// with special formatting for a symlink.
|
||||||
pub fn file_name_view(&self) -> Cell {
|
pub fn file_name_view(&self) -> String {
|
||||||
if self.stat.kind == io::FileType::Symlink {
|
if self.stat.kind == io::FileType::Symlink {
|
||||||
self.symlink_file_name_view()
|
self.symlink_file_name_view()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Cell {
|
self.file_colour().paint(&*self.name).to_string()
|
||||||
length: 0, // This length is ignored (rightmost column)
|
|
||||||
text: self.file_colour().paint(&*self.name).to_string(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +126,7 @@ impl<'a> File<'a> {
|
|||||||
/// an error, highlight the target and arrow in red. The error would
|
/// an error, highlight the target and arrow in red. The error would
|
||||||
/// be shown out of context, and it's almost always because the
|
/// be shown out of context, and it's almost always because the
|
||||||
/// target doesn't exist.
|
/// target doesn't exist.
|
||||||
fn symlink_file_name_view(&self) -> Cell {
|
fn symlink_file_name_view(&self) -> String {
|
||||||
let name = &*self.name;
|
let name = &*self.name;
|
||||||
let style = self.file_colour();
|
let style = self.file_colour();
|
||||||
|
|
||||||
@ -129,26 +137,20 @@ impl<'a> File<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match self.target_file(&target_path) {
|
match self.target_file(&target_path) {
|
||||||
Ok(file) => Cell {
|
Ok(file) => format!("{} {} {}{}{}",
|
||||||
length: 0, // These lengths are never actually used...
|
|
||||||
text: format!("{} {} {}{}{}",
|
|
||||||
style.paint(name),
|
style.paint(name),
|
||||||
GREY.paint("=>"),
|
GREY.paint("=>"),
|
||||||
Cyan.paint(target_path.dirname_str().unwrap()),
|
Cyan.paint(target_path.dirname_str().unwrap()),
|
||||||
Cyan.paint("/"),
|
Cyan.paint("/"),
|
||||||
file.file_colour().paint(file.name.as_slice())),
|
file.file_colour().paint(file.name.as_slice())),
|
||||||
},
|
Err(filename) => format!("{} {} {}",
|
||||||
Err(filename) => Cell {
|
|
||||||
length: 0, // ...because the rightmost column lengths are ignored!
|
|
||||||
text: format!("{} {} {}",
|
|
||||||
style.paint(name),
|
style.paint(name),
|
||||||
Red.paint("=>"),
|
Red.paint("=>"),
|
||||||
Red.underline().paint(filename.as_slice())),
|
Red.underline().paint(filename.as_slice())),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Cell::paint(style, name)
|
style.paint(name).to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +186,7 @@ impl<'a> File<'a> {
|
|||||||
stat: stat,
|
stat: stat,
|
||||||
name: filename.to_string(),
|
name: filename.to_string(),
|
||||||
ext: ext(filename.as_slice()),
|
ext: ext(filename.as_slice()),
|
||||||
|
this: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
14
src/main.rs
14
src/main.rs
@ -39,11 +39,14 @@ fn exa(options: &Options) {
|
|||||||
let path = Path::new(file);
|
let path = Path::new(file);
|
||||||
match fs::stat(&path) {
|
match fs::stat(&path) {
|
||||||
Ok(stat) => {
|
Ok(stat) => {
|
||||||
if stat.kind == FileType::Directory && options.dir_action != DirAction::AsFile {
|
if stat.kind == FileType::Directory && options.dir_action == DirAction::Tree {
|
||||||
|
files.push(File::with_stat(stat, &path, None, true));
|
||||||
|
}
|
||||||
|
else if stat.kind == FileType::Directory && options.dir_action != DirAction::AsFile {
|
||||||
dirs.push(path);
|
dirs.push(path);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
files.push(File::with_stat(stat, &path, None));
|
files.push(File::with_stat(stat, &path, None, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => println!("{}: {}", file, e),
|
Err(e) => println!("{}: {}", file, e),
|
||||||
@ -55,7 +58,7 @@ fn exa(options: &Options) {
|
|||||||
let mut first = files.is_empty();
|
let mut first = files.is_empty();
|
||||||
|
|
||||||
if !files.is_empty() {
|
if !files.is_empty() {
|
||||||
options.view(None, &files[]);
|
options.view(None, &files[], options.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directories are put on a stack rather than just being iterated through,
|
// Directories are put on a stack rather than just being iterated through,
|
||||||
@ -77,8 +80,7 @@ fn exa(options: &Options) {
|
|||||||
|
|
||||||
match Dir::readdir(&dir_path) {
|
match Dir::readdir(&dir_path) {
|
||||||
Ok(ref dir) => {
|
Ok(ref dir) => {
|
||||||
let unsorted_files = dir.files();
|
let files = options.transform_files(dir.files(false));
|
||||||
let files: Vec<File> = options.transform_files(unsorted_files);
|
|
||||||
|
|
||||||
// When recursing, add any directories to the dirs stack
|
// When recursing, add any directories to the dirs stack
|
||||||
// backwards: the *last* element of the stack is used each
|
// backwards: the *last* element of the stack is used each
|
||||||
@ -95,7 +97,7 @@ fn exa(options: &Options) {
|
|||||||
}
|
}
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
||||||
options.view(Some(dir), &files[]);
|
options.view(Some(dir), &files[], options.filter);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}: {}", dir_path.display(), e);
|
println!("{}: {}", dir_path.display(), e);
|
||||||
|
@ -20,10 +20,15 @@ use self::Misfire::*;
|
|||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub dir_action: DirAction,
|
pub dir_action: DirAction,
|
||||||
pub path_strs: Vec<String>,
|
pub path_strs: Vec<String>,
|
||||||
|
pub filter: FileFilter,
|
||||||
|
view: View,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Copy)]
|
||||||
|
pub struct FileFilter {
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
show_invisibles: bool,
|
show_invisibles: bool,
|
||||||
sort_field: SortField,
|
sort_field: SortField,
|
||||||
view: View,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
@ -45,6 +50,7 @@ impl Options {
|
|||||||
getopts::optflag("R", "recurse", "recurse into directories"),
|
getopts::optflag("R", "recurse", "recurse into directories"),
|
||||||
getopts::optopt ("s", "sort", "field to sort by", "WORD"),
|
getopts::optopt ("s", "sort", "field to sort by", "WORD"),
|
||||||
getopts::optflag("S", "blocks", "show number of file system blocks"),
|
getopts::optflag("S", "blocks", "show number of file system blocks"),
|
||||||
|
getopts::optflag("T", "tree", "recurse into subdirectories in a tree view"),
|
||||||
getopts::optflag("x", "across", "sort multi-column view entries across"),
|
getopts::optflag("x", "across", "sort multi-column view entries across"),
|
||||||
getopts::optflag("?", "help", "show list of command-line options"),
|
getopts::optflag("?", "help", "show list of command-line options"),
|
||||||
];
|
];
|
||||||
@ -66,18 +72,26 @@ impl Options {
|
|||||||
Ok(Options {
|
Ok(Options {
|
||||||
dir_action: try!(dir_action(&matches)),
|
dir_action: try!(dir_action(&matches)),
|
||||||
path_strs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
|
path_strs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
|
||||||
|
view: try!(view(&matches)),
|
||||||
|
filter: FileFilter {
|
||||||
reverse: matches.opt_present("reverse"),
|
reverse: matches.opt_present("reverse"),
|
||||||
show_invisibles: matches.opt_present("all"),
|
show_invisibles: matches.opt_present("all"),
|
||||||
sort_field: sort_field,
|
sort_field: sort_field,
|
||||||
view: try!(view(&matches)),
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Display the files using this Option's View.
|
pub fn transform_files<'a>(&self, files: Vec<File<'a>>) -> Vec<File<'a>> {
|
||||||
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
|
self.filter.transform_files(files)
|
||||||
self.view.view(dir, files)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Display the files using this Option's View.
|
||||||
|
pub fn view(&self, dir: Option<&Dir>, files: &[File], filter: FileFilter) {
|
||||||
|
self.view.view(dir, files, filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileFilter {
|
||||||
/// Transform the files (sorting, reversing, filtering) before listing them.
|
/// Transform the files (sorting, reversing, filtering) before listing them.
|
||||||
pub fn transform_files<'a>(&self, mut files: Vec<File<'a>>) -> Vec<File<'a>> {
|
pub fn transform_files<'a>(&self, mut files: Vec<File<'a>>) -> Vec<File<'a>> {
|
||||||
|
|
||||||
@ -111,7 +125,7 @@ impl Options {
|
|||||||
/// What to do when encountering a directory?
|
/// What to do when encountering a directory?
|
||||||
#[derive(PartialEq, Debug, Copy)]
|
#[derive(PartialEq, Debug, Copy)]
|
||||||
pub enum DirAction {
|
pub enum DirAction {
|
||||||
AsFile, List, Recurse
|
AsFile, List, Recurse, Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
/// User-supplied field to sort by.
|
/// User-supplied field to sort by.
|
||||||
@ -189,7 +203,7 @@ fn view(matches: &getopts::Matches) -> Result<View, Misfire> {
|
|||||||
Err(Misfire::Useless("oneline", true, "long"))
|
Err(Misfire::Useless("oneline", true, "long"))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Ok(View::Details(try!(Columns::new(matches)), matches.opt_present("header")))
|
Ok(View::Details(try!(Columns::new(matches)), matches.opt_present("header"), matches.opt_present("tree")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if matches.opt_present("binary") {
|
else if matches.opt_present("binary") {
|
||||||
@ -242,12 +256,14 @@ fn file_size(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
|
|||||||
fn dir_action(matches: &getopts::Matches) -> Result<DirAction, Misfire> {
|
fn dir_action(matches: &getopts::Matches) -> Result<DirAction, Misfire> {
|
||||||
let recurse = matches.opt_present("recurse");
|
let recurse = matches.opt_present("recurse");
|
||||||
let list = matches.opt_present("list-dirs");
|
let list = matches.opt_present("list-dirs");
|
||||||
|
let tree = matches.opt_present("tree");
|
||||||
|
|
||||||
match (recurse, list) {
|
match (recurse, list, tree) {
|
||||||
(true, true ) => Err(Misfire::Conflict("recurse", "list-dirs")),
|
(true, true, _ ) => Err(Misfire::Conflict("recurse", "list-dirs")),
|
||||||
(true, false) => Ok(DirAction::Recurse),
|
(true, false, false) => Ok(DirAction::Recurse),
|
||||||
(false, true ) => Ok(DirAction::AsFile),
|
(true, false, true ) => Ok(DirAction::Tree),
|
||||||
(false, false) => Ok(DirAction::List),
|
(false, true, _ ) => Ok(DirAction::AsFile),
|
||||||
|
(false, false, _ ) => Ok(DirAction::List),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +320,6 @@ impl Columns {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.push(FileName);
|
|
||||||
columns
|
columns
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,24 @@ use std::iter::{AdditiveIterator, repeat};
|
|||||||
use column::{Column, Cell};
|
use column::{Column, Cell};
|
||||||
use column::Alignment::Left;
|
use column::Alignment::Left;
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
use file::File;
|
use file::{File, GREY};
|
||||||
use options::Columns;
|
use options::{Columns, FileFilter};
|
||||||
use users::OSUsers;
|
use users::OSUsers;
|
||||||
|
|
||||||
use ansi_term::Style::Plain;
|
use ansi_term::Style::Plain;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Debug)]
|
#[derive(PartialEq, Copy, Debug)]
|
||||||
pub enum View {
|
pub enum View {
|
||||||
Details(Columns, bool),
|
Details(Columns, bool, bool),
|
||||||
Lines,
|
Lines,
|
||||||
Grid(bool, usize),
|
Grid(bool, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
|
pub fn view(&self, dir: Option<&Dir>, files: &[File], filter: FileFilter) {
|
||||||
match *self {
|
match *self {
|
||||||
View::Grid(across, width) => grid_view(across, width, files),
|
View::Grid(across, width) => grid_view(across, width, files),
|
||||||
View::Details(ref cols, header) => details_view(&*cols.for_dir(dir), files, header),
|
View::Details(ref cols, header, tree) => details_view(&*cols.for_dir(dir), files, header, tree, filter),
|
||||||
View::Lines => lines_view(files),
|
View::Lines => lines_view(files),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ impl View {
|
|||||||
/// The lines view literally just displays each file, line-by-line.
|
/// The lines view literally just displays each file, line-by-line.
|
||||||
fn lines_view(files: &[File]) {
|
fn lines_view(files: &[File]) {
|
||||||
for file in files.iter() {
|
for file in files.iter() {
|
||||||
println!("{}", file.file_name_view().text);
|
println!("{}", file.file_name_view());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ fn grid_view(across: bool, console_width: usize, files: &[File]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn details_view(columns: &[Column], files: &[File], header: bool) {
|
fn details_view(columns: &[Column], files: &[File], header: bool, tree: bool, filter: FileFilter) {
|
||||||
// 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
|
||||||
@ -131,33 +131,80 @@ fn details_view(columns: &[Column], files: &[File], header: bool) {
|
|||||||
|
|
||||||
let mut cache = OSUsers::empty_cache();
|
let mut cache = OSUsers::empty_cache();
|
||||||
|
|
||||||
let mut table: Vec<Vec<Cell>> = files.iter()
|
let mut table = Vec::new();
|
||||||
.map(|f| columns.iter().map(|c| f.display(c, &mut cache)).collect())
|
get_files(columns, &mut cache, tree, &mut table, files, 0, filter);
|
||||||
.collect();
|
|
||||||
|
|
||||||
if header {
|
if header {
|
||||||
table.insert(0, columns.iter().map(|c| Cell::paint(Plain.underline(), c.header())).collect());
|
let row = Row {
|
||||||
|
depth: 0,
|
||||||
|
cells: columns.iter().map(|c| Cell::paint(Plain.underline(), c.header())).collect(),
|
||||||
|
name: Plain.underline().paint("Name").to_string(),
|
||||||
|
last: false,
|
||||||
|
children: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
table.insert(0, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
let column_widths: Vec<usize> = range(0, columns.len())
|
let column_widths: Vec<usize> = range(0, columns.len())
|
||||||
.map(|n| table.iter().map(|row| row[n].length).max().unwrap_or(0))
|
.map(|n| table.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let mut stack = Vec::new();
|
||||||
|
|
||||||
for row in table.iter() {
|
for row in table.iter() {
|
||||||
for (num, column) in columns.iter().enumerate() {
|
for (num, column) in columns.iter().enumerate() {
|
||||||
if num != 0 {
|
let padding = column_widths[num] - row.cells[num].length;
|
||||||
print!(" "); // Separator
|
print!("{} ", column.alignment().pad_string(&row.cells[num].text, padding));
|
||||||
}
|
}
|
||||||
|
|
||||||
if num == columns.len() - 1 {
|
if tree {
|
||||||
// The final column doesn't need to have trailing spaces
|
stack.resize(row.depth + 1, "├──");
|
||||||
print!("{}", row[num].text);
|
stack[row.depth ] = if row.last { "└──" } else { "├──" };
|
||||||
|
|
||||||
|
for i in range(1, row.depth + 1) {
|
||||||
|
print!("{}", GREY.paint(stack[i ]));
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
let padding = column_widths[num] - row[num].length;
|
if row.children {
|
||||||
print!("{}", column.alignment().pad_string(&row[num].text, padding));
|
stack[row.depth ] = if row.last { " " } else { "│ " };
|
||||||
|
}
|
||||||
|
|
||||||
|
if row.depth != 0 {
|
||||||
|
print!(" ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print!("\n");
|
|
||||||
|
print!("{}\n", row.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_files(columns: &[Column], cache: &mut OSUsers, recurse: bool, dest: &mut Vec<Row>, src: &[File], depth: usize, filter: FileFilter) {
|
||||||
|
for (index, file) in src.iter().enumerate() {
|
||||||
|
|
||||||
|
let row = Row {
|
||||||
|
depth: depth,
|
||||||
|
cells: columns.iter().map(|c| file.display(c, cache)).collect(),
|
||||||
|
name: file.file_name_view(),
|
||||||
|
last: index == src.len() - 1,
|
||||||
|
children: file.this.is_some(),
|
||||||
|
};
|
||||||
|
|
||||||
|
dest.push(row);
|
||||||
|
|
||||||
|
if recurse {
|
||||||
|
if let Some(ref dir) = file.this {
|
||||||
|
let files = filter.transform_files(dir.files(true));
|
||||||
|
get_files(columns, cache, recurse, dest, files.as_slice(), depth + 1, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Row {
|
||||||
|
pub depth: usize,
|
||||||
|
pub cells: Vec<Cell>,
|
||||||
|
pub name: String,
|
||||||
|
pub last: bool,
|
||||||
|
pub children: bool,
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user