2017-05-01 21:26:16 +00:00
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
2017-05-01 14:17:07 +00:00
|
|
|
|
use ansi_term::{ANSIString, Style};
|
|
|
|
|
|
|
|
|
|
use fs::{File, FileTarget};
|
|
|
|
|
use output::Colours;
|
2017-05-01 20:54:53 +00:00
|
|
|
|
use output::escape;
|
2017-05-01 14:17:07 +00:00
|
|
|
|
use output::cell::TextCellContents;
|
|
|
|
|
|
|
|
|
|
|
2017-07-08 11:11:11 +00:00
|
|
|
|
/// Basically a file name factory.
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
|
pub struct FileStyle {
|
|
|
|
|
|
|
|
|
|
/// Whether to append file class characters to file names.
|
|
|
|
|
pub classify: Classify,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileStyle {
|
|
|
|
|
|
|
|
|
|
/// Create a new `FileName` that prints the given file’s name, painting it
|
|
|
|
|
/// with the remaining arguments.
|
2017-07-08 11:24:22 +00:00
|
|
|
|
pub fn for_file<'a, 'dir>(&self, file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
|
|
|
|
|
FileName {
|
|
|
|
|
file, colours,
|
|
|
|
|
link_style: LinkStyle::JustFilenames,
|
|
|
|
|
classify: self.classify,
|
|
|
|
|
target: if file.is_link() { Some(file.link_target()) }
|
|
|
|
|
else { None }
|
|
|
|
|
}
|
2017-07-08 11:11:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// When displaying a file name, there needs to be some way to handle broken
|
|
|
|
|
/// links, depending on how long the resulting Cell can be.
|
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
2017-07-08 11:24:22 +00:00
|
|
|
|
enum LinkStyle {
|
2017-07-08 11:11:11 +00:00
|
|
|
|
|
|
|
|
|
/// Just display the file names, but colour them differently if they’re
|
|
|
|
|
/// a broken link or can’t be followed.
|
|
|
|
|
JustFilenames,
|
|
|
|
|
|
|
|
|
|
/// Display all files in their usual style, but follow each link with an
|
|
|
|
|
/// arrow pointing to their path, colouring the path differently if it’s
|
|
|
|
|
/// a broken link, and doing nothing if it can’t be followed.
|
|
|
|
|
FullLinkPaths,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Whether to append file class characters to the file names.
|
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
|
|
|
|
pub enum Classify {
|
|
|
|
|
|
|
|
|
|
/// Just display the file names, without any characters.
|
|
|
|
|
JustFilenames,
|
|
|
|
|
|
|
|
|
|
/// Add a character after the file name depending on what class of file
|
|
|
|
|
/// it is.
|
|
|
|
|
AddFileIndicators,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Classify {
|
|
|
|
|
fn default() -> Classify {
|
|
|
|
|
Classify::JustFilenames
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
/// A **file name** holds all the information necessary to display the name
|
|
|
|
|
/// of the given file. This is used in all of the views.
|
|
|
|
|
pub struct FileName<'a, 'dir: 'a> {
|
2017-05-07 13:08:36 +00:00
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
/// A reference to the file that we're getting the name of.
|
|
|
|
|
file: &'a File<'dir>,
|
2017-05-07 13:08:36 +00:00
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
/// The colours used to paint the file name and its surrounding text.
|
2017-05-01 14:37:02 +00:00
|
|
|
|
colours: &'a Colours,
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
|
|
|
|
/// The file that this file points to if it's a link.
|
|
|
|
|
target: Option<FileTarget<'dir>>,
|
|
|
|
|
|
|
|
|
|
/// How to handle displaying links.
|
2017-05-07 13:08:36 +00:00
|
|
|
|
link_style: LinkStyle,
|
2017-05-07 14:31:00 +00:00
|
|
|
|
|
|
|
|
|
/// Whether to append file class characters to file names.
|
|
|
|
|
classify: Classify,
|
2017-05-01 14:37:02 +00:00
|
|
|
|
}
|
2017-05-01 14:17:07 +00:00
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
2017-05-01 14:37:02 +00:00
|
|
|
|
impl<'a, 'dir> FileName<'a, 'dir> {
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
2017-07-08 11:24:22 +00:00
|
|
|
|
/// Sets the flag on this file name to display link targets with an
|
|
|
|
|
/// arrow followed by their path.
|
|
|
|
|
pub fn with_link_paths(mut self) -> Self {
|
|
|
|
|
self.link_style = LinkStyle::FullLinkPaths;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
/// Paints the name of the file using the colours, resulting in a vector
|
|
|
|
|
/// of coloured cells that can be printed to the terminal.
|
|
|
|
|
///
|
|
|
|
|
/// This method returns some `TextCellContents`, rather than a `TextCell`,
|
|
|
|
|
/// because for the last cell in a table, it doesn’t need to have its
|
|
|
|
|
/// width calculated.
|
2017-05-07 14:31:00 +00:00
|
|
|
|
pub fn paint(&self) -> TextCellContents {
|
2017-05-01 14:37:02 +00:00
|
|
|
|
let mut bits = Vec::new();
|
2017-05-01 14:17:07 +00:00
|
|
|
|
|
2017-06-29 12:07:45 +00:00
|
|
|
|
if self.file.parent_dir.is_none() {
|
2017-05-01 14:37:02 +00:00
|
|
|
|
if let Some(parent) = self.file.path.parent() {
|
2017-05-01 21:26:16 +00:00
|
|
|
|
self.add_parent_bits(&mut bits, parent);
|
2017-05-01 14:17:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-01 14:37:02 +00:00
|
|
|
|
if !self.file.name.is_empty() {
|
|
|
|
|
for bit in self.coloured_file_name() {
|
|
|
|
|
bits.push(bit);
|
|
|
|
|
}
|
2017-05-01 14:17:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-07 16:15:22 +00:00
|
|
|
|
if let (LinkStyle::FullLinkPaths, Some(target)) = (self.link_style, self.target.as_ref()) {
|
|
|
|
|
match *target {
|
2017-05-07 13:08:36 +00:00
|
|
|
|
FileTarget::Ok(ref target) => {
|
2017-05-01 14:37:02 +00:00
|
|
|
|
bits.push(Style::default().paint(" "));
|
|
|
|
|
bits.push(self.colours.punctuation.paint("->"));
|
|
|
|
|
bits.push(Style::default().paint(" "));
|
|
|
|
|
|
|
|
|
|
if let Some(parent) = target.path.parent() {
|
2017-05-01 21:26:16 +00:00
|
|
|
|
self.add_parent_bits(&mut bits, parent);
|
2017-05-01 14:17:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-01 14:37:02 +00:00
|
|
|
|
if !target.name.is_empty() {
|
2017-07-08 11:11:11 +00:00
|
|
|
|
let target = FileName {
|
|
|
|
|
file: target,
|
|
|
|
|
colours: self.colours,
|
|
|
|
|
target: None,
|
|
|
|
|
link_style: LinkStyle::FullLinkPaths,
|
|
|
|
|
classify: Classify::JustFilenames,
|
|
|
|
|
};
|
|
|
|
|
|
2017-05-01 15:53:51 +00:00
|
|
|
|
for bit in target.coloured_file_name() {
|
|
|
|
|
bits.push(bit);
|
|
|
|
|
}
|
2017-05-01 14:37:02 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2017-05-01 14:17:07 +00:00
|
|
|
|
|
2017-05-07 13:08:36 +00:00
|
|
|
|
FileTarget::Broken(ref broken_path) => {
|
2017-05-01 14:37:02 +00:00
|
|
|
|
bits.push(Style::default().paint(" "));
|
|
|
|
|
bits.push(self.colours.broken_arrow.paint("->"));
|
|
|
|
|
bits.push(Style::default().paint(" "));
|
2017-05-02 07:46:43 +00:00
|
|
|
|
escape(broken_path.display().to_string(), &mut bits, self.colours.broken_filename, self.colours.control_char.underline());
|
2017-05-01 14:37:02 +00:00
|
|
|
|
},
|
2017-05-01 14:17:07 +00:00
|
|
|
|
|
2017-05-07 13:08:36 +00:00
|
|
|
|
FileTarget::Err(_) => {
|
2017-05-01 14:37:02 +00:00
|
|
|
|
// Do nothing -- the error gets displayed on the next line
|
2017-05-07 09:44:09 +00:00
|
|
|
|
},
|
2017-05-01 14:37:02 +00:00
|
|
|
|
}
|
2017-05-01 14:39:55 +00:00
|
|
|
|
}
|
2017-05-07 14:31:00 +00:00
|
|
|
|
else if let Classify::AddFileIndicators = self.classify {
|
2017-05-01 14:39:55 +00:00
|
|
|
|
if let Some(class) = self.classify_char() {
|
|
|
|
|
bits.push(Style::default().paint(class));
|
2017-05-01 14:17:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-01 14:37:02 +00:00
|
|
|
|
bits.into()
|
2017-05-01 14:17:07 +00:00
|
|
|
|
}
|
2017-05-01 14:37:02 +00:00
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
2017-05-01 21:26:16 +00:00
|
|
|
|
/// Adds the bits of the parent path to the given bits vector.
|
|
|
|
|
/// The path gets its characters escaped based on the colours.
|
|
|
|
|
fn add_parent_bits(&self, bits: &mut Vec<ANSIString>, parent: &Path) {
|
|
|
|
|
let coconut = parent.components().count();
|
|
|
|
|
|
|
|
|
|
if coconut == 1 && parent.has_root() {
|
|
|
|
|
bits.push(self.colours.symlink_path.paint("/"));
|
|
|
|
|
}
|
|
|
|
|
else if coconut >= 1 {
|
|
|
|
|
escape(parent.to_string_lossy().to_string(), bits, self.colours.symlink_path, self.colours.control_char);
|
|
|
|
|
bits.push(self.colours.symlink_path.paint("/"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
2017-05-01 21:26:16 +00:00
|
|
|
|
/// The character to be displayed after a file when classifying is on, if
|
|
|
|
|
/// the file’s type has one associated with it.
|
2017-05-01 14:39:55 +00:00
|
|
|
|
fn classify_char(&self) -> Option<&'static str> {
|
|
|
|
|
if self.file.is_executable_file() {
|
|
|
|
|
Some("*")
|
|
|
|
|
} else if self.file.is_directory() {
|
|
|
|
|
Some("/")
|
|
|
|
|
} else if self.file.is_pipe() {
|
|
|
|
|
Some("|")
|
|
|
|
|
} else if self.file.is_link() {
|
|
|
|
|
Some("@")
|
|
|
|
|
} else if self.file.is_socket() {
|
|
|
|
|
Some("=")
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
2017-05-01 14:37:02 +00:00
|
|
|
|
/// 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<'unused>(&self) -> Vec<ANSIString<'unused>> {
|
2017-05-01 14:43:27 +00:00
|
|
|
|
let file_style = self.style();
|
2017-05-01 14:37:02 +00:00
|
|
|
|
let mut bits = Vec::new();
|
2017-05-01 20:54:53 +00:00
|
|
|
|
escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char);
|
2017-05-01 14:37:02 +00:00
|
|
|
|
bits
|
|
|
|
|
}
|
2017-05-01 14:17:07 +00:00
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
|
|
|
|
/// Figures out which colour to paint the filename part of the output,
|
|
|
|
|
/// depending on which “type” of file it appears to be -- either from the
|
|
|
|
|
/// class on the filesystem or from its name.
|
2017-05-01 14:43:27 +00:00
|
|
|
|
pub fn style(&self) -> Style {
|
2017-05-07 14:14:06 +00:00
|
|
|
|
|
|
|
|
|
// Override the style with the “broken link” style when this file is
|
|
|
|
|
// a link that we can’t follow for whatever reason. This is used when
|
|
|
|
|
// there’s no other place to show that the link doesn’t work.
|
2017-05-07 13:45:04 +00:00
|
|
|
|
if let LinkStyle::JustFilenames = self.link_style {
|
|
|
|
|
if let Some(ref target) = self.target {
|
|
|
|
|
if target.is_broken() {
|
|
|
|
|
return self.colours.broken_arrow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-07 14:14:06 +00:00
|
|
|
|
// Otherwise, just apply a bunch of rules in order. For example,
|
|
|
|
|
// executable image files should be executable rather than images.
|
2017-05-01 14:37:02 +00:00
|
|
|
|
match self.file {
|
|
|
|
|
f if f.is_directory() => self.colours.filetypes.directory,
|
|
|
|
|
f if f.is_executable_file() => self.colours.filetypes.executable,
|
|
|
|
|
f if f.is_link() => self.colours.filetypes.symlink,
|
|
|
|
|
f if f.is_pipe() => self.colours.filetypes.pipe,
|
|
|
|
|
f if f.is_char_device()
|
|
|
|
|
| f.is_block_device() => self.colours.filetypes.device,
|
|
|
|
|
f if f.is_socket() => self.colours.filetypes.socket,
|
|
|
|
|
f if !f.is_file() => self.colours.filetypes.special,
|
|
|
|
|
f if f.is_immediate() => self.colours.filetypes.immediate,
|
|
|
|
|
f if f.is_image() => self.colours.filetypes.image,
|
|
|
|
|
f if f.is_video() => self.colours.filetypes.video,
|
|
|
|
|
f if f.is_music() => self.colours.filetypes.music,
|
|
|
|
|
f if f.is_lossless() => self.colours.filetypes.lossless,
|
|
|
|
|
f if f.is_crypto() => self.colours.filetypes.crypto,
|
|
|
|
|
f if f.is_document() => self.colours.filetypes.document,
|
|
|
|
|
f if f.is_compressed() => self.colours.filetypes.compressed,
|
|
|
|
|
f if f.is_temp() => self.colours.filetypes.temp,
|
|
|
|
|
f if f.is_compiled() => self.colours.filetypes.compiled,
|
|
|
|
|
_ => self.colours.filetypes.normal,
|
|
|
|
|
}
|
2017-05-01 14:17:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|