diff --git a/src/options/help.rs b/src/options/help.rs index e6cd25c..e82b4e0 100644 --- a/src/options/help.rs +++ b/src/options/help.rs @@ -7,6 +7,7 @@ DISPLAY OPTIONS -R, --recurse recurse into directories -T, --tree recurse into subdirectories in a tree view -x, --across sort multi-column view entries across + -F, --classify show file type indicator (one of */=@|) --color=WHEN, --colour=WHEN when to colourise the output (always, auto, never) --color-scale, --colour-scale colour file sizes according to their magnitude diff --git a/src/options/mod.rs b/src/options/mod.rs index 853973f..44ea2cc 100644 --- a/src/options/mod.rs +++ b/src/options/mod.rs @@ -55,6 +55,7 @@ impl Options { opts.optflag("R", "recurse", "recurse into directories"); opts.optflag("T", "tree", "recurse into subdirectories in a tree view"); opts.optflag("x", "across", "sort multi-column view entries across"); + opts.optflag("F", "classify", "show file type indicator (one of */=@|)"); opts.optopt ("", "color", "when to show anything in colours", "WHEN"); opts.optopt ("", "colour", "when to show anything in colours (alternate spelling)", "WHEN"); opts.optflag("", "color-scale", "use a colour scale when displaying file sizes (alternate spelling)"); diff --git a/src/options/view.rs b/src/options/view.rs index a9cb95b..f848c63 100644 --- a/src/options/view.rs +++ b/src/options/view.rs @@ -58,6 +58,7 @@ impl View { filter: filter.clone(), xattr: xattr::ENABLED && matches.opt_present("extended"), colours: colours, + classify: matches.opt_present("classify"), }; Ok(details) @@ -86,6 +87,8 @@ impl View { }; let other_options_scan = || { + let classify = matches.opt_present("classify"); + let term_colours = TerminalColours::deduce(matches)?; let term_width = TerminalWidth::deduce()?; @@ -103,6 +106,7 @@ impl View { else { let lines = Lines { colours: colours, + classify: classify, }; Ok(View::Lines(lines)) @@ -116,6 +120,7 @@ impl View { filter: filter.clone(), // TODO: clone xattr: false, colours: colours, + classify: classify, }; Ok(View::Details(details)) @@ -125,6 +130,7 @@ impl View { across: matches.opt_present("across"), console_width: width, colours: colours, + classify: classify, }; Ok(View::Grid(grid)) @@ -148,6 +154,7 @@ impl View { filter: filter.clone(), xattr: false, colours: colours, + classify: classify, }; Ok(View::Details(details)) @@ -155,6 +162,7 @@ impl View { else { let lines = Lines { colours: colours, + classify: classify, }; Ok(View::Lines(lines)) diff --git a/src/output/cell.rs b/src/output/cell.rs index b78f821..b446996 100644 --- a/src/output/cell.rs +++ b/src/output/cell.rs @@ -5,6 +5,8 @@ use std::ops::{Add, Deref, DerefMut}; use ansi_term::{Style, ANSIString, ANSIStrings}; use unicode_width::UnicodeWidthStr; +use fs::File; + /// 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. @@ -178,6 +180,19 @@ impl TextCellContents { #[derive(PartialEq, Debug, Clone, Copy, Default)] pub struct DisplayWidth(usize); +impl DisplayWidth { + pub fn from_file(file: &File, classify: bool) -> DisplayWidth { + let name_width = *DisplayWidth::from(&*file.name); + if classify { + if file.is_executable_file() || file.is_directory() || + file.is_pipe() || file.is_link() || file.is_socket() { + return DisplayWidth(name_width + 1); + } + } + DisplayWidth(name_width) + } +} + impl<'a> From<&'a str> for DisplayWidth { fn from(input: &'a str) -> DisplayWidth { DisplayWidth(UnicodeWidthStr::width(input)) diff --git a/src/output/details.rs b/src/output/details.rs index 6e524c8..d91d2f4 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -140,6 +140,9 @@ pub struct Details { /// The colours to use to display information in the table, including the /// colour of the tree view symbols. pub colours: Colours, + + /// Whether to show a file type indiccator. + pub classify: bool, } /// The **environment** struct contains any data that could change between @@ -303,7 +306,7 @@ impl Details { for (index, egg) in file_eggs.into_iter().enumerate() { let mut files = Vec::new(); let mut errors = egg.errors; - let mut width = DisplayWidth::from(&*egg.file.name); + let mut width = DisplayWidth::from_file(&egg.file, self.classify); if egg.file.dir.is_none() { if let Some(parent) = egg.file.path.parent() { @@ -312,7 +315,7 @@ impl Details { } let name = TextCell { - contents: filename(&egg.file, &self.colours, true), + contents: filename(&egg.file, &self.colours, true, self.classify), width: width, }; @@ -453,7 +456,7 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> { } pub fn filename_cell(&self, file: File, links: bool) -> TextCell { - let mut width = DisplayWidth::from(&*file.name); + let mut width = DisplayWidth::from_file(&file, self.opts.classify); if file.dir.is_none() { if let Some(parent) = file.path.parent() { @@ -462,7 +465,7 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> { } TextCell { - contents: filename(&file, &self.opts.colours, links), + contents: filename(&file, &self.opts.colours, links, self.opts.classify), width: width, } } diff --git a/src/output/grid.rs b/src/output/grid.rs index e03b12e..3eec694 100644 --- a/src/output/grid.rs +++ b/src/output/grid.rs @@ -13,6 +13,7 @@ pub struct Grid { pub across: bool, pub console_width: usize, pub colours: Colours, + pub classify: bool, } impl Grid { @@ -28,7 +29,7 @@ impl Grid { grid.reserve(files.len()); for file in files.iter() { - let mut width = DisplayWidth::from(&*file.name); + let mut width = DisplayWidth::from_file(file, self.classify); if file.dir.is_none() { if let Some(parent) = file.path.parent() { @@ -37,7 +38,7 @@ impl Grid { } grid.add(grid::Cell { - contents: filename(file, &self.colours, false).strings().to_string(), + contents: filename(file, &self.colours, false, self.classify).strings().to_string(), width: *width, }); } @@ -48,7 +49,7 @@ impl Grid { else { // File names too long for a grid - drop down to just listing them! for file in files.iter() { - writeln!(w, "{}", filename(file, &self.colours, false).strings())?; + writeln!(w, "{}", filename(file, &self.colours, false, self.classify).strings())?; } Ok(()) } diff --git a/src/output/lines.rs b/src/output/lines.rs index 668900e..6582dec 100644 --- a/src/output/lines.rs +++ b/src/output/lines.rs @@ -11,13 +11,14 @@ use super::colours::Colours; #[derive(Clone, Copy, Debug, PartialEq)] pub struct Lines { pub colours: Colours, + pub classify: bool, } /// The lines view literally just displays each file, line-by-line. impl Lines { pub fn view(&self, files: Vec, w: &mut W) -> IOResult<()> { for file in files { - writeln!(w, "{}", ANSIStrings(&filename(&file, &self.colours, true)))?; + writeln!(w, "{}", ANSIStrings(&filename(&file, &self.colours, true, self.classify)))?; } Ok(()) } diff --git a/src/output/mod.rs b/src/output/mod.rs index 242ecb1..06d1475 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -19,7 +19,7 @@ mod colours; mod tree; -pub fn filename(file: &File, colours: &Colours, links: bool) -> TextCellContents { +pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents { let mut bits = Vec::new(); if file.dir.is_none() { @@ -78,6 +78,18 @@ pub fn filename(file: &File, colours: &Colours, links: bool) -> TextCellContents // Do nothing -- the error gets displayed on the next line } } + } else if classify { + if file.is_executable_file() { + bits.push(Style::default().paint("*")); + } else if file.is_directory() { + bits.push(Style::default().paint("/")); + } else if file.is_pipe() { + bits.push(Style::default().paint("|")); + } else if file.is_link() { + bits.push(Style::default().paint("@")); + } else if file.is_socket() { + bits.push(Style::default().paint("=")); + } } bits.into()