summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/file.rs2
-rw-r--r--src/main.rs4
-rw-r--r--src/output/cell.rs130
-rw-r--r--src/output/column.rs38
-rw-r--r--src/output/details.rs253
-rw-r--r--src/output/grid_details.rs30
-rw-r--r--src/output/lines.rs6
-rw-r--r--src/output/mod.rs37
8 files changed, 310 insertions, 190 deletions
diff --git a/src/file.rs b/src/file.rs
index 08c45bb..d4c9b9e 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -195,7 +195,7 @@ impl<'dir> File<'dir> {
/// If statting the file fails (usually because the file on the
/// other end doesn't exist), returns the *filename* of the file
/// that should be there.
- pub fn link_target(&self) -> Result<File, String> {
+ pub fn link_target(&self) -> Result<File<'dir>, String> {
let path = match fs::read_link(&self.path) {
Ok(path) => path,
Err(_) => return Err(self.name.clone()),
diff --git a/src/main.rs b/src/main.rs
index 1e9f76c..11a2fc0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -135,8 +135,8 @@ impl Exa {
match self.options.view {
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),
+ View::GridDetails(gd) => gd.view(dir, files),
+ View::Lines(l) => l.view(files),
}
}
}
diff --git a/src/output/cell.rs b/src/output/cell.rs
new file mode 100644
index 0000000..1d3f6e2
--- /dev/null
+++ b/src/output/cell.rs
@@ -0,0 +1,130 @@
+//! The `TextCell` type for the details and lines views.
+
+use ansi_term::{Style, ANSIString, ANSIStrings};
+use unicode_width::UnicodeWidthStr;
+
+
+/// An individual cell that holds text in a table, used in the details and
+/// lines views to store ANSI-terminal-formatted data before it is printed.
+///
+/// A text cell is made up of zero or more strings coupled with the
+/// pre-computed length of all the strings combined. When constructing details
+/// or grid-details tables, the length will have to be queried multiple times,
+/// so it makes sense to cache it.
+///
+/// (This used to be called `Cell`, but was renamed because there’s a Rust
+/// type by that name too.)
+#[derive(PartialEq, Debug, Clone, Default)]
+pub struct TextCell {
+
+ /// The contents of this cell, as a vector of ANSI-styled strings.
+ pub contents: TextCellContents,
+
+ /// The Unicode “display width” of this cell, in characters.
+ ///
+ /// As with the `File` type’s width, this is related to the number of
+ /// *graphemes*, rather than *characters*, in the cell: most are 1 column
+ /// wide, but in some contexts, certain characters are two columns wide.
+ pub length: usize,
+}
+
+impl TextCell {
+
+ /// Creates a new text cell that holds the given text in the given style,
+ /// computing the Unicode width of the text.
+ pub fn paint(style: Style, text: String) -> Self {
+ TextCell {
+ length: text.width(),
+ contents: vec![ style.paint(text) ],
+ }
+ }
+
+ /// Creates a new text cell that holds the given text in the given style,
+ /// computing the Unicode width of the text. (This could be merged with
+ /// `paint`, but.)
+ pub fn paint_str(style: Style, text: &'static str) -> Self {
+ TextCell {
+ length: text.len(),
+ contents: vec![ style.paint(text) ],
+ }
+ }
+
+ /// Creates a new “blank” text cell that contains a single hyphen in the
+ /// given style, which should be the “punctuation” style from a `Colours`
+ /// value.
+ ///
+ /// This is used in place of empty table cells, as it is easier to read
+ /// tabular data when there is *something* in each cell.
+ pub fn blank(style: Style) -> Self {
+ TextCell {
+ length: 1,
+ contents: vec![ style.paint("-") ],
+ }
+ }
+
+ /// Adds the given number of unstyled spaces after this cell.
+ ///
+ /// This method allocates a `String` to hold the spaces.
+ pub fn add_spaces(&mut self, count: usize) {
+ use std::iter::repeat;
+
+ self.length += count;
+
+ let spaces: String = repeat(' ').take(count).collect();
+ self.contents.push(Style::default().paint(spaces));
+ }
+
+ /// Adds the contents of another `ANSIString` to the end of this cell.
+ pub fn push(&mut self, string: ANSIString<'static>, length: usize) {
+ self.contents.push(string);
+ self.length += length;
+ }
+
+ /// Adds all the contents of another `TextCell` to the end of this cell.
+ pub fn append(&mut self, other: TextCell) {
+ self.length += other.length;
+ self.contents.extend(other.contents);
+ }
+
+ /// Produces an `ANSIStrings` value that can be used to print the styled
+ /// values of this cell as an ANSI-terminal-formatted string.
+ pub fn strings(&self) -> ANSIStrings {
+ ANSIStrings(&self.contents)
+ }
+}
+
+
+// I’d like to eventually abstract cells so that instead of *every* cell
+// storing a vector, only variable-length cells would, and individual cells
+// would just store an array of a fixed length (which would usually be just 1
+// or 2), which wouldn’t require a heap allocation.
+//
+// For examples, look at the `render_*` methods in the `Table` object in the
+// details view:
+//
+// - `render_blocks`, `inode`, and `links` will always return a
+// one-string-long TextCell;
+// - `render_size` will return one or two strings in a TextCell, depending on
+// the size and whether one is present;
+// - `render_permissions` will return ten or eleven strings;
+// - `filename` and `symlink_filename` in the output module root return six or
+// five strings.
+//
+// In none of these cases are we dealing with a *truly variable* number of
+// strings: it is only when the strings are concatenated together do we need a
+// growable, heap-allocated buffer.
+//
+// So it would be nice to abstract the `TextCell` type so instead of a `Vec`,
+// it can use anything of type `T: IntoIterator<Item=ANSIString<’static>>`.
+// This would allow us to still hold all the data, but allocate less.
+//
+// But exa still has bugs and I need to fix those first :(
+
+
+/// The contents of a text cell, as a vector of ANSI-styled strings.
+///
+/// It’s possible to use this type directly in the case where you want a
+/// `TextCell` but aren’t concerned with tracking its width, because it occurs
+/// in the final cell of a table or grid and there’s no point padding it. This
+/// happens when dealing with file names.
+pub type TextCellContents = Vec<ANSIString<'static>>; \ No newline at end of file
diff --git a/src/output/column.rs b/src/output/column.rs
index c2b8568..cfec1f9 100644
--- a/src/output/column.rs
+++ b/src/output/column.rs
@@ -1,6 +1,3 @@
-use ansi_term::Style;
-use unicode_width::UnicodeWidthStr;
-
use dir::Dir;
@@ -194,38 +191,3 @@ impl Default for TimeTypes {
TimeTypes { accessed: false, modified: true, created: false }
}
}
-
-
-#[derive(PartialEq, Debug, Clone)]
-pub struct Cell {
- pub length: usize,
- pub text: String,
-}
-
-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/output/details.rs b/src/output/details.rs
index a7d1da9..e9e59b2 100644
--- a/src/output/details.rs
+++ b/src/output/details.rs
@@ -124,9 +124,10 @@ use feature::xattr::{Attribute, FileAttributes};
use file::fields as f;
use file::File;
use options::{FileFilter, RecurseOptions};
-use output::column::{Alignment, Column, Columns, Cell, SizeFormat};
+use output::column::{Alignment, Column, Columns, SizeFormat};
+use output::cell::TextCell;
-use ansi_term::{ANSIString, ANSIStrings, Style};
+use ansi_term::Style;
use datetime::local::{LocalDateTime, DatePiece};
use datetime::format::DateFormat;
@@ -198,7 +199,7 @@ impl Details {
// Then add files to the table and print it out.
self.add_files_to_table(&mut table, files, 0);
for cell in table.print_table() {
- println!("{}", cell.text);
+ println!("{}", cell.strings());
}
}
@@ -213,20 +214,18 @@ impl Details {
let mut file_eggs = Vec::new();
struct Egg<'_> {
- cells: Vec<Cell>,
- name: Cell,
+ cells: Vec<TextCell>,
xattrs: Vec<Attribute>,
errors: Vec<(io::Error, Option<PathBuf>)>,
dir: Option<Dir>,
- file: Arc<File<'_>>,
+ file: File<'_>,
}
pool.scoped(|scoped| {
let file_eggs = Arc::new(Mutex::new(&mut file_eggs));
let table = Arc::new(Mutex::new(&mut table));
- for file in src.into_iter() {
- let file: Arc<File> = Arc::new(file);
+ for file in src {
let file_eggs = file_eggs.clone();
let table = table.clone();
@@ -251,11 +250,6 @@ impl Details {
let cells = table.lock().unwrap().cells_for_file(&file, !xattrs.is_empty());
- let name = Cell {
- text: filename(&file, &self.colours, true),
- length: file.file_name_width()
- };
-
let mut dir = None;
if let Some(r) = self.recurse {
@@ -268,7 +262,6 @@ impl Details {
let egg = Egg {
cells: cells,
- name: name,
xattrs: xattrs,
errors: errors,
dir: dir,
@@ -280,17 +273,22 @@ impl Details {
}
});
- file_eggs.sort_by(|a, b| self.filter.compare_files(&*a.file, &*b.file));
+ file_eggs.sort_by(|a, b| self.filter.compare_files(&a.file, &b.file));
let num_eggs = file_eggs.len();
for (index, egg) in file_eggs.into_iter().enumerate() {
let mut files = Vec::new();
let mut errors = egg.errors;
+ let name = TextCell {
+ length: egg.file.file_name_width(),
+ contents: filename(egg.file, &self.colours, true),
+ };
+
let row = Row {
depth: depth,
cells: Some(egg.cells),
- name: egg.name,
+ name: name,
last: index == num_eggs - 1,
};
@@ -342,14 +340,11 @@ struct Row {
/// almost always be `Some`, containing a vector of cells. It will only be
/// `None` for a row displaying an attribute or error, neither of which
/// have cells.
- cells: Option<Vec<Cell>>,
-
- // Did You Know?
- // A Vec<Cell> and an Option<Vec<Cell>> actually have the same byte size!
+ cells: Option<Vec<TextCell>>,
/// This file's name, in coloured output. The name is treated separately
/// from the other cells, as it never requires padding.
- name: Cell,
+ name: TextCell,
/// How many directories deep into the tree structure this is. Directories
/// on top have depth 0.
@@ -429,8 +424,8 @@ impl<U> Table<U> where U: Users {
pub fn add_header(&mut self) {
let row = Row {
depth: 0,
- cells: Some(self.columns.iter().map(|c| Cell::paint(self.colours.header, c.header())).collect()),
- name: Cell::paint(self.colours.header, "Name"),
+ cells: Some(self.columns.iter().map(|c| TextCell::paint_str(self.colours.header, c.header())).collect()),
+ name: TextCell::paint_str(self.colours.header, "Name"),
last: false,
};
@@ -446,7 +441,7 @@ impl<U> Table<U> where U: Users {
let row = Row {
depth: depth,
cells: None,
- name: Cell::paint(self.colours.broken_arrow, &error_message),
+ name: TextCell::paint(self.colours.broken_arrow, error_message),
last: last,
};
@@ -457,19 +452,26 @@ impl<U> Table<U> where U: Users {
let row = Row {
depth: depth,
cells: None,
- name: Cell::paint(self.colours.perms.attribute, &format!("{} (len {})", xattr.name, xattr.size)),
+ name: TextCell::paint(self.colours.perms.attribute, format!("{} (len {})", xattr.name, xattr.size)),
last: last,
};
self.rows.push(row);
}
- pub fn add_file_with_cells(&mut self, cells: Vec<Cell>, file: &File, depth: usize, last: bool, links: bool) {
+ pub fn filename_cell(&self, file: File, links: bool) -> TextCell {
+ TextCell {
+ length: file.file_name_width(),
+ contents: filename(file, &self.colours, links),
+ }
+ }
+
+ pub fn add_file_with_cells(&mut self, cells: Vec<TextCell>, name_cell: TextCell, depth: usize, last: bool) {
let row = Row {
- depth: depth,
- cells: Some(cells),
- name: Cell { text: filename(file, &self.colours, links), length: file.file_name_width() },
- last: last,
+ depth: depth,
+ cells: Some(cells),
+ name: name_cell,
+ last: last,
};
self.rows.push(row);
@@ -477,13 +479,13 @@ impl<U> Table<U> where U: Users {
/// Use the list of columns to find which cells should be produced for
/// this file, per-column.
- pub fn cells_for_file(&mut self, file: &File, xattrs: bool) -> Vec<Cell> {
+ pub fn cells_for_file(&mut self, file: &File, xattrs: bool) -> Vec<TextCell> {
self.columns.clone().iter()
.map(|c| self.display(file, c, xattrs))
.collect()
}
- fn display(&mut self, file: &File, column: &Column, xattrs: bool) -> Cell {
+ fn display(&mut self, file: &File, column: &Column, xattrs: bool) -> TextCell {
use output::column::TimeType::*;
match *column {
@@ -501,7 +503,7 @@ impl<U> Table<U> where U: Users {
}
}
- fn render_permissions(&self, permissions: f::Permissions, xattrs: bool) -> Cell {
+ fn render_permissions(&self, permissions: f::Permissions, xattrs: bool) -> TextCell {
let c = self.colours.perms;
let bit = |bit, chr: &'static str, style: Style| {
if bit { style.paint(chr) } else { self.colours.punctuation.paint("-") }
@@ -518,7 +520,7 @@ impl<U> Table<U> where U: Users {
let x_colour = if let f::Type::File = permissions.file_type { c.user_execute_file }
else { c.user_execute_other };
- let mut columns = vec![
+ let mut chars = vec![
file_type,
bit(permissions.user_read, "r", c.user_read),
bit(permissions.user_write, "w", c.user_write),
@@ -532,63 +534,71 @@ impl<U> Table<U> where U: Users {
];
if xattrs {
- columns.push(c.attribute.paint("@"));
+ chars.push(c.attribute.paint("@"));
}
- Cell {
- text: ANSIStrings(&columns).to_string(),
- length: columns.len(),
+ TextCell {
+ length: chars.len(),
+ contents: chars,
}
}
- fn render_links(&self, links: f::Links) -> Cell {
+ fn render_links(&self, links: f::Links) -> TextCell {
let style = if links.multiple { self.colours.links.multi_link_file }
else { self.colours.links.normal };
- Cell::paint(style, &self.numeric.format_int(links.count))
+ TextCell::paint(style, self.numeric.format_int(links.count))
}
- fn render_blocks(&self, blocks: f::Blocks) -> Cell {
+ fn render_blocks(&self, blocks: f::Blocks) -> TextCell {
match blocks {
- f::Blocks::Some(blocks) => Cell::paint(self.colours.blocks, &blocks.to_string()),
- f::Blocks::None => Cell::paint(self.colours.punctuation, "-"),
+ f::Blocks::Some(blk) => TextCell::paint(self.colours.blocks, blk.to_string()),
+ f::Blocks::None => TextCell::blank(self.colours.punctuation),
}
}
- fn render_inode(&self, inode: f::Inode) -> Cell {
- Cell::paint(self.colours.inode, &inode.0.to_string())
+ fn render_inode(&self, inode: f::Inode) -> TextCell {
+ TextCell::paint(self.colours.inode, inode.0.to_string())
}
- fn render_size(&self, size: f::Size, size_format: SizeFormat) -> Cell {
- use number_prefix::{binary_prefix, decimal_prefix, Prefixed, Standalone, PrefixNames};
+ fn render_size(&self, size: f::Size, size_format: SizeFormat) -> TextCell {
+ use number_prefix::{binary_prefix, decimal_prefix};
+ use number_prefix::{Prefixed, Standalone, PrefixNames};
- if let f::Size::Some(offset) = size {
- let result = match size_format {
- SizeFormat::DecimalBytes => decimal_prefix(offset as f64),
- SizeFormat::BinaryBytes => binary_prefix(offset as f64),
- SizeFormat::JustBytes => return Cell::paint(self.colours.size.numbers, &self.numeric.format_int(offset)),
- };
+ let size = match size {
+ f::Size::Some(s) => s,
+ f::Size::None => return TextCell::blank(self.colours.punctuation),
+ };
- match result {
- Standalone(bytes) => Cell::paint(self.colours.size.numbers, &*bytes.to_string()),
- Prefixed(prefix, n) => {
- let number = if n < 10f64 { self.numeric.format_float(n, 1) } else { self.numeric.format_int(n as isize) };
- let symbol = prefix.symbol();
+ let result = match size_format {
+ SizeFormat::DecimalBytes => decimal_prefix(size as f64),
+ SizeFormat::BinaryBytes => binary_prefix(size as f64),
+ SizeFormat::JustBytes => {
+ let string = self.numeric.format_int(size);
+ return TextCell::paint(self.colours.size.numbers, string);
+ },
+ };
- Cell {
- text: ANSIStrings( &[ self.colours.size.numbers.paint(&number[..]), self.colours.size.unit.paint(symbol) ]).to_string(),
- length: number.len() + symbol.len(),
- }
- }
- }
- }
- else {
- Cell::paint(self.colours.punctuation, "-")
+ let (prefix, n) = match result {
+ Standalone(b) => return TextCell::paint(self.colours.size.numbers, b.to_string()),
+ Prefixed(p, n) => (p, n)
+ };
+
+ let symbol = prefix.symbol();
+ let number = if n < 10f64 { self.numeric.format_float(n, 1) }
+ else { self.numeric.format_int(n as isize) };
+
+ TextCell {
+ length: number.len() + symbol.len(),
+ contents: vec![
+ self.colours.size.numbers.paint(number),
+ self.colours.size.unit.paint(symbol),
+ ],
}
}
#[allow(trivial_numeric_casts)]
- fn render_time(&self, timestamp: f::Time) -> Cell {
+ fn render_time(&self, timestamp: f::Time) -> TextCell {
let date = self.tz.at(LocalDateTime::at(timestamp.0 as i64));
let datestamp = if date.year() == self.current_year {
@@ -598,60 +608,60 @@ impl<U> Table<U> where U: Users {
DATE_AND_YEAR.format(&date, &self.time)
};
- Cell::paint(self.colours.date, &datestamp)
+ TextCell::paint(self.colours.date, datestamp)
}
- fn render_git_status(&self, git: f::Git) -> Cell {
- Cell {
- text: ANSIStrings(&[ self.render_git_char(git.staged),
- self.render_git_char(git.unstaged) ]).to_string(),
- length: 2,
- }
- }
-
- fn render_git_char(&self, status: f::GitStatus) -> ANSIString {
- match status {
+ fn render_git_status(&self, git: f::Git) -> TextCell {
+ let git_char = |status| match status {
f::GitStatus::NotModified => self.colours.punctuation.paint("-"),
f::GitStatus::New => self.colours.git.new.paint("N"),
f::GitStatus::Modified => self.colours.git.modified.paint("M"),
f::GitStatus::Deleted => self.colours.git.deleted.paint("D"),
f::GitStatus::Renamed => self.colours.git.renamed.paint("R"),
f::GitStatus::TypeChange => self.colours.git.typechange.paint("T"),
+ };
+
+ TextCell {
+ length: 2,
+ contents: vec![
+ git_char(git.staged),
+ git_char(git.unstaged)
+ ],
}
}
- fn render_user(&mut self, user: f::User) -> Cell {
+ fn render_user(&mut self, user: f::User) -> TextCell {
let user_name = match self.users.get_user_by_uid(user.0) {
Some(user) => user.name,
None => user.0.to_string(),
};
let style = if self.users.get_current_uid() == user.0 { self.colours.users.user_you }
- else { self.colours.users.user_someone_else };
- Cell::paint(style, &*user_name)
+ else { self.colours.users.user_someone_else };
+ TextCell::paint(style, user_name)
}
- fn render_group(&mut self, group: f::Group) -> Cell {
+ fn render_group(&mut self, group: f::Group) -> TextCell {
let mut style = self.colours.users.group_not_yours;
- let group_name = match self.users.get_group_by_gid(group.0) {
- Some(group) => {
- let current_uid = self.users.get_current_uid();
- if let Some(current_user) = self.users.get_user_by_uid(current_uid) {
- if current_user.primary_group == group.gid || group.members.contains(&current_user.name) {
- style = self.colours.users.group_yours;
- }
- }
- group.name
- },
- None => group.0.to_string(),
+ let group = match self.users.get_group_by_gid(group.0) {
+ Some(g) => g,
+ None => return TextCell::paint(style, group.0.to_string()),
};
- Cell::paint(style, &*group_name)
+ let current_uid = self.users.get_current_uid();
+ if let Some(current_user) = self.users.get_user_by_uid(current_uid) {
+ if current_user.primary_group == group.gid
+ || group.members.contains(&current_user.name) {
+ style = self.colours.users.group_yours;
+ }
+ }
+
+ TextCell::paint(style, group.name)
}
/// Render the table as a vector of Cells, to be displayed on standard output.
- pub fn print_table(&self) -> Vec<Cell> {
+ pub fn print_table(self) -> Vec<TextCell> {
let mut stack = Vec::new();
let mut cells = Vec::new();
@@ -664,14 +674,16 @@ impl<U> Table<U> where U: Users {
let total_width: usize = self.columns.len() + column_widths.iter().fold(0, Add::add);
- for row in self.rows.iter() {
- let mut cell = Cell::empty();
+ for row in self.rows {
+ let mut cell = TextCell::default();
+
+ if let Some(cells) = row.cells {
+ for (n, (this_cell, width)) in cells.into_iter().zip(column_widths.iter()).enumerate() {
+ let padding = width - this_cell.length;
- if let Some(ref cells) = row.cells {
- for (n, width) in column_widths.iter().enumerate() {
match self.columns[n].alignment() {
- Alignment::Left => { cell.append(&cells[n]); cell.add_spaces(width - cells[n].length); }
- Alignment::Right => { cell.add_spaces(width - cells[n].length); cell.append(&cells[n]); }
+ Alignment::Left => { cell.append(this_cell); cell.add_spaces(padding); }
+ Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
}
cell.add_spaces(1);
@@ -681,8 +693,7 @@ impl<U> Table<U> where U: Users {
cell.add_spaces(total_width)
}
- let mut filename = String::new();
- let mut filename_length = 0;
+ let mut filename = TextCell::default();
// A stack tracks which tree characters should be printed. It's
// necessary to maintain information about the previously-printed
@@ -699,8 +710,7 @@ impl<U> Table<U> where U: Users {
stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge };
for i in 1 .. row.depth + 1 {
- filename.push_str(&*self.colours.punctuation.paint(stack[i].ascii_art()).to_string());
- filename_length += 4;
+ filename.push(self.colours.punctuation.paint(stack[i].ascii_art()), 4);
}
stack[row.depth] = if row.last { TreePart::Blank } else { TreePart::Line };
@@ -708,15 +718,13 @@ impl<U> Table<U> 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 {
- filename.push(' ');
- filename_length += 1;
+ filename.add_spaces(1);
}
// Print the name without worrying about padding.
- filename.push_str(&*row.name.text);
- filename_length += row.name.length;
+ filename.append(row.name);
- cell.append(&Cell { text: filename, length: filename_length });
+ cell.append(filename);
cells.push(cell);
}
@@ -767,7 +775,8 @@ pub mod test {
pub use super::Table;
pub use file::File;
pub use file::fields as f;
- pub use output::column::{Cell, Column};
+ pub use output::column::Column;
+ pub use output::cell::TextCell;
pub use users::{User, Group, uid_t, gid_t};
pub use users::mock::MockUsers;
@@ -806,7 +815,7 @@ pub mod test {
table.users = users;
let user = f::User(1000);
- let expected = Cell::paint(Red.bold(), "enoch");
+ let expected = TextCell::paint_str(Red.bold(), "enoch");
assert_eq!(expected, table.render_user(user))
}
@@ -819,7 +828,7 @@ pub mod test {
table.users = users;
let user = f::User(1000);
- let expected = Cell::paint(Cyan.bold(), "1000");
+ let expected = TextCell::paint_str(Cyan.bold(), "1000");
assert_eq!(expected, table.render_user(user));
}
@@ -830,7 +839,7 @@ pub mod test {
table.users.add_user(newser(1000, "enoch", 100));
let user = f::User(1000);
- let expected = Cell::paint(Green.bold(), "enoch");
+ let expected = TextCell::paint_str(Green.bold(), "enoch");
assert_eq!(expected, table.render_user(user));
}
@@ -840,7 +849,7 @@ pub mod test {
table.colours.users.user_someone_else = Red.normal();
let user = f::User(1000);
- let expected = Cell::paint(Red.normal(), "1000");
+ let expected = TextCell::paint_str(Red.normal(), "1000");
assert_eq!(expected, table.render_user(user));
}
@@ -850,7 +859,7 @@ pub mod test {
table.colours.users.user_someone_else = Blue.underline();
let user = f::User(2_147_483_648);
- let expected = Cell::paint(Blue.underline(), "2147483648");
+ let expected = TextCell::paint_str(Blue.underline(), "2147483648");
assert_eq!(expected, table.render_user(user));
}
}
@@ -869,7 +878,7 @@ pub mod test {
table.users = users;
let group = f::Group(100);
- let expected = Cell::paint(Fixed(101).normal(), "folk");
+ let expected = TextCell::paint_str(Fixed(101).normal(), "folk");
assert_eq!(expected, table.render_group(group))
}
@@ -882,7 +891,7 @@ pub mod test {
table.users = users;
let group = f::Group(100);
- let expected = Cell::paint(Fixed(87).normal(), "100");
+ let expected = TextCell::paint_str(Fixed(87).normal(), "100");
assert_eq!(expected, table.render_group(group));
}
@@ -897,7 +906,7 @@ pub mod test {
table.users = users;
let group = f::Group(100);
- let expected = Cell::paint(Fixed(64).normal(), "folk");
+ let expected = TextCell::paint_str(Fixed(64).normal(), "folk");
assert_eq!(expected, table.render_group(group))
}
@@ -912,7 +921,7 @@ pub mod test {
table.users = users;
let group = f::Group(100);
- let expected = Cell::paint(Fixed(31).normal(), "folk");
+ let expected = TextCell::paint_str(Fixed(31).normal(), "folk");
assert_eq!(expected, table.render_group(group))
}
@@ -922,7 +931,7 @@ pub mod test {
table.colours.users.group_not_yours = Blue.underline();
let group = f::Group(2_147_483_648);
- let expected = Cell::paint(Blue.underline(), "2147483648");
+ let expected = TextCell::paint_str(Blue.underline(), "2147483648");
assert_eq!(expected, table.render_group(group));
}
}
diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs
index e482464..332bfce 100644
--- a/src/output/grid_details.rs
+++ b/src/output/grid_details.rs
@@ -1,5 +1,6 @@
use std::iter::repeat;
+use ansi_term::ANSIStrings;
use users::OSUsers;
use term_grid as grid;
@@ -7,7 +8,8 @@ use dir::Dir;
use feature::xattr::FileAttributes;
use file::File;
-use output::column::{Column, Cell};
+use output::cell::TextCell;
+use output::column::Column;
use output::details::{Details, Table};
use output::grid::Grid;
@@ -25,19 +27,25 @@ fn file_has_xattrs(file: &File) -> bool {
}
impl GridDetails {
- pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
+ pub fn view(&self, dir: Option<&Dir>, files: Vec<File>) {
let columns_for_dir = match self.details.columns {
Some(cols) => cols.for_dir(dir),
None => Vec::new(),
};
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, file_has_xattrs(file))).collect();
+ let cells = files.iter()
+ .map(|file| first_table.cells_for_file(file, file_has_xattrs(file)))
+ .collect::<Vec<_>>();
- let mut last_working_table = self.make_grid(1, &*columns_for_dir, files, cells.clone());
+ let file_names = files.into_iter()
+ .map(|file| first_table.filename_cell(file, false))
+ .collect::<Vec<_>>();
+
+ let mut last_working_table = self.make_grid(1, &columns_for_dir, &file_names, cells.clone());
for column_count in 2.. {
- let grid = self.make_grid(column_count, &*columns_for_dir, files, cells.clone());
+ let grid = self.make_grid(column_count, &columns_for_dir, &file_names, cells.clone());
let the_grid_fits = {
let d = grid.fit_into_columns(column_count);
@@ -60,7 +68,7 @@ impl GridDetails {
table
}
- fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], files: &[File], cells: Vec<Vec<Cell>>) -> grid::Grid {
+ fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], file_names: &[TextCell], cells: Vec<Vec<TextCell>>) -> grid::Grid {
let mut tables: Vec<_> = repeat(()).map(|_| self.make_table(columns_for_dir)).take(column_count).collect();
let mut num_cells = cells.len();
@@ -71,7 +79,7 @@ impl GridDetails {
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() {
+ for (i, (file_name, row)) in file_names.iter().zip(cells.into_iter()).enumerate() {
let index = if self.grid.across {
i % column_count
}
@@ -79,10 +87,10 @@ impl GridDetails {
i / original_height
};
- tables[index].add_file_with_cells(row, file, 0, false, false);
+ tables[index].add_file_with_cells(row, file_name.clone(), 0, false);
}
- let columns: Vec<_> = tables.iter().map(|t| t.print_table()).collect();
+ let columns: Vec<_> = tables.into_iter().map(|t| t.print_table()).collect();
let direction = if self.grid.across { grid::Direction::LeftToRight }
else { grid::Direction::TopToBottom };
@@ -97,7 +105,7 @@ impl GridDetails {
for column in columns.iter() {
if row < column.len() {
let cell = grid::Cell {
- contents: column[row].text.clone(),
+ contents: ANSIStrings(&column[row].contents).to_string(),
width: column[row].length,
};
@@ -110,7 +118,7 @@ impl GridDetails {
for column in columns.iter() {
for cell in column.iter() {
let cell = grid::Cell {
- contents: cell.text.clone(),
+ contents: ANSIStrings(&cell.contents).to_string(),
width: cell.length,
};
diff --git a/src/output/lines.rs b/src/output/lines.rs
index 7f19105..39ef2c3 100644
--- a/src/output/lines.rs
+++ b/src/output/lines.rs
@@ -1,6 +1,8 @@
use colours::Colours;
use file::File;
+use ansi_term::ANSIStrings;
+
use super::filename;
@@ -11,9 +13,9 @@ pub struct Lines {
/// The lines view literally just displays each file, line-by-line.
impl Lines {
- pub fn view(&self, files: &[File]) {
+ pub fn view(&self, files: Vec<File>) {
for file in files {
- println!("{}", filename(file