summaryrefslogtreecommitdiffstats
path: root/src/output
diff options
context:
space:
mode:
authorBen S <ogham@bsago.me>2015-02-05 14:39:56 +0000
committerBen S <ogham@bsago.me>2015-02-05 14:39:56 +0000
commit42ae7b3d332fa46df3bb27f474f6b91a2d399d62 (patch)
treeb046d4d87a08cf7080224bf05617728e2efc7769 /src/output
parent5f2acf570c99c8e5fad5b7fabacd8c3d9b4c5c43 (diff)
Refactor the code after tree view changes
- Turn the views and main program loop into structs, rather than just as one gigantic function - Separate views into their own files The addition of the git column and the tree view meant that a lot of functions now just took extra arguments that didn't seem to fit. For example, it didn't really work to have only one 'view' method that printed out everything, as the different view options now all take different parameters.
Diffstat (limited to 'src/output')
-rw-r--r--src/output/details.rs106
-rw-r--r--src/output/grid.rs102
-rw-r--r--src/output/lines.rs8
-rw-r--r--src/output/mod.rs7
4 files changed, 223 insertions, 0 deletions
diff --git a/src/output/details.rs b/src/output/details.rs
new file mode 100644
index 0000000..422a7ca
--- /dev/null
+++ b/src/output/details.rs
@@ -0,0 +1,106 @@
+use column::{Column, Cell};
+use dir::Dir;
+use file::{File, GREY};
+use options::{Columns, FileFilter};
+use users::OSUsers;
+
+use ansi_term::Style::Plain;
+
+#[derive(PartialEq, Debug, Copy)]
+pub struct Details {
+ pub columns: Columns,
+ pub header: bool,
+ pub tree: bool,
+ pub filter: FileFilter,
+}
+
+impl Details {
+
+ pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
+ // The output gets formatted into columns, which looks nicer. To
+ // do this, we have to write the results into a table, instead of
+ // displaying each file immediately, then calculating the maximum
+ // width of each column based on the length of the results and
+ // padding the fields during output.
+
+ let columns = self.columns.for_dir(dir);
+ let mut cache = OSUsers::empty_cache();
+ let mut table = Vec::new();
+ self.get_files(&columns[], &mut cache, &mut table, files, 0);
+
+ if self.header {
+ let row = Row {
+ depth: 0,
+ cells: columns.iter().map(|c| Cell::paint(Plain.underline(), c.header())).collect(),
+ name: Plain.underline().paint("Name").to_string(),
+ last: false,
+ children: false,
+ };
+
+ table.insert(0, row);
+ }
+
+ let column_widths: Vec<usize> = range(0, columns.len())
+ .map(|n| table.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
+ .collect();
+
+ let mut stack = Vec::new();
+
+ for row in table {
+ for (num, column) in columns.iter().enumerate() {
+ let padding = column_widths[num] - row.cells[num].length;
+ print!("{} ", column.alignment().pad_string(&row.cells[num].text, padding));
+ }
+
+ if self.tree {
+ stack.resize(row.depth + 1, "├──");
+ stack[row.depth] = if row.last { "└──" } else { "├──" };
+
+ for i in 1 .. row.depth + 1 {
+ print!("{}", GREY.paint(stack[i]));
+ }
+
+ if row.children {
+ stack[row.depth] = if row.last { " " } else { "│ " };
+ }
+
+ if row.depth != 0 {
+ print!(" ");
+ }
+ }
+
+ print!("{}\n", row.name);
+ }
+ }
+
+ fn get_files(&self, columns: &[Column], cache: &mut OSUsers, dest: &mut Vec<Row>, src: &[File], depth: usize) {
+ for (index, file) in src.iter().enumerate() {
+
+ let row = Row {
+ depth: depth,
+ cells: columns.iter().map(|c| file.display(c, cache)).collect(),
+ name: file.file_name_view(),
+ last: index == src.len() - 1,
+ children: file.this.is_some(),
+ };
+
+ dest.push(row);
+
+ if self.tree {
+ if let Some(ref dir) = file.this {
+ let mut files = dir.files(true);
+ self.filter.transform_files(&mut files);
+ self.get_files(columns, cache, dest, files.as_slice(), depth + 1);
+ }
+ }
+ }
+ }
+}
+
+struct Row {
+ pub depth: usize,
+ pub cells: Vec<Cell>,
+ pub name: String,
+ pub last: bool,
+ pub children: bool,
+}
diff --git a/src/output/grid.rs b/src/output/grid.rs
new file mode 100644
index 0000000..8c5d632
--- /dev/null
+++ b/src/output/grid.rs
@@ -0,0 +1,102 @@
+use column::Alignment::Left;
+use file::File;
+use super::lines::lines_view;
+
+use std::cmp::max;
+use std::iter::{AdditiveIterator, repeat};
+
+#[derive(PartialEq, Debug, Copy)]
+pub struct Grid {
+ pub across: bool,
+ pub console_width: usize,
+}
+
+impl Grid {
+ fn fit_into_grid(&self, files: &[File]) -> Option<(usize, Vec<usize>)> {
+ // TODO: this function could almost certainly be optimised...
+ // surely not *all* of the numbers of lines are worth searching through!
+
+ // Instead of numbers of columns, try to find the fewest number of *lines*
+ // that the output will fit in.
+ for num_lines in 1 .. files.len() {
+
+ // The number of columns is the number of files divided by the number
+ // of lines, *rounded up*.
+ let mut num_columns = files.len() / num_lines;
+ if files.len() % num_lines != 0 {
+ num_columns += 1;
+ }
+
+ // Early abort: if there are so many columns that the width of the
+ // *column separators* is bigger than the width of the screen, then
+ // don't even try to tabulate it.
+ // This is actually a necessary check, because the width is stored as
+ // a usize, and making it go negative makes it huge instead, but it
+ // also serves as a speed-up.
+ let separator_width = (num_columns - 1) * 2;
+ if self.console_width < separator_width {
+ continue;
+ }
+
+ // Remove the separator width from the available space.
+ let adjusted_width = self.console_width - separator_width;
+
+ // Find the width of each column by adding the lengths of the file
+ // names in that column up.
+ let mut column_widths: Vec<usize> = repeat(0).take(num_columns).collect();
+ for (index, file) in files.iter().enumerate() {
+ let index = if self.across {
+ index % num_columns
+ }
+ else {
+ index / num_lines
+ };
+ column_widths[index] = max(column_widths[index], file.name.len());
+ }
+
+ // If they all fit in the terminal, combined, then success!
+ if column_widths.iter().map(|&x| x).sum() < adjusted_width {
+ return Some((num_lines, column_widths));
+ }
+ }
+
+ // If you get here you have really long file names.
+ return None;
+ }
+
+ pub fn view(&self, files: &[File]) {
+ if let Some((num_lines, widths)) = self.fit_into_grid(files) {
+ for y in 0 .. num_lines {
+ for x in 0 .. widths.len() {
+ let num = if self.across {
+ y * widths.len() + x
+ }
+ else {
+ y + num_lines * x
+ };
+
+ // Show whitespace in the place of trailing files
+ if num >= files.len() {
+ continue;
+ }
+
+ let ref file = files[num];
+ let styled_name = file.file_colour().paint(file.name.as_slice()).to_string();
+ if x == widths.len() - 1 {
+ // The final column doesn't need to have trailing spaces
+ print!("{}", styled_name);
+ }
+ else {
+ assert!(widths[x] >= file.name.len());
+ print!("{}", Left.pad_string(&styled_name, widths[x] - file.name.len() + 2));
+ }
+ }
+ print!("\n");
+ }
+ }
+ else {
+ // Drop down to lines view if the file names are too big for a grid
+ lines_view(files);
+ }
+ }
+}
diff --git a/src/output/lines.rs b/src/output/lines.rs
new file mode 100644
index 0000000..6649c29
--- /dev/null
+++ b/src/output/lines.rs
@@ -0,0 +1,8 @@
+use file::File;
+
+/// The lines view literally just displays each file, line-by-line.
+pub fn lines_view(files: &[File]) {
+ for file in files {
+ println!("{}", file.file_name_view());
+ }
+}
diff --git a/src/output/mod.rs b/src/output/mod.rs
new file mode 100644
index 0000000..054e6bf
--- /dev/null
+++ b/src/output/mod.rs
@@ -0,0 +1,7 @@
+mod grid;
+mod details;
+mod lines;
+
+pub use self::grid::Grid;
+pub use self::details::Details;
+pub use self::lines::lines_view;