Parcourir la source

Move viewing code to its own module

Benjamin Sago il y a 11 ans
Parent
commit
3f08836c3f
3 fichiers modifiés avec 176 ajouts et 172 suppressions
  1. 4 163
      src/exa.rs
  2. 2 9
      src/options.rs
  3. 170 0
      src/output.rs

+ 4 - 163
src/exa.rs

@@ -7,25 +7,18 @@ extern crate users;
 
 use std::io::FileType;
 use std::io::fs;
-use std::iter::{AdditiveIterator, repeat};
 use std::os;
-use std::cmp::max;
 
-use column::Alignment::Left;
-use column::Column;
 use dir::Dir;
 use file::File;
-use options::{Options, View};
-
-use ansi_term::Style::Plain;
-use ansi_term::strip_formatting;
-use users::OSUsers;
+use options::Options;
 
 pub mod column;
 pub mod dir;
 pub mod file;
 pub mod filetype;
 pub mod options;
+pub mod output;
 pub mod term;
 
 fn main() {
@@ -70,7 +63,7 @@ fn exa(options: &Options) {
     let mut first = files.is_empty();
 
     if !files.is_empty() {
-        view(options, files);
+        options.view.view(files);
     }
 
     for dir_name in dirs.iter() {
@@ -90,7 +83,7 @@ fn exa(options: &Options) {
                     println!("{}:", dir_name);
                 }
 
-                view(options, files);
+                options.view.view(files);
             }
             Err(e) => {
                 println!("{}: {}", dir_name, e);
@@ -99,155 +92,3 @@ fn exa(options: &Options) {
         };
     }
 }
-
-fn view(options: &Options, files: Vec<File>) {
-    match options.view {
-        View::Grid(across, width)  => grid_view(across, width, files),
-        View::Details(ref cols)    => details_view(cols, files, options.header),
-        View::Lines                => lines_view(files),
-    }
-}
-
-fn lines_view(files: Vec<File>) {
-    for file in files.iter() {
-        println!("{}", file.file_name());
-    }
-}
-
-fn fit_into_grid(across: bool, console_width: usize, files: &Vec<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 range(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 console_width < separator_width {
-            continue;
-        }
-
-        // Remove the separator width from the available space.
-        let adjusted_width = 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 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;
-}
-
-fn grid_view(across: bool, console_width: usize, files: Vec<File>) {
-    if let Some((num_lines, widths)) = fit_into_grid(across, console_width, &files) {
-        for y in range(0, num_lines) {
-            for x in range(0, widths.len()) {
-                let num = if 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);
-    }
-}
-
-fn details_view(columns: &Vec<Column>, files: Vec<File>, header: bool) {
-    // 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 mut cache = OSUsers::empty_cache();
-
-    let mut table: Vec<Vec<String>> = files.iter()
-        .map(|f| columns.iter().map(|c| f.display(c, &mut cache)).collect())
-        .collect();
-
-    if header {
-        table.insert(0, columns.iter().map(|c| Plain.underline().paint(c.header()).to_string()).collect());
-    }
-
-    // Each column needs to have its invisible colour-formatting
-    // characters stripped before it has its width calculated, or the
-    // width will be incorrect and the columns won't line up properly.
-    // This is fairly expensive to do (it uses a regex), so the
-    // results are cached.
-
-    let lengths: Vec<Vec<usize>> = table.iter()
-        .map(|row| row.iter().map(|col| strip_formatting(col.as_slice()).len()).collect())
-        .collect();
-
-    let column_widths: Vec<usize> = range(0, columns.len())
-        .map(|n| lengths.iter().map(|row| row[n]).max().unwrap_or(0))
-        .collect();
-
-    for (field_widths, row) in lengths.iter().zip(table.iter()) {
-        for (num, column) in columns.iter().enumerate() {
-            if num != 0 {
-                print!(" ");  // Separator
-            }
-
-            if num == columns.len() - 1 {
-                // The final column doesn't need to have trailing spaces
-                print!("{}", row[num]);
-            }
-            else {
-                let padding = column_widths[num] - field_widths[num];
-                print!("{}", column.alignment().pad_string(&row[num], padding));
-            }
-        }
-        print!("\n");
-    }
-}

+ 2 - 9
src/options.rs

@@ -4,6 +4,7 @@ extern crate natord;
 use file::File;
 use column::{Column, SizeFormat};
 use column::Column::*;
+use output::View;
 use term::dimensions;
 
 use std::ascii::AsciiExt;
@@ -28,14 +29,7 @@ impl SortField {
     }
 }
 
-pub enum View {
-    Details(Vec<Column>),
-    Lines,
-    Grid(bool, usize),
-}
-
 pub struct Options {
-    pub header: bool,
     pub list_dirs: bool,
     path_strs: Vec<String>,
     reverse: bool,
@@ -78,7 +72,6 @@ impl Options {
         }
 
         Ok(Options {
-            header:          matches.opt_present("header"),
             list_dirs:       matches.opt_present("list-dirs"),
             path_strs:       if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
             reverse:         matches.opt_present("reverse"),
@@ -94,7 +87,7 @@ impl Options {
 
     fn view(matches: &getopts::Matches) -> View {
         if matches.opt_present("long") {
-            View::Details(Options::columns(matches))
+            View::Details(Options::columns(matches), matches.opt_present("header"))
         }
         else if matches.opt_present("oneline") {
             View::Lines

+ 170 - 0
src/output.rs

@@ -0,0 +1,170 @@
+use std::cmp::max;
+use std::iter::{AdditiveIterator, repeat};
+
+use column::Column;
+use column::Alignment::Left;
+use file::File;
+use users::OSUsers;
+
+use ansi_term::Style::Plain;
+use ansi_term::strip_formatting;
+
+pub enum View {
+    Details(Vec<Column>, bool),
+    Lines,
+    Grid(bool, usize),
+}
+
+impl View {
+    pub fn view(&self, files: Vec<File>) {
+        match *self {
+            View::Grid(across, width)       => grid_view(across, width, files),
+            View::Details(ref cols, header) => details_view(cols, files, header),
+            View::Lines                     => lines_view(files),
+        }
+    }
+}
+
+fn lines_view(files: Vec<File>) {
+    for file in files.iter() {
+        println!("{}", file.file_name());
+    }
+}
+
+fn fit_into_grid(across: bool, console_width: usize, files: &Vec<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 range(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 console_width < separator_width {
+            continue;
+        }
+
+        // Remove the separator width from the available space.
+        let adjusted_width = 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 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;
+}
+
+fn grid_view(across: bool, console_width: usize, files: Vec<File>) {
+    if let Some((num_lines, widths)) = fit_into_grid(across, console_width, &files) {
+        for y in range(0, num_lines) {
+            for x in range(0, widths.len()) {
+                let num = if 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);
+    }
+}
+
+fn details_view(columns: &Vec<Column>, files: Vec<File>, header: bool) {
+    // 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 mut cache = OSUsers::empty_cache();
+
+    let mut table: Vec<Vec<String>> = files.iter()
+        .map(|f| columns.iter().map(|c| f.display(c, &mut cache)).collect())
+        .collect();
+
+    if header {
+        table.insert(0, columns.iter().map(|c| Plain.underline().paint(c.header()).to_string()).collect());
+    }
+
+    // Each column needs to have its invisible colour-formatting
+    // characters stripped before it has its width calculated, or the
+    // width will be incorrect and the columns won't line up properly.
+    // This is fairly expensive to do (it uses a regex), so the
+    // results are cached.
+
+    let lengths: Vec<Vec<usize>> = table.iter()
+        .map(|row| row.iter().map(|col| strip_formatting(col.as_slice()).len()).collect())
+        .collect();
+
+    let column_widths: Vec<usize> = range(0, columns.len())
+        .map(|n| lengths.iter().map(|row| row[n]).max().unwrap_or(0))
+        .collect();
+
+    for (field_widths, row) in lengths.iter().zip(table.iter()) {
+        for (num, column) in columns.iter().enumerate() {
+            if num != 0 {
+                print!(" ");  // Separator
+            }
+
+            if num == columns.len() - 1 {
+                // The final column doesn't need to have trailing spaces
+                print!("{}", row[num]);
+            }
+            else {
+                let padding = column_widths[num] - field_widths[num];
+                print!("{}", column.alignment().pad_string(&row[num], padding));
+            }
+        }
+        print!("\n");
+    }
+}