diff --git a/src/output/details.rs b/src/output/details.rs index a9abac7..f182caa 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -101,7 +101,7 @@ use output::colours::Colours; use output::column::{Alignment, Column, Columns, SizeFormat}; use output::cell::{TextCell, DisplayWidth}; use output::tree::TreeTrunk; -use super::filename; +use output::file_name::filename; /// With the **Details** view, the output gets formatted into columns, with diff --git a/src/output/file_name.rs b/src/output/file_name.rs new file mode 100644 index 0000000..4487799 --- /dev/null +++ b/src/output/file_name.rs @@ -0,0 +1,145 @@ +use ansi_term::{ANSIString, Style}; + +use fs::{File, FileTarget}; +use output::Colours; +use output::cell::TextCellContents; + + +pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents { + let mut bits = Vec::new(); + + // TODO: This long function could do with some splitting up. + + if file.dir.is_none() { + if let Some(parent) = file.path.parent() { + let coconut = parent.components().count(); + + if coconut == 1 && parent.has_root() { + bits.push(colours.symlink_path.paint("/")); + } + else if coconut >= 1 { + bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string())); + bits.push(colours.symlink_path.paint("/")); + } + } + } + + if !file.name.is_empty() { + for bit in coloured_file_name(file, colours) { + bits.push(bit); + } + } + + if links && file.is_link() { + match file.link_target() { + FileTarget::Ok(target) => { + bits.push(Style::default().paint(" ")); + bits.push(colours.punctuation.paint("->")); + bits.push(Style::default().paint(" ")); + + if let Some(parent) = target.path.parent() { + let coconut = parent.components().count(); + + if coconut == 1 && parent.has_root() { + bits.push(colours.symlink_path.paint("/")); + } + else if coconut >= 1 { + bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string())); + bits.push(colours.symlink_path.paint("/")); + } + } + + if !target.name.is_empty() { + bits.push(file_colour(colours, &target).paint(target.name)); + } + }, + + FileTarget::Broken(broken_path) => { + bits.push(Style::default().paint(" ")); + bits.push(colours.broken_arrow.paint("->")); + bits.push(Style::default().paint(" ")); + bits.push(colours.broken_filename.paint(broken_path.display().to_string())); + }, + + FileTarget::Err(_) => { + // 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() +} + +/// Returns at least one ANSI-highlighted string representing this file’s +/// name using the given set of colours. +/// +/// Ordinarily, this will be just one string: the file’s complete name, +/// coloured according to its file type. If the name contains control +/// characters such as newlines or escapes, though, we can’t just print them +/// to the screen directly, because then there’ll be newlines in weird places. +/// +/// So in that situation, those characters will be escaped and highlighted in +/// a different colour. +fn coloured_file_name<'a>(file: &File, colours: &Colours) -> Vec> { + let colour = file_colour(colours, file); + let mut bits = Vec::new(); + + if file.name.chars().all(|c| c >= 0x20 as char) { + bits.push(colour.paint(file.name.clone())); + } + else { + for c in file.name.chars() { + // The `escape_default` method on `char` is *almost* what we want here, but + // it still escapes non-ASCII UTF-8 characters, which are still printable. + + if c >= 0x20 as char { + // TODO: This allocates way too much, + // hence the `all` check above. + let mut s = String::new(); + s.push(c); + bits.push(colour.paint(s)); + } else { + let s = c.escape_default().collect::(); + bits.push(colours.control_char.paint(s)); + } + } + } + + bits +} + +pub fn file_colour(colours: &Colours, file: &File) -> Style { + match file { + f if f.is_directory() => colours.filetypes.directory, + f if f.is_executable_file() => colours.filetypes.executable, + f if f.is_link() => colours.filetypes.symlink, + f if f.is_pipe() => colours.filetypes.pipe, + f if f.is_char_device() + | f.is_block_device() => colours.filetypes.device, + f if f.is_socket() => colours.filetypes.socket, + f if !f.is_file() => colours.filetypes.special, + f if f.is_immediate() => colours.filetypes.immediate, + f if f.is_image() => colours.filetypes.image, + f if f.is_video() => colours.filetypes.video, + f if f.is_music() => colours.filetypes.music, + f if f.is_lossless() => colours.filetypes.lossless, + f if f.is_crypto() => colours.filetypes.crypto, + f if f.is_document() => colours.filetypes.document, + f if f.is_compressed() => colours.filetypes.compressed, + f if f.is_temp() => colours.filetypes.temp, + f if f.is_compiled() => colours.filetypes.compiled, + _ => colours.filetypes.normal, + } +} diff --git a/src/output/grid.rs b/src/output/grid.rs index 5456d58..9a87580 100644 --- a/src/output/grid.rs +++ b/src/output/grid.rs @@ -5,7 +5,7 @@ use term_grid as grid; use fs::File; use output::DisplayWidth; use output::colours::Colours; -use super::filename; +use output::file_name::filename; #[derive(PartialEq, Debug, Copy, Clone)] diff --git a/src/output/lines.rs b/src/output/lines.rs index 6582dec..e7512ad 100644 --- a/src/output/lines.rs +++ b/src/output/lines.rs @@ -4,7 +4,7 @@ use ansi_term::ANSIStrings; use fs::File; -use super::filename; +use output::file_name::filename; use super::colours::Colours; diff --git a/src/output/mod.rs b/src/output/mod.rs index 5b87d2f..8f8a149 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -1,7 +1,3 @@ -use ansi_term::{ANSIString, Style}; - -use fs::{File, FileTarget}; - pub use self::cell::{TextCell, TextCellContents, DisplayWidth}; pub use self::colours::Colours; pub use self::details::Details; @@ -17,143 +13,4 @@ pub mod column; mod cell; mod colours; mod tree; - - -pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents { - let mut bits = Vec::new(); - - // TODO: This long function could do with some splitting up. - - if file.dir.is_none() { - if let Some(parent) = file.path.parent() { - let coconut = parent.components().count(); - - if coconut == 1 && parent.has_root() { - bits.push(colours.symlink_path.paint("/")); - } - else if coconut >= 1 { - bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string())); - bits.push(colours.symlink_path.paint("/")); - } - } - } - - if !file.name.is_empty() { - for bit in coloured_file_name(file, colours) { - bits.push(bit); - } - } - - if links && file.is_link() { - match file.link_target() { - FileTarget::Ok(target) => { - bits.push(Style::default().paint(" ")); - bits.push(colours.punctuation.paint("->")); - bits.push(Style::default().paint(" ")); - - if let Some(parent) = target.path.parent() { - let coconut = parent.components().count(); - - if coconut == 1 && parent.has_root() { - bits.push(colours.symlink_path.paint("/")); - } - else if coconut >= 1 { - bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string())); - bits.push(colours.symlink_path.paint("/")); - } - } - - if !target.name.is_empty() { - bits.push(file_colour(colours, &target).paint(target.name)); - } - }, - - FileTarget::Broken(broken_path) => { - bits.push(Style::default().paint(" ")); - bits.push(colours.broken_arrow.paint("->")); - bits.push(Style::default().paint(" ")); - bits.push(colours.broken_filename.paint(broken_path.display().to_string())); - }, - - FileTarget::Err(_) => { - // 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() -} - -/// Returns at least one ANSI-highlighted string representing this file’s -/// name using the given set of colours. -/// -/// Ordinarily, this will be just one string: the file’s complete name, -/// coloured according to its file type. If the name contains control -/// characters such as newlines or escapes, though, we can’t just print them -/// to the screen directly, because then there’ll be newlines in weird places. -/// -/// So in that situation, those characters will be escaped and highlighted in -/// a different colour. -fn coloured_file_name<'a>(file: &File, colours: &Colours) -> Vec> { - let colour = file_colour(colours, file); - let mut bits = Vec::new(); - - if file.name.chars().all(|c| c >= 0x20 as char) { - bits.push(colour.paint(file.name.clone())); - } - else { - for c in file.name.chars() { - // The `escape_default` method on `char` is *almost* what we want here, but - // it still escapes non-ASCII UTF-8 characters, which are still printable. - - if c >= 0x20 as char { - // TODO: This allocates way too much, - // hence the `all` check above. - let mut s = String::new(); - s.push(c); - bits.push(colour.paint(s)); - } else { - let s = c.escape_default().collect::(); - bits.push(colours.control_char.paint(s)); - } - } - } - - bits -} - -pub fn file_colour(colours: &Colours, file: &File) -> Style { - match file { - f if f.is_directory() => colours.filetypes.directory, - f if f.is_executable_file() => colours.filetypes.executable, - f if f.is_link() => colours.filetypes.symlink, - f if f.is_pipe() => colours.filetypes.pipe, - f if f.is_char_device() - | f.is_block_device() => colours.filetypes.device, - f if f.is_socket() => colours.filetypes.socket, - f if !f.is_file() => colours.filetypes.special, - f if f.is_immediate() => colours.filetypes.immediate, - f if f.is_image() => colours.filetypes.image, - f if f.is_video() => colours.filetypes.video, - f if f.is_music() => colours.filetypes.music, - f if f.is_lossless() => colours.filetypes.lossless, - f if f.is_crypto() => colours.filetypes.crypto, - f if f.is_document() => colours.filetypes.document, - f if f.is_compressed() => colours.filetypes.compressed, - f if f.is_temp() => colours.filetypes.temp, - f if f.is_compiled() => colours.filetypes.compiled, - _ => colours.filetypes.normal, - } -} +pub mod file_name;