Explorar el Código

Get terminal width for grid view (resolve #1)

Ben S hace 11 años
padre
commit
cf3e32c9c1
Se han modificado 4 ficheros con 66 adiciones y 13 borrados
  1. 3 13
      src/exa.rs
  2. 5 0
      src/file.rs
  3. 57 0
      src/term.rs
  4. 1 0
      src/unix.rs

+ 3 - 13
src/exa.rs

@@ -2,10 +2,7 @@
 extern crate regex;
 #[phase(plugin)] extern crate regex_macros;
 extern crate ansi_term;
-
 extern crate unicode;
-use std::char::UnicodeChar;
-use std::iter::AdditiveIterator;
 
 use std::os;
 
@@ -25,6 +22,7 @@ pub mod filetype;
 pub mod unix;
 pub mod options;
 pub mod sort;
+pub mod term;
 
 fn main() {
     let args = os::args();
@@ -66,20 +64,12 @@ fn exa(opts: &Options) {
     }
 }
 
-fn width(string: &str) -> uint {
-    string.as_slice().chars()
-        .map(|c| c.width(true))
-        .filter(|o| o.is_some())
-        .map(|o| o.unwrap())
-        .sum()
-}
-
 fn grid_view(options: &Options, across: bool, dir: Dir) {
     let unsorted_files = dir.files();
     let files: Vec<&File> = options.transform_files(&unsorted_files);
     
-    let max_column_length = files.iter().map(|f| width(f.name.as_slice())).max().unwrap();
-    let console_width = 80;
+    let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap();
+    let (console_width, _) = term::dimensions().unwrap_or((80, 24));
     let num_columns = (console_width + 1) / (max_column_length + 1);
     let count = files.len();
 

+ 5 - 0
src/file.rs

@@ -1,5 +1,6 @@
 use std::io::{fs, IoResult};
 use std::io;
+use unicode::str::UnicodeStrSlice;
 
 use ansi_term::{Paint, Colour, Plain, Style, Red, Green, Yellow, Blue, Purple, Cyan, Fixed};
 
@@ -157,6 +158,10 @@ impl<'a> File<'a> {
         }
     }
 
+    pub fn file_name_width(&self) -> uint {
+        self.name.as_slice().width(false)
+    }
+
     fn target_file_name_and_arrow(&self, target_path: Path) -> String {
         let v = target_path.filename().unwrap();
         let filename = String::from_utf8_lossy(v).to_string();

+ 57 - 0
src/term.rs

@@ -0,0 +1,57 @@
+
+mod c {
+    #![allow(non_camel_case_types)]
+    extern crate libc;
+    pub use self::libc::{
+        c_int,
+        c_ushort,
+        c_ulong,
+        STDOUT_FILENO,
+    };
+    use std::mem::zeroed;
+
+    // Getting the terminal size is done using an ioctl command that
+    // takes the file handle to the terminal (which in our case is
+    // stdout), and populates a structure with the values.
+
+    pub struct winsize {
+        pub ws_row: c_ushort,
+        pub ws_col: c_ushort,
+    }
+
+    // Unfortunately the actual command is not standardised...
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "android")]
+    static TIOCGWINSZ: c_ulong = 0x5413;
+
+    #[cfg(target_os = "freebsd")]
+    #[cfg(target_os = "macos")]
+    static TIOCGWINSZ: c_ulong = 0x40087468;
+
+    extern {
+        pub fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
+    }
+
+    pub fn dimensions() -> winsize {
+        unsafe {
+            let mut window: winsize = zeroed();
+            ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut window as *mut winsize);
+            window
+        }
+    }
+}
+
+pub fn dimensions() -> Option<(uint, uint)> {
+    let w = c::dimensions();
+
+    // If either of the dimensions is 0 then the command failed,
+    // usually because output isn't to a terminal (instead to a file
+    // or pipe or something)
+    if w.ws_col == 0 || w.ws_row == 0 {
+        None
+    }
+    else {
+        Some((w.ws_col as uint, w.ws_row as uint))
+    }
+}

+ 1 - 0
src/unix.rs

@@ -39,6 +39,7 @@ mod c {
         pub fn getuid() -> libc::c_int;
     }
 }
+
 pub struct Unix {
     user_names:    HashMap<u32, Option<String>>,  // mapping of user IDs to user names
     group_names:   HashMap<u32, Option<String>>,  // mapping of groups IDs to group names