From 766279b803934522af6570c8e465b0d3959ac903 Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 06:17:00 +0100 Subject: [PATCH 01/10] Versions bump --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fda465..84913f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "git2" 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 = [ "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)", @@ -87,12 +87,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libgit2-sys" 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 = [ "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)", "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)", ] @@ -111,7 +111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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)", - "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)", ] @@ -186,7 +186,7 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", From ccdf9ff4a679e00298f7e0fd9bd68318ded20cb8 Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 13:21:21 +0100 Subject: [PATCH 02/10] Add --grid --long option This commit adds --grid, which, when used with --long, will split the details into multiple columns. Currently this is just 2 columns, but in the future it will be based on the width of the terminal. In order to do this, I had to do two things: 1. Add a `links` parameter to the filename function, which disables the printing of the arrow and link target in the details view. When this is active, the columns get way too large, and it becomes not worth it. 2. Change the `print_table` function from actually printing the table to stdout to returning a list of `Cells` based on the table. This list then gets its width measured to calculate the width of the resulting table. --- src/column.rs | 40 ++++++------ src/main.rs | 7 +- src/options.rs | 127 +++++++++++++++++++------------------ src/output/details.rs | 61 ++++++++++++------ src/output/grid_details.rs | 60 ++++++++++++++++++ src/output/lines.rs | 2 +- src/output/mod.rs | 7 +- 7 files changed, 194 insertions(+), 110 deletions(-) create mode 100644 src/output/grid_details.rs diff --git a/src/column.rs b/src/column.rs index 23e2a4b..6f4a1e7 100644 --- a/src/column.rs +++ b/src/column.rs @@ -1,5 +1,3 @@ -use std::iter::repeat; - use ansi_term::Style; use unicode_width::UnicodeWidthStr; @@ -58,25 +56,6 @@ impl Column { } } -/// Pad a string with the given number of spaces. -fn spaces(length: usize) -> String { - repeat(" ").take(length).collect() -} - -impl Alignment { - /// 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 { @@ -85,10 +64,29 @@ pub struct Cell { } impl Cell { + pub fn empty() -> Cell { + Cell { + text: String::new(), + length: 0, + } + } + pub fn paint(style: Style, string: &str) -> Cell { Cell { text: style.paint(string).to_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); + } } diff --git a/src/main.rs b/src/main.rs index 28042f8..22af23c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -179,9 +179,10 @@ impl<'dir> Exa<'dir> { fn print(&self, dir: Option<&Dir>, files: &[File]) { match self.options.view { - View::Grid(g) => g.view(files), - View::Details(d) => d.view(dir, files), - View::Lines(l) => l.view(files), + View::Grid(g) => g.view(files), + View::Details(d) => d.view(dir, files), + View::GridDetails(gd) => gd.view(dir, files), + View::Lines(l) => l.view(files), } } } diff --git a/src/options.rs b/src/options.rs index f0a49e2..092b6ab 100644 --- a/src/options.rs +++ b/src/options.rs @@ -13,7 +13,7 @@ use column::Column::*; use dir::Dir; use feature::Attribute; use file::File; -use output::{Grid, Details, Lines}; +use output::{Grid, Details, GridDetails, Lines}; use term::dimensions; @@ -37,6 +37,7 @@ impl Options { opts.optflag("B", "bytes", "list file sizes in bytes, without prefixes"); opts.optflag("d", "list-dirs", "list directories as regular files"); 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("h", "header", "show a header row at the top"); opts.optflag("H", "links", "show number of hard links"); @@ -248,15 +249,16 @@ impl fmt::Display for Misfire { #[derive(PartialEq, Debug, Copy, Clone)] pub enum View { Details(Details), - Lines(Lines), Grid(Grid), + GridDetails(GridDetails), + Lines(Lines), } impl View { pub fn deduce(matches: &getopts::Matches, filter: FileFilter, dir_action: DirAction) -> Result { use self::Misfire::*; - if matches.opt_present("long") { + let long = || { if matches.opt_present("across") { Err(Useless("across", true, "long")) } @@ -272,78 +274,79 @@ impl View { 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")) - } - else if matches.opt_present("bytes") { - Err(Useless("bytes", false, "long")) - } - else if matches.opt_present("inode") { - Err(Useless("inode", false, "long")) - } - else if matches.opt_present("links") { - Err(Useless("links", false, "long")) - } - else if matches.opt_present("header") { - Err(Useless("header", false, "long")) - } - else if matches.opt_present("blocks") { - Err(Useless("blocks", false, "long")) - } - else if cfg!(feature="git") && matches.opt_present("git") { - Err(Useless("git", false, "long")) - } - else if matches.opt_present("time") { - Err(Useless("time", false, "long")) - } - else if matches.opt_present("tree") { - Err(Useless("tree", false, "long")) - } - else if matches.opt_present("group") { - Err(Useless("group", false, "long")) - } - else if matches.opt_present("level") && !matches.opt_present("recurse") { - Err(Useless2("level", "recurse", "tree")) - } - else if Attribute::feature_implemented() && matches.opt_present("extended") { - 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")) + }; + + let long_options_scan = || { + for option in &[ "binary", "bytes", "inode", "links", "header", "blocks", "time", "tree", "group" ] { + if matches.opt_present(option) { + return Err(Useless(option, false, "long")); + } + } + + if cfg!(feature="git") && matches.opt_present("git") { + Err(Useless("git", false, "long")) + } + else { + Ok(()) + } + }; + + let other_options_scan = || { + if let Some((width, _)) = dimensions() { + if matches.opt_present("oneline") { + if matches.opt_present("across") { + Err(Useless("across", true, "oneline")) + } + else { + let lines = Lines { + colours: Colours::colourful(), + }; + + Ok(View::Lines(lines)) + } } else { - let lines = Lines { - colours: Colours::colourful(), + let grid = Grid { + across: matches.opt_present("across"), + console_width: width, + colours: Colours::colourful(), }; - Ok(View::Lines(lines)) + Ok(View::Grid(grid)) } } else { - let grid = Grid { - across: matches.opt_present("across"), - console_width: width, - colours: Colours::colourful(), + // 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::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() } } diff --git a/src/output/details.rs b/src/output/details.rs index 84da4de..e67490d 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -1,3 +1,6 @@ +use std::iter::repeat; +use std::string::ToString; + use colours::Colours; use column::{Alignment, Column, Cell}; use dir::Dir; @@ -66,14 +69,16 @@ impl Details { // Then add files to the table and print it out. 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 /// is present. fn add_files_to_table(&self, table: &mut Table, src: &[File], depth: usize) { 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 // 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 /// from the other cells, as it never requires padding. - name: String, + name: Cell, /// How many directories deep into the tree structure this is. Directories /// on top have depth 0. @@ -157,7 +162,7 @@ impl Table { /// Create a new, empty Table object, setting the caching fields to their /// empty states. - fn with_options(colours: Colours, columns: Vec) -> Table { + pub fn with_options(colours: Colours, columns: Vec) -> Table { Table { columns: columns, rows: Vec::new(), @@ -177,11 +182,11 @@ impl Table where U: Users { /// 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 /// actually used, such as the depth or list of attributes. - fn add_header(&mut self) { + pub fn add_header(&mut self) { let row = Row { depth: 0, 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, attrs: Vec::new(), children: false, @@ -191,11 +196,11 @@ impl Table where U: Users { } /// 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 row = Row { depth: depth, cells: self.cells_for_file(file), - name: filename(file, &self.colours), + name: Cell { text: filename(file, &self.colours, links), length: file.file_name_width() }, last: last, attrs: file.xattrs.clone(), children: file.this.is_some(), @@ -206,7 +211,7 @@ impl Table where U: Users { /// Use the list of columns to find which cells should be produced for /// this file, per-column. - fn cells_for_file(&mut self, file: &File) -> Vec { + pub fn cells_for_file(&mut self, file: &File) -> Vec { self.columns.clone().iter() .map(|c| self.display(file, c)) .collect() @@ -373,8 +378,9 @@ impl Table where U: Users { } /// 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 { let mut stack = Vec::new(); + let mut cells = Vec::new(); // 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 @@ -383,12 +389,21 @@ impl Table where U: Users { .map(|n| self.rows.iter().map(|row| row.cells[n].length).max().unwrap_or(0)) .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() { - let padding = width - row.cells[n].length; - print!("{} ", self.columns[n].alignment().pad_string(&row.cells[n].text, padding)); + match self.columns[n].alignment() { + 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 // necessary to maintain information about the previously-printed // lines, as the output will change based on whether the @@ -398,7 +413,8 @@ impl Table where U: Users { stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge }; 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 { @@ -408,24 +424,29 @@ impl Table where U: Users { // If any tree characters have been printed, then add an extra // space, which makes the output look much better. if row.depth != 0 { - print!(" "); + filename.push(' '); + filename_length += 1; } } // Print the name without worrying about padding. - print!("{}\n", row.name); + filename.push_str(&*row.name.text); + filename_length += row.name.length; if xattr { let width = row.attrs.iter().map(|a| a.name().len()).max().unwrap_or(0); for attr in row.attrs.iter() { let name = attr.name(); - println!("{}\t{}", - Alignment::Left.pad_string(name, width - name.len()), - attr.size() - ) + let spaces: String = repeat(" ").take(width - name.len()).collect(); + filename.push_str(&*format!("\n{}{} {}", name, spaces, attr.size())) } } + + cell.append(&Cell { text: filename, length: filename_length }); + cells.push(cell); } + + cells } } diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs new file mode 100644 index 0000000..93e498a --- /dev/null +++ b/src/output/grid_details.rs @@ -0,0 +1,60 @@ +use std::convert; +use std::iter::repeat; + +use term_grid as grid; + +use 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 = 2; + + let make_table = || { + let mut table = Table::with_options(self.details.colours, self.details.columns.for_dir(dir)); + if self.details.header { table.add_header() } + table + }; + + let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(columns).collect(); + + for (i, file) in files.iter().enumerate() { + tables[i % columns].add_file(file, 0, false, false); + } + + 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: 2, + }); + + for table in tables { + for cell in table.print_table(false, false).into_iter() { + grid.add(cell.into()); + } + } + + print!("{}", grid.fit_into_columns(columns)); + } +} + +impl convert::From for grid::Cell { + fn from(input: Cell) -> Self { + grid::Cell { + contents: input.text, + width: input.length, + } + } +} diff --git a/src/output/lines.rs b/src/output/lines.rs index 1d6dade..7f19105 100644 --- a/src/output/lines.rs +++ b/src/output/lines.rs @@ -13,7 +13,7 @@ pub struct Lines { impl Lines { pub fn view(&self, files: &[File]) { for file in files { - println!("{}", filename(file, &self.colours)); + println!("{}", filename(file, &self.colours, true)); } } } diff --git a/src/output/mod.rs b/src/output/mod.rs index ea48507..b4d9bba 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -7,14 +7,15 @@ use filetype::file_colour; pub use self::details::Details; pub use self::grid::Grid; pub use self::lines::Lines; +pub use self::grid_details::GridDetails; mod grid; pub mod details; mod lines; +mod grid_details; - -pub fn filename(file: &File, colours: &Colours) -> String { - if file.is_link() { +pub fn filename(file: &File, colours: &Colours, links: bool) -> String { + if links && file.is_link() { symlink_filename(file, colours) } else { From 08f3514d686d92fd93cef96d9c52d055f009c5a6 Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 16:25:59 +0100 Subject: [PATCH 03/10] Adapt the long grid view to the console width --- src/output/grid_details.rs | 51 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index 93e498a..b5e8cf5 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -1,4 +1,3 @@ -use std::convert; use std::iter::repeat; use term_grid as grid; @@ -17,44 +16,56 @@ pub struct GridDetails { impl GridDetails { pub fn view(&self, dir: Option<&Dir>, files: &[File]) { + let mut last_working_table = self.make_grid(1, dir, files); - let columns = 2; + for column_count in 2.. { + let grid = self.make_grid(column_count, dir, files); + if grid.fit_into_columns(column_count).width() <= self.grid.console_width { + last_working_table = grid; + } + else { + print!("{}", last_working_table.fit_into_columns(column_count - 1)); + return; + } + } + } + + pub fn make_grid(&self, column_count: usize, dir: Option<&Dir>, files: &[File]) -> grid::Grid { let make_table = || { let mut table = Table::with_options(self.details.colours, self.details.columns.for_dir(dir)); if self.details.header { table.add_header() } table }; - let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(columns).collect(); + let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(column_count).collect(); for (i, file) in files.iter().enumerate() { - tables[i % columns].add_file(file, 0, false, false); + tables[i % column_count].add_file(file, 0, false, false); } - let direction = if self.grid.across { grid::Direction::LeftToRight } - else { grid::Direction::TopToBottom }; + let direction = grid::Direction::LeftToRight; let mut grid = grid::Grid::new(grid::GridOptions { direction: direction, - separator_width: 2, + separator_width: 4, }); - for table in tables { - for cell in table.print_table(false, false).into_iter() { - grid.add(cell.into()); + let columns: Vec<_> = tables.iter().map(|t| t.print_table(false, false)).collect(); + + for row in 0 .. columns[0].len() { + 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); + } } } - print!("{}", grid.fit_into_columns(columns)); - } -} - -impl convert::From for grid::Cell { - fn from(input: Cell) -> Self { - grid::Cell { - contents: input.text, - width: input.length, - } + grid } } From 89526964c99446919f3784bd3a74fd03a441a8b1 Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 19:11:39 +0100 Subject: [PATCH 04/10] Go top-to-bottom, not left-to-right --- src/output/grid_details.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index b5e8cf5..a95e852 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -40,29 +40,31 @@ impl GridDetails { let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(column_count).collect(); - for (i, file) in files.iter().enumerate() { - tables[i % column_count].add_file(file, 0, false, false); + let mut height = files.len() / column_count; + if files.len() % column_count != 0 { + height += 1; } - let direction = grid::Direction::LeftToRight; + for (i, file) in files.iter().enumerate() { + tables[i / height].add_file(file, 0, false, false); + } + let columns: Vec<_> = tables.iter().map(|t| t.print_table(false, false)).collect(); + + let direction = grid::Direction::TopToBottom; let mut grid = grid::Grid::new(grid::GridOptions { direction: direction, separator_width: 4, }); - let columns: Vec<_> = tables.iter().map(|t| t.print_table(false, false)).collect(); + for column in columns.iter() { + for cell in column.iter() { + let cell = grid::Cell { + contents: cell.text.clone(), + width: cell.length, + }; - for row in 0 .. columns[0].len() { - 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); - } + grid.add(cell); } } From 922cd2a1886d7c03d47d77ba87d5bfb0e5cb18fb Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 19:57:13 +0100 Subject: [PATCH 05/10] Cache the rendered cells Previously, each time it tried to render a table (to check its width), it both re-queried the filesystem and re-formatted the values into coloured strings. These values are now calculated only once before the table is drawn, and are used repeatedly throughout. Although it looks as though there's more `clone()`ing going on than before, it used to be recalculating things and storing them as vectors anyway, so the memory would still be used in any case. --- src/column.rs | 2 +- src/output/details.rs | 7 ++++++- src/output/grid_details.rs | 22 +++++++++++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/column.rs b/src/column.rs index 6f4a1e7..0da3651 100644 --- a/src/column.rs +++ b/src/column.rs @@ -57,7 +57,7 @@ impl Column { } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub struct Cell { pub length: usize, pub text: String, diff --git a/src/output/details.rs b/src/output/details.rs index e67490d..9988b69 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -197,9 +197,14 @@ impl Table where U: Users { /// 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); + self.add_file_with_cells(cells, file, depth, last, links) + } + + pub fn add_file_with_cells(&mut self, cells: Vec, file: &File, depth: usize, last: bool, links: bool) { let row = Row { depth: depth, - cells: self.cells_for_file(file), + cells: cells, name: Cell { text: filename(file, &self.colours, links), length: file.file_name_width() }, last: last, attrs: file.xattrs.clone(), diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index a95e852..192cb1f 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -2,7 +2,7 @@ use std::iter::repeat; use term_grid as grid; -use column::Cell; +use column::{Column, Cell}; use dir::Dir; use file::File; use output::details::{Details, Table}; @@ -16,10 +16,14 @@ pub struct GridDetails { impl GridDetails { pub fn view(&self, dir: Option<&Dir>, files: &[File]) { - let mut last_working_table = self.make_grid(1, dir, files); + 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, dir, files); + let grid = self.make_grid(column_count, &*columns_for_dir, files, cells.clone()); if grid.fit_into_columns(column_count).width() <= self.grid.console_width { last_working_table = grid; @@ -31,22 +35,22 @@ impl GridDetails { } } - pub fn make_grid(&self, column_count: usize, dir: Option<&Dir>, files: &[File]) -> grid::Grid { + pub fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], files: &[File], cells: Vec>) -> grid::Grid { let make_table = || { - let mut table = Table::with_options(self.details.colours, self.details.columns.for_dir(dir)); + let mut table = Table::with_options(self.details.colours, columns_for_dir.into()); if self.details.header { table.add_header() } table }; let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(column_count).collect(); - let mut height = files.len() / column_count; - if files.len() % column_count != 0 { + let mut height = cells.len() / column_count; + if cells.len() % column_count != 0 { height += 1; } - for (i, file) in files.iter().enumerate() { - tables[i / height].add_file(file, 0, false, false); + for (i, (file, row)) in files.iter().zip(cells.into_iter()).enumerate() { + tables[i / height].add_file_with_cells(row, file, 0, false, false); } let columns: Vec<_> = tables.iter().map(|t| t.print_table(false, false)).collect(); From 26e3abc6b1f7ad5a390ebe72b34be09705921480 Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 20:41:38 +0100 Subject: [PATCH 06/10] Re-add missing failing option combinations --- src/options.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/options.rs b/src/options.rs index 092b6ab..b290675 100644 --- a/src/options.rs +++ b/src/options.rs @@ -288,6 +288,12 @@ impl View { if cfg!(feature="git") && matches.opt_present("git") { Err(Useless("git", false, "long")) } + else if matches.opt_present("level") && !matches.opt_present("recurse") { + Err(Useless2("level", "recurse", "tree")) + } + else if Attribute::feature_implemented() && matches.opt_present("extended") { + Err(Useless("extended", false, "long")) + } else { Ok(()) } @@ -721,5 +727,4 @@ mod test { let opts = Options::getopts(&[ "--level".to_string(), "69105".to_string() ]); assert_eq!(opts.unwrap_err(), Misfire::Useless2("level", "recurse", "tree")) } - } From 6d6e8b78f08b0b261c61e5ea58a22e5d9e6aad39 Mon Sep 17 00:00:00 2001 From: Ben S Date: Sun, 28 Jun 2015 21:27:18 +0100 Subject: [PATCH 07/10] Fix bug where unfilled displays were being checked --- src/output/grid_details.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index 192cb1f..9c3eeaa 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -25,7 +25,12 @@ impl GridDetails { for column_count in 2.. { let grid = self.make_grid(column_count, &*columns_for_dir, files, cells.clone()); - if grid.fit_into_columns(column_count).width() <= self.grid.console_width { + 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 { From 2bc7fde7150029dda126e7410b521d758809f00d Mon Sep 17 00:00:00 2001 From: Ben S Date: Mon, 29 Jun 2015 13:13:23 +0100 Subject: [PATCH 08/10] Allow using --across with --long --grid --- src/options.rs | 2 +- src/output/grid_details.rs | 56 +++++++++++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/options.rs b/src/options.rs index b290675..91c7f0f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -259,7 +259,7 @@ impl View { use self::Misfire::*; let long = || { - if matches.opt_present("across") { + if matches.opt_present("across") && !matches.opt_present("grid") { Err(Useless("across", true, "long")) } else if matches.opt_present("oneline") { diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index 9c3eeaa..bb191f8 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -49,31 +49,67 @@ impl GridDetails { let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(column_count).collect(); - let mut height = cells.len() / column_count; + let mut num_cells = cells.len(); + if self.details.header { + num_cells += column_count; + } + + let mut original_height = cells.len() / column_count; if cells.len() % column_count != 0 { + original_height += 1; + } + + let mut height = num_cells / column_count; + + if num_cells % column_count != 0 { height += 1; } for (i, (file, row)) in files.iter().zip(cells.into_iter()).enumerate() { - tables[i / height].add_file_with_cells(row, file, 0, false, false); + 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 = grid::Direction::TopToBottom; + 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, }); - for column in columns.iter() { - for cell in column.iter() { - let cell = grid::Cell { - contents: cell.text.clone(), - width: cell.length, - }; + 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); + 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); + } } } From fe0d433e65e8cde164cf2533057f4461174de584 Mon Sep 17 00:00:00 2001 From: Ben S Date: Mon, 29 Jun 2015 14:24:39 +0100 Subject: [PATCH 09/10] Require functionality from new term_grid --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84913f0..761c51c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ "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)", "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)", "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)", @@ -262,7 +262,7 @@ dependencies = [ [[package]] name = "term_grid" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-width 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", From 56895ab84fff784b2e8c550fd47ea1dd3698814b Mon Sep 17 00:00:00 2001 From: Ben S Date: Mon, 29 Jun 2015 14:47:07 +0100 Subject: [PATCH 10/10] Extract some methods --- src/output/grid_details.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index bb191f8..92b373c 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -1,5 +1,6 @@ use std::iter::repeat; +use users::OSUsers; use term_grid as grid; use column::{Column, Cell}; @@ -40,30 +41,22 @@ impl GridDetails { } } - pub fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], files: &[File], cells: Vec>) -> grid::Grid { - let make_table = || { - let mut table = Table::with_options(self.details.colours, columns_for_dir.into()); - if self.details.header { table.add_header() } - table - }; + fn make_table(&self, columns_for_dir: &[Column]) -> Table { + let mut table = Table::with_options(self.details.colours, columns_for_dir.into()); + if self.details.header { table.add_header() } + table + } - let mut tables: Vec<_> = repeat(()).map(|_| make_table()).take(column_count).collect(); + fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], files: &[File], cells: Vec>) -> 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 mut original_height = cells.len() / column_count; - if cells.len() % column_count != 0 { - original_height += 1; - } - - let mut height = num_cells / column_count; - - if num_cells % column_count != 0 { - height += 1; - } + 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 { @@ -116,3 +109,10 @@ impl GridDetails { grid } } + + +fn divide_rounding_up(a: usize, b: usize) -> usize { + let mut result = a / b; + if a % b != 0 { result += 1; } + result +} \ No newline at end of file