Skip to content

Commit

Permalink
Merge branch 'file-name-refactoring'
Browse files Browse the repository at this point in the history
  • Loading branch information
ogham committed May 2, 2017
2 parents 3ebc225 + cac8041 commit 609bafe
Show file tree
Hide file tree
Showing 23 changed files with 387 additions and 201 deletions.
10 changes: 10 additions & 0 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Vagrant.configure(2) do |config|
echo -ne "#{test_dir}/file-names/invalid-utf8-2: [\\xc3\\x28]" | xargs -0 touch
echo -ne "#{test_dir}/file-names/invalid-utf8-3: [\\xe2\\x82\\x28]" | xargs -0 touch
echo -ne "#{test_dir}/file-names/invalid-utf8-4: [\\xf0\\x28\\x8c\\x28]" | xargs -0 touch
echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]" | xargs -0 mkdir
echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/subfile" | xargs -0 touch
echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/another: [\\n]" | xargs -0 touch
echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/broken" | xargs -0 touch
mkdir "#{test_dir}/file-names/links"
ln -s "#{test_dir}/file-names/new-line-dir"*/* "#{test_dir}/file-names/links"
echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/broken" | xargs -0 rm
EOF


Expand Down
7 changes: 6 additions & 1 deletion src/exa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ use std::ffi::OsStr;
use std::io::{stderr, Write, Result as IOResult};
use std::path::{Component, Path};

use ansi_term::{ANSIStrings, Style};

use fs::{Dir, File};
use options::{Options, View};
pub use options::Misfire;
use output::escape;

mod fs;
mod info;
Expand Down Expand Up @@ -116,7 +119,9 @@ impl<'w, W: Write + 'w> Exa<'w, W> {
}

if !is_only_dir {
writeln!(self.writer, "{}:", dir.path.display())?;
let mut bits = Vec::new();
escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default());
writeln!(self.writer, "{}:", ANSIStrings(&bits))?;
}

let mut children = Vec::new();
Expand Down
7 changes: 5 additions & 2 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//! Files, and methods and fields to access their metadata.

use std::ascii::AsciiExt;
use std::env::current_dir;
use std::fs;
use std::io::Error as IOError;
use std::io::Result as IOResult;
Expand All @@ -14,6 +12,7 @@ use fs::fields as f;
#[cfg(any(target_os = "macos", target_os = "linux"))]
use std::os::unix::fs::FileTypeExt;


/// Constant table copied from https://doc.rust-lang.org/src/std/sys/unix/ext/fs.rs.html#11-259
/// which is currently unstable and lacks vision for stabilization,
/// see https://github.com/rust-lang/rust/issues/27712
Expand Down Expand Up @@ -344,6 +343,8 @@ impl<'dir> File<'dir> {
/// directory, so will not work if this file has just been passed in on
/// the command line.
pub fn git_status(&self) -> f::Git {
use std::env::current_dir;

match self.dir {
None => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified },
Some(d) => {
Expand Down Expand Up @@ -421,6 +422,8 @@ impl<'a> AsRef<File<'a>> for File<'a> {
/// against a pre-compiled list of extensions which are known to only exist
/// within ASCII, so it's alright.
fn ext(path: &Path) -> Option<String> {
use std::ascii::AsciiExt;

let name = match path.file_name() {
Some(f) => f.to_string_lossy().to_string(),
None => return None,
Expand Down
11 changes: 11 additions & 0 deletions src/output/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,21 @@ impl TextCellContents {
ANSIStrings(&self.0)
}

/// Calculates the width that a cell with these contents would take up, by
/// counting the number of characters in each unformatted ANSI string.
pub fn width(&self) -> DisplayWidth {
let foo = self.0.iter().map(|anstr| anstr.chars().count()).sum();
DisplayWidth(foo)
}

/// Promotes these contents to a full cell containing them alongside
/// their calculated width.
pub fn promote(self) -> TextCell {
TextCell {
width: self.width(),
contents: self,
}
}
}


Expand Down
36 changes: 5 additions & 31 deletions src/output/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ use fs::feature::xattr::{Attribute, FileAttributes};
use options::{FileFilter, RecurseOptions};
use output::colours::Colours;
use output::column::{Alignment, Column, Columns, SizeFormat};
use output::cell::{TextCell, DisplayWidth};
use output::cell::{TextCell, TextCellContents, DisplayWidth};
use output::tree::TreeTrunk;
use super::filename;
use output::file_name::FileName;


/// With the **Details** view, the output gets formatted into columns, with
Expand Down Expand Up @@ -307,24 +307,10 @@ impl Details {
let mut files = Vec::new();
let mut errors = egg.errors;

let filename = filename(&egg.file, &self.colours, true, self.classify);
let mut width = filename.width();

if egg.file.dir.is_none() {
if let Some(parent) = egg.file.path.parent() {
width = width + 1 + DisplayWidth::from(parent.to_string_lossy().as_ref());
}
}

let name = TextCell {
contents: filename,
width: width,
};

let row = Row {
depth: depth,
cells: Some(egg.cells),
name: name,
name: FileName::new(&egg.file, &self.colours).paint(true, self.classify).promote(),
last: index == num_eggs - 1,
};

Expand Down Expand Up @@ -457,20 +443,8 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> {
self.rows.push(row);
}

pub fn filename_cell(&self, file: File, links: bool) -> TextCell {
let filename = filename(&file, &self.opts.colours, links, self.opts.classify);
let mut width = filename.width();

if file.dir.is_none() {
if let Some(parent) = file.path.parent() {
width = width + 1 + DisplayWidth::from(parent.to_string_lossy().as_ref());
}
}

TextCell {
contents: filename,
width: width,
}
pub fn filename(&self, file: File, links: bool) -> TextCellContents {
FileName::new(&file, &self.opts.colours).paint(links, self.opts.classify)
}

pub fn add_file_with_cells(&mut self, cells: Vec<TextCell>, name_cell: TextCell, depth: usize, last: bool) {
Expand Down
25 changes: 25 additions & 0 deletions src/output/escape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use ansi_term::{ANSIString, Style};


pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
if string.chars().all(|c| c >= 0x20 as char) {
bits.push(good.paint(string));
}
else {
for c in string.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(good.paint(s));
} else {
let s = c.escape_default().collect::<String>();
bits.push(bad.paint(s));
}
}
}
}
151 changes: 151 additions & 0 deletions src/output/file_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use std::path::Path;

use ansi_term::{ANSIString, Style};

use fs::{File, FileTarget};
use output::Colours;
use output::escape;
use output::cell::TextCellContents;


pub struct FileName<'a, 'dir: 'a> {
file: &'a File<'dir>,
colours: &'a Colours,
}

impl<'a, 'dir> FileName<'a, 'dir> {
pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
FileName {
file: file,
colours: colours,
}
}

pub fn paint(&self, links: bool, classify: bool) -> TextCellContents {
let mut bits = Vec::new();

if self.file.dir.is_none() {
if let Some(parent) = self.file.path.parent() {
self.add_parent_bits(&mut bits, parent);
}
}

if !self.file.name.is_empty() {
for bit in self.coloured_file_name() {
bits.push(bit);
}
}

if links && self.file.is_link() {
match self.file.link_target() {
FileTarget::Ok(target) => {
bits.push(Style::default().paint(" "));
bits.push(self.colours.punctuation.paint("->"));
bits.push(Style::default().paint(" "));

if let Some(parent) = target.path.parent() {
self.add_parent_bits(&mut bits, parent);
}

if !target.name.is_empty() {
let target = FileName::new(&target, self.colours);
for bit in target.coloured_file_name() {
bits.push(bit);
}
}
},

FileTarget::Broken(broken_path) => {
bits.push(Style::default().paint(" "));
bits.push(self.colours.broken_arrow.paint("->"));
bits.push(Style::default().paint(" "));
escape(broken_path.display().to_string(), &mut bits, self.colours.broken_filename, self.colours.control_char.underline());
},

FileTarget::Err(_) => {
// Do nothing -- the error gets displayed on the next line
}
}
}
else if classify {
if let Some(class) = self.classify_char() {
bits.push(Style::default().paint(class));
}
}

bits.into()
}

/// 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("/"));
}
}

/// The character to be displayed after a file when classifying is on, if
/// the file’s type has one associated with it.
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
}
}

/// 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>> {
let file_style = self.style();
let mut bits = Vec::new();
escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char);
bits
}

pub fn style(&self) -> Style {
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,
}
}
}
16 changes: 5 additions & 11 deletions src/output/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use std::io::{Write, Result as IOResult};
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)]
Expand All @@ -29,14 +28,8 @@ impl Grid {
grid.reserve(files.len());

for file in files.iter() {
let filename = filename(file, &self.colours, false, self.classify);

let mut width = filename.width();
if file.dir.is_none() {
if let Some(parent) = file.path.parent() {
width = width + 1 + DisplayWidth::from(parent.to_string_lossy().as_ref());
}
}
let filename = FileName::new(file, &self.colours).paint(false, self.classify);
let width = filename.width();

grid.add(grid::Cell {
contents: filename.strings().to_string(),
Expand All @@ -50,7 +43,8 @@ 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, self.classify).strings())?;
let name_cell = FileName::new(file, &self.colours).paint(false, self.classify);
writeln!(w, "{}", name_cell.strings())?;
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/output/grid_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl GridDetails {
.collect::<Vec<_>>();

let file_names = files.into_iter()
.map(|file| first_table.filename_cell(file, false))
.map(|file| first_table.filename(file, false).promote())
.collect::<Vec<_>>();

(cells, file_names)
Expand Down
Loading

0 comments on commit 609bafe

Please sign in to comment.