mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-26 22:06:26 +00:00
Merge branch 'long-grid-view'
This commit is contained in:
commit
090cebe669
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -13,7 +13,7 @@ dependencies = [
|
|||||||
"num_cpus 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"number_prefix 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"number_prefix 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"term_grid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"term_grid 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-width 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"users 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"users 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -71,7 +71,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "git2"
|
name = "git2"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
source = "git+https://github.com/alexcrichton/git2-rs.git#e5a439b13f45ca6b95fbf5f47ccf4b030d37ed1c"
|
source = "git+https://github.com/alexcrichton/git2-rs.git#3a7a990607a766fa65a40b920d70c8289691d2f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -87,12 +87,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
source = "git+https://github.com/alexcrichton/git2-rs.git#e5a439b13f45ca6b95fbf5f47ccf4b030d37ed1c"
|
source = "git+https://github.com/alexcrichton/git2-rs.git#3a7a990607a766fa65a40b920d70c8289691d2f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libssh2-sys 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libssh2-sys 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libz-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libz-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl-sys 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libz-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libz-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl-sys 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.6.2"
|
version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -262,7 +262,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term_grid"
|
name = "term_grid"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-width 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::iter::repeat;
|
|
||||||
|
|
||||||
use ansi_term::Style;
|
use ansi_term::Style;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
@ -58,37 +56,37 @@ impl Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pad a string with the given number of spaces.
|
|
||||||
fn spaces(length: usize) -> String {
|
|
||||||
repeat(" ").take(length).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Alignment {
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
/// Pad a string with the given alignment and number of spaces.
|
|
||||||
///
|
|
||||||
/// This doesn't take the width the string *should* be, rather the number
|
|
||||||
/// of spaces to add: this is because the strings are usually full of
|
|
||||||
/// invisible control characters, so getting the displayed width of the
|
|
||||||
/// string is not as simple as just getting its length.
|
|
||||||
pub fn pad_string(&self, string: &str, padding: usize) -> String {
|
|
||||||
match *self {
|
|
||||||
Alignment::Left => format!("{}{}", string, spaces(padding)),
|
|
||||||
Alignment::Right => format!("{}{}", spaces(padding), string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
pub struct Cell {
|
pub struct Cell {
|
||||||
pub length: usize,
|
pub length: usize,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cell {
|
impl Cell {
|
||||||
|
pub fn empty() -> Cell {
|
||||||
|
Cell {
|
||||||
|
text: String::new(),
|
||||||
|
length: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn paint(style: Style, string: &str) -> Cell {
|
pub fn paint(style: Style, string: &str) -> Cell {
|
||||||
Cell {
|
Cell {
|
||||||
text: style.paint(string).to_string(),
|
text: style.paint(string).to_string(),
|
||||||
length: UnicodeWidthStr::width(string),
|
length: UnicodeWidthStr::width(string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_spaces(&mut self, count: usize) {
|
||||||
|
self.length += count;
|
||||||
|
for _ in 0 .. count {
|
||||||
|
self.text.push(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, other: &Cell) {
|
||||||
|
self.length += other.length;
|
||||||
|
self.text.push_str(&*other.text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,9 +179,10 @@ impl<'dir> Exa<'dir> {
|
|||||||
|
|
||||||
fn print(&self, dir: Option<&Dir>, files: &[File]) {
|
fn print(&self, dir: Option<&Dir>, files: &[File]) {
|
||||||
match self.options.view {
|
match self.options.view {
|
||||||
View::Grid(g) => g.view(files),
|
View::Grid(g) => g.view(files),
|
||||||
View::Details(d) => d.view(dir, files),
|
View::Details(d) => d.view(dir, files),
|
||||||
View::Lines(l) => l.view(files),
|
View::GridDetails(gd) => gd.view(dir, files),
|
||||||
|
View::Lines(l) => l.view(files),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
136
src/options.rs
136
src/options.rs
@ -13,7 +13,7 @@ use column::Column::*;
|
|||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
use feature::Attribute;
|
use feature::Attribute;
|
||||||
use file::File;
|
use file::File;
|
||||||
use output::{Grid, Details, Lines};
|
use output::{Grid, Details, GridDetails, Lines};
|
||||||
use term::dimensions;
|
use term::dimensions;
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ impl Options {
|
|||||||
opts.optflag("B", "bytes", "list file sizes in bytes, without prefixes");
|
opts.optflag("B", "bytes", "list file sizes in bytes, without prefixes");
|
||||||
opts.optflag("d", "list-dirs", "list directories as regular files");
|
opts.optflag("d", "list-dirs", "list directories as regular files");
|
||||||
opts.optflag("g", "group", "show group as well as user");
|
opts.optflag("g", "group", "show group as well as user");
|
||||||
|
opts.optflag("G", "grid", "display entries in a grid view (default)");
|
||||||
opts.optflag("", "group-directories-first", "list directories before other files");
|
opts.optflag("", "group-directories-first", "list directories before other files");
|
||||||
opts.optflag("h", "header", "show a header row at the top");
|
opts.optflag("h", "header", "show a header row at the top");
|
||||||
opts.optflag("H", "links", "show number of hard links");
|
opts.optflag("H", "links", "show number of hard links");
|
||||||
@ -248,16 +249,17 @@ impl fmt::Display for Misfire {
|
|||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
pub enum View {
|
pub enum View {
|
||||||
Details(Details),
|
Details(Details),
|
||||||
Lines(Lines),
|
|
||||||
Grid(Grid),
|
Grid(Grid),
|
||||||
|
GridDetails(GridDetails),
|
||||||
|
Lines(Lines),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
pub fn deduce(matches: &getopts::Matches, filter: FileFilter, dir_action: DirAction) -> Result<View, Misfire> {
|
pub fn deduce(matches: &getopts::Matches, filter: FileFilter, dir_action: DirAction) -> Result<View, Misfire> {
|
||||||
use self::Misfire::*;
|
use self::Misfire::*;
|
||||||
|
|
||||||
if matches.opt_present("long") {
|
let long = || {
|
||||||
if matches.opt_present("across") {
|
if matches.opt_present("across") && !matches.opt_present("grid") {
|
||||||
Err(Useless("across", true, "long"))
|
Err(Useless("across", true, "long"))
|
||||||
}
|
}
|
||||||
else if matches.opt_present("oneline") {
|
else if matches.opt_present("oneline") {
|
||||||
@ -272,78 +274,85 @@ impl View {
|
|||||||
colours: if dimensions().is_some() { Colours::colourful() } else { Colours::plain() },
|
colours: if dimensions().is_some() { Colours::colourful() } else { Colours::plain() },
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(View::Details(details))
|
Ok(details)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
else if matches.opt_present("binary") {
|
|
||||||
Err(Useless("binary", false, "long"))
|
let long_options_scan = || {
|
||||||
}
|
for option in &[ "binary", "bytes", "inode", "links", "header", "blocks", "time", "tree", "group" ] {
|
||||||
else if matches.opt_present("bytes") {
|
if matches.opt_present(option) {
|
||||||
Err(Useless("bytes", false, "long"))
|
return Err(Useless(option, false, "long"));
|
||||||
}
|
}
|
||||||
else if matches.opt_present("inode") {
|
}
|
||||||
Err(Useless("inode", false, "long"))
|
|
||||||
}
|
if cfg!(feature="git") && matches.opt_present("git") {
|
||||||
else if matches.opt_present("links") {
|
Err(Useless("git", false, "long"))
|
||||||
Err(Useless("links", false, "long"))
|
}
|
||||||
}
|
else if matches.opt_present("level") && !matches.opt_present("recurse") {
|
||||||
else if matches.opt_present("header") {
|
Err(Useless2("level", "recurse", "tree"))
|
||||||
Err(Useless("header", false, "long"))
|
}
|
||||||
}
|
else if Attribute::feature_implemented() && matches.opt_present("extended") {
|
||||||
else if matches.opt_present("blocks") {
|
Err(Useless("extended", false, "long"))
|
||||||
Err(Useless("blocks", false, "long"))
|
}
|
||||||
}
|
else {
|
||||||
else if cfg!(feature="git") && matches.opt_present("git") {
|
Ok(())
|
||||||
Err(Useless("git", false, "long"))
|
}
|
||||||
}
|
};
|
||||||
else if matches.opt_present("time") {
|
|
||||||
Err(Useless("time", false, "long"))
|
let other_options_scan = || {
|
||||||
}
|
if let Some((width, _)) = dimensions() {
|
||||||
else if matches.opt_present("tree") {
|
if matches.opt_present("oneline") {
|
||||||
Err(Useless("tree", false, "long"))
|
if matches.opt_present("across") {
|
||||||
}
|
Err(Useless("across", true, "oneline"))
|
||||||
else if matches.opt_present("group") {
|
}
|
||||||
Err(Useless("group", false, "long"))
|
else {
|
||||||
}
|
let lines = Lines {
|
||||||
else if matches.opt_present("level") && !matches.opt_present("recurse") {
|
colours: Colours::colourful(),
|
||||||
Err(Useless2("level", "recurse", "tree"))
|
};
|
||||||
}
|
|
||||||
else if Attribute::feature_implemented() && matches.opt_present("extended") {
|
Ok(View::Lines(lines))
|
||||||
Err(Useless("extended", false, "long"))
|
}
|
||||||
}
|
|
||||||
else if let Some((width, _)) = dimensions() {
|
|
||||||
if matches.opt_present("oneline") {
|
|
||||||
if matches.opt_present("across") {
|
|
||||||
Err(Useless("across", true, "oneline"))
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let lines = Lines {
|
let grid = Grid {
|
||||||
colours: Colours::colourful(),
|
across: matches.opt_present("across"),
|
||||||
|
console_width: width,
|
||||||
|
colours: Colours::colourful(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(View::Lines(lines))
|
Ok(View::Grid(grid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let grid = Grid {
|
// If the terminal width couldn't be matched for some reason, such
|
||||||
across: matches.opt_present("across"),
|
// as the program's stdout being connected to a file, then
|
||||||
console_width: width,
|
// fallback to the lines view.
|
||||||
colours: Colours::colourful(),
|
let lines = Lines {
|
||||||
|
colours: Colours::plain(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(View::Grid(grid))
|
Ok(View::Lines(lines))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches.opt_present("long") {
|
||||||
|
let long_options = try!(long());
|
||||||
|
|
||||||
|
if matches.opt_present("grid") {
|
||||||
|
match other_options_scan() {
|
||||||
|
Ok(View::Grid(grid)) => return Ok(View::GridDetails(GridDetails { grid: grid, details: long_options })),
|
||||||
|
Ok(lines) => return Ok(lines),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Ok(View::Details(long_options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// If the terminal width couldn't be matched for some reason, such
|
|
||||||
// as the program's stdout being connected to a file, then
|
|
||||||
// fallback to the lines view.
|
|
||||||
let lines = Lines {
|
|
||||||
colours: Colours::plain(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(View::Lines(lines))
|
try!(long_options_scan());
|
||||||
}
|
|
||||||
|
other_options_scan()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,5 +727,4 @@ mod test {
|
|||||||
let opts = Options::getopts(&[ "--level".to_string(), "69105".to_string() ]);
|
let opts = Options::getopts(&[ "--level".to_string(), "69105".to_string() ]);
|
||||||
assert_eq!(opts.unwrap_err(), Misfire::Useless2("level", "recurse", "tree"))
|
assert_eq!(opts.unwrap_err(), Misfire::Useless2("level", "recurse", "tree"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use std::iter::repeat;
|
||||||
|
use std::string::ToString;
|
||||||
|
|
||||||
use colours::Colours;
|
use colours::Colours;
|
||||||
use column::{Alignment, Column, Cell};
|
use column::{Alignment, Column, Cell};
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
@ -66,14 +69,16 @@ impl Details {
|
|||||||
|
|
||||||
// Then add files to the table and print it out.
|
// Then add files to the table and print it out.
|
||||||
self.add_files_to_table(&mut table, files, 0);
|
self.add_files_to_table(&mut table, files, 0);
|
||||||
table.print_table(self.xattr, self.recurse.is_some());
|
for cell in table.print_table(self.xattr, self.recurse.is_some()) {
|
||||||
|
println!("{}", cell.text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds files to the table - recursively, if the `recurse` option
|
/// Adds files to the table - recursively, if the `recurse` option
|
||||||
/// is present.
|
/// is present.
|
||||||
fn add_files_to_table<U: Users>(&self, table: &mut Table<U>, src: &[File], depth: usize) {
|
fn add_files_to_table<U: Users>(&self, table: &mut Table<U>, src: &[File], depth: usize) {
|
||||||
for (index, file) in src.iter().enumerate() {
|
for (index, file) in src.iter().enumerate() {
|
||||||
table.add_file(file, depth, index == src.len() - 1);
|
table.add_file(file, depth, index == src.len() - 1, true);
|
||||||
|
|
||||||
// There are two types of recursion that exa supports: a tree
|
// There are two types of recursion that exa supports: a tree
|
||||||
// view, which is dealt with here, and multiple listings, which is
|
// view, which is dealt with here, and multiple listings, which is
|
||||||
@ -105,7 +110,7 @@ struct Row {
|
|||||||
|
|
||||||
/// This file's name, in coloured output. The name is treated separately
|
/// This file's name, in coloured output. The name is treated separately
|
||||||
/// from the other cells, as it never requires padding.
|
/// from the other cells, as it never requires padding.
|
||||||
name: String,
|
name: Cell,
|
||||||
|
|
||||||
/// How many directories deep into the tree structure this is. Directories
|
/// How many directories deep into the tree structure this is. Directories
|
||||||
/// on top have depth 0.
|
/// on top have depth 0.
|
||||||
@ -157,7 +162,7 @@ impl Table<OSUsers> {
|
|||||||
|
|
||||||
/// Create a new, empty Table object, setting the caching fields to their
|
/// Create a new, empty Table object, setting the caching fields to their
|
||||||
/// empty states.
|
/// empty states.
|
||||||
fn with_options(colours: Colours, columns: Vec<Column>) -> Table<OSUsers> {
|
pub fn with_options(colours: Colours, columns: Vec<Column>) -> Table<OSUsers> {
|
||||||
Table {
|
Table {
|
||||||
columns: columns,
|
columns: columns,
|
||||||
rows: Vec::new(),
|
rows: Vec::new(),
|
||||||
@ -177,11 +182,11 @@ impl<U> Table<U> where U: Users {
|
|||||||
/// Add a dummy "header" row to the table, which contains the names of all
|
/// Add a dummy "header" row to the table, which contains the names of all
|
||||||
/// the columns, underlined. This has dummy data for the cases that aren't
|
/// the columns, underlined. This has dummy data for the cases that aren't
|
||||||
/// actually used, such as the depth or list of attributes.
|
/// actually used, such as the depth or list of attributes.
|
||||||
fn add_header(&mut self) {
|
pub fn add_header(&mut self) {
|
||||||
let row = Row {
|
let row = Row {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
cells: self.columns.iter().map(|c| Cell::paint(self.colours.header, c.header())).collect(),
|
cells: self.columns.iter().map(|c| Cell::paint(self.colours.header, c.header())).collect(),
|
||||||
name: self.colours.header.paint("Name").to_string(),
|
name: Cell::paint(self.colours.header, "Name"),
|
||||||
last: false,
|
last: false,
|
||||||
attrs: Vec::new(),
|
attrs: Vec::new(),
|
||||||
children: false,
|
children: false,
|
||||||
@ -191,11 +196,16 @@ impl<U> Table<U> where U: Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the cells for the given file, and add the result to the table.
|
/// Get the cells for the given file, and add the result to the table.
|
||||||
fn add_file(&mut self, file: &File, depth: usize, last: bool) {
|
pub fn add_file(&mut self, file: &File, depth: usize, last: bool, links: bool) {
|
||||||
|
let cells = self.cells_for_file(file);
|
||||||
|
self.add_file_with_cells(cells, file, depth, last, links)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file_with_cells(&mut self, cells: Vec<Cell>, file: &File, depth: usize, last: bool, links: bool) {
|
||||||
let row = Row {
|
let row = Row {
|
||||||
depth: depth,
|
depth: depth,
|
||||||
cells: self.cells_for_file(file),
|
cells: cells,
|
||||||
name: filename(file, &self.colours),
|
name: Cell { text: filename(file, &self.colours, links), length: file.file_name_width() },
|
||||||
last: last,
|
last: last,
|
||||||
attrs: file.xattrs.clone(),
|
attrs: file.xattrs.clone(),
|
||||||
children: file.this.is_some(),
|
children: file.this.is_some(),
|
||||||
@ -206,7 +216,7 @@ impl<U> Table<U> where U: Users {
|
|||||||
|
|
||||||
/// Use the list of columns to find which cells should be produced for
|
/// Use the list of columns to find which cells should be produced for
|
||||||
/// this file, per-column.
|
/// this file, per-column.
|
||||||
fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
|
pub fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
|
||||||
self.columns.clone().iter()
|
self.columns.clone().iter()
|
||||||
.map(|c| self.display(file, c))
|
.map(|c| self.display(file, c))
|
||||||
.collect()
|
.collect()
|
||||||
@ -373,8 +383,9 @@ impl<U> Table<U> where U: Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Print the table to standard output, consuming it in the process.
|
/// Print the table to standard output, consuming it in the process.
|
||||||
fn print_table(self, xattr: bool, show_children: bool) {
|
pub fn print_table(&self, xattr: bool, show_children: bool) -> Vec<Cell> {
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
|
let mut cells = Vec::new();
|
||||||
|
|
||||||
// Work out the list of column widths by finding the longest cell for
|
// Work out the list of column widths by finding the longest cell for
|
||||||
// each column, then formatting each cell in that column to be the
|
// each column, then formatting each cell in that column to be the
|
||||||
@ -383,12 +394,21 @@ impl<U> Table<U> where U: Users {
|
|||||||
.map(|n| self.rows.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
|
.map(|n| self.rows.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for row in self.rows.into_iter() {
|
for row in self.rows.iter() {
|
||||||
|
let mut cell = Cell::empty();
|
||||||
|
|
||||||
for (n, width) in column_widths.iter().enumerate() {
|
for (n, width) in column_widths.iter().enumerate() {
|
||||||
let padding = width - row.cells[n].length;
|
match self.columns[n].alignment() {
|
||||||
print!("{} ", self.columns[n].alignment().pad_string(&row.cells[n].text, padding));
|
Alignment::Left => { cell.append(&row.cells[n]); cell.add_spaces(width - row.cells[n].length); }
|
||||||
|
Alignment::Right => { cell.add_spaces(width - row.cells[n].length); cell.append(&row.cells[n]); }
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.add_spaces(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut filename = String::new();
|
||||||
|
let mut filename_length = 0;
|
||||||
|
|
||||||
// A stack tracks which tree characters should be printed. It's
|
// A stack tracks which tree characters should be printed. It's
|
||||||
// necessary to maintain information about the previously-printed
|
// necessary to maintain information about the previously-printed
|
||||||
// lines, as the output will change based on whether the
|
// lines, as the output will change based on whether the
|
||||||
@ -398,7 +418,8 @@ impl<U> Table<U> where U: Users {
|
|||||||
stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge };
|
stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge };
|
||||||
|
|
||||||
for i in 1 .. row.depth + 1 {
|
for i in 1 .. row.depth + 1 {
|
||||||
print!("{}", self.colours.punctuation.paint(stack[i].ascii_art()));
|
filename.push_str(&*self.colours.punctuation.paint(stack[i].ascii_art()).to_string());
|
||||||
|
filename_length += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if row.children {
|
if row.children {
|
||||||
@ -408,24 +429,29 @@ impl<U> Table<U> where U: Users {
|
|||||||
// If any tree characters have been printed, then add an extra
|
// If any tree characters have been printed, then add an extra
|
||||||
// space, which makes the output look much better.
|
// space, which makes the output look much better.
|
||||||
if row.depth != 0 {
|
if row.depth != 0 {
|
||||||
print!(" ");
|
filename.push(' ');
|
||||||
|
filename_length += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the name without worrying about padding.
|
// Print the name without worrying about padding.
|
||||||
print!("{}\n", row.name);
|
filename.push_str(&*row.name.text);
|
||||||
|
filename_length += row.name.length;
|
||||||
|
|
||||||
if xattr {
|
if xattr {
|
||||||
let width = row.attrs.iter().map(|a| a.name().len()).max().unwrap_or(0);
|
let width = row.attrs.iter().map(|a| a.name().len()).max().unwrap_or(0);
|
||||||
for attr in row.attrs.iter() {
|
for attr in row.attrs.iter() {
|
||||||
let name = attr.name();
|
let name = attr.name();
|
||||||
println!("{}\t{}",
|
let spaces: String = repeat(" ").take(width - name.len()).collect();
|
||||||
Alignment::Left.pad_string(name, width - name.len()),
|
filename.push_str(&*format!("\n{}{} {}", name, spaces, attr.size()))
|
||||||
attr.size()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell.append(&Cell { text: filename, length: filename_length });
|
||||||
|
cells.push(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cells
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
src/output/grid_details.rs
Normal file
118
src/output/grid_details.rs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
use std::iter::repeat;
|
||||||
|
|
||||||
|
use users::OSUsers;
|
||||||
|
use term_grid as grid;
|
||||||
|
|
||||||
|
use column::{Column, Cell};
|
||||||
|
use dir::Dir;
|
||||||
|
use file::File;
|
||||||
|
use output::details::{Details, Table};
|
||||||
|
use output::grid::Grid;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
|
pub struct GridDetails {
|
||||||
|
pub grid: Grid,
|
||||||
|
pub details: Details,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GridDetails {
|
||||||
|
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
|
||||||
|
let columns_for_dir = self.details.columns.for_dir(dir);
|
||||||
|
let mut first_table = Table::with_options(self.details.colours, columns_for_dir.clone());
|
||||||
|
let cells: Vec<_> = files.iter().map(|file| first_table.cells_for_file(file)).collect();
|
||||||
|
|
||||||
|
let mut last_working_table = self.make_grid(1, &*columns_for_dir, files, cells.clone());
|
||||||
|
|
||||||
|
for column_count in 2.. {
|
||||||
|
let grid = self.make_grid(column_count, &*columns_for_dir, files, cells.clone());
|
||||||
|
|
||||||
|
let the_grid_fits = {
|
||||||
|
let d = grid.fit_into_columns(column_count);
|
||||||
|
d.is_complete() && d.width() <= self.grid.console_width
|
||||||
|
};
|
||||||
|
|
||||||
|
if the_grid_fits {
|
||||||
|
last_working_table = grid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print!("{}", last_working_table.fit_into_columns(column_count - 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_table(&self, columns_for_dir: &[Column]) -> Table<OSUsers> {
|
||||||
|
let mut table = Table::with_options(self.details.colours, columns_for_dir.into());
|
||||||
|
if self.details.header { table.add_header() }
|
||||||
|
table
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], files: &[File], cells: Vec<Vec<Cell>>) -> grid::Grid {
|
||||||
|
let mut tables: Vec<_> = repeat(()).map(|_| self.make_table(columns_for_dir)).take(column_count).collect();
|
||||||
|
|
||||||
|
let mut num_cells = cells.len();
|
||||||
|
if self.details.header {
|
||||||
|
num_cells += column_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
let original_height = divide_rounding_up(cells.len(), column_count);
|
||||||
|
let height = divide_rounding_up(num_cells, column_count);
|
||||||
|
|
||||||
|
for (i, (file, row)) in files.iter().zip(cells.into_iter()).enumerate() {
|
||||||
|
let index = if self.grid.across {
|
||||||
|
i % column_count
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i / original_height
|
||||||
|
};
|
||||||
|
|
||||||
|
tables[index].add_file_with_cells(row, file, 0, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let columns: Vec<_> = tables.iter().map(|t| t.print_table(false, false)).collect();
|
||||||
|
|
||||||
|
let direction = if self.grid.across { grid::Direction::LeftToRight }
|
||||||
|
else { grid::Direction::TopToBottom };
|
||||||
|
|
||||||
|
let mut grid = grid::Grid::new(grid::GridOptions {
|
||||||
|
direction: direction,
|
||||||
|
separator_width: 4,
|
||||||
|
});
|
||||||
|
|
||||||
|
if self.grid.across {
|
||||||
|
for row in 0 .. height {
|
||||||
|
for column in columns.iter() {
|
||||||
|
if row < column.len() {
|
||||||
|
let cell = grid::Cell {
|
||||||
|
contents: column[row].text.clone(),
|
||||||
|
width: column[row].length,
|
||||||
|
};
|
||||||
|
|
||||||
|
grid.add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for column in columns.iter() {
|
||||||
|
for cell in column.iter() {
|
||||||
|
let cell = grid::Cell {
|
||||||
|
contents: cell.text.clone(),
|
||||||
|
width: cell.length,
|
||||||
|
};
|
||||||
|
|
||||||
|
grid.add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn divide_rounding_up(a: usize, b: usize) -> usize {
|
||||||
|
let mut result = a / b;
|
||||||
|
if a % b != 0 { result += 1; }
|
||||||
|
result
|
||||||
|
}
|
@ -13,7 +13,7 @@ pub struct Lines {
|
|||||||
impl Lines {
|
impl Lines {
|
||||||
pub fn view(&self, files: &[File]) {
|
pub fn view(&self, files: &[File]) {
|
||||||
for file in files {
|
for file in files {
|
||||||
println!("{}", filename(file, &self.colours));
|
println!("{}", filename(file, &self.colours, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,15 @@ use filetype::file_colour;
|
|||||||
pub use self::details::Details;
|
pub use self::details::Details;
|
||||||
pub use self::grid::Grid;
|
pub use self::grid::Grid;
|
||||||
pub use self::lines::Lines;
|
pub use self::lines::Lines;
|
||||||
|
pub use self::grid_details::GridDetails;
|
||||||
|
|
||||||
mod grid;
|
mod grid;
|
||||||
pub mod details;
|
pub mod details;
|
||||||
mod lines;
|
mod lines;
|
||||||
|
mod grid_details;
|
||||||
|
|
||||||
|
pub fn filename(file: &File, colours: &Colours, links: bool) -> String {
|
||||||
pub fn filename(file: &File, colours: &Colours) -> String {
|
if links && file.is_link() {
|
||||||
if file.is_link() {
|
|
||||||
symlink_filename(file, colours)
|
symlink_filename(file, colours)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Loading…
Reference in New Issue
Block a user