Skip to content

Commit e819dd9

Browse files
committed
Merge branch 'more-file-name-fields'
2 parents 897d6ff + e916097 commit e819dd9

File tree

9 files changed

+142
-31
lines changed

9 files changed

+142
-31
lines changed

src/fs/file.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,11 +407,24 @@ pub enum FileTarget<'dir> {
407407
Broken(PathBuf),
408408

409409
/// There was an IO error when following the link. This can happen if the
410-
/// file isn't a link to begin with, but also if, say, we don't have
410+
/// file isnt a link to begin with, but also if, say, we dont have
411411
/// permission to follow it.
412412
Err(IOError),
413413
}
414414

415+
impl<'dir> FileTarget<'dir> {
416+
417+
/// Whether this link doesn’t lead to a file, for whatever reason. This
418+
/// gets used to determine how to highlight the link in grid views.
419+
pub fn is_broken(&self) -> bool {
420+
match self {
421+
&FileTarget::Ok(_) => false,
422+
&FileTarget::Broken(_) => true,
423+
&FileTarget::Err(_) => true,
424+
}
425+
}
426+
}
427+
415428

416429
#[cfg(test)]
417430
mod test {

src/options/view.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use getopts;
44

55
use output::Colours;
66
use output::{Grid, Details, GridDetails, Lines};
7-
use options::{FileFilter, DirAction, Misfire};
87
use output::column::{Columns, TimeTypes, SizeFormat};
8+
use output::file_name::Classify;
9+
use options::{FileFilter, DirAction, Misfire};
910
use term::dimensions;
1011
use fs::feature::xattr;
1112

@@ -58,7 +59,7 @@ impl View {
5859
filter: filter.clone(),
5960
xattr: xattr::ENABLED && matches.opt_present("extended"),
6061
colours: colours,
61-
classify: matches.opt_present("classify"),
62+
classify: Classify::deduce(matches),
6263
};
6364

6465
Ok(details)
@@ -87,8 +88,7 @@ impl View {
8788
};
8889

8990
let other_options_scan = || {
90-
let classify = matches.opt_present("classify");
91-
91+
let classify = Classify::deduce(matches);
9292
let term_colours = TerminalColours::deduce(matches)?;
9393
let term_width = TerminalWidth::deduce()?;
9494

@@ -366,3 +366,12 @@ impl TerminalColours {
366366
}
367367
}
368368
}
369+
370+
371+
372+
impl Classify {
373+
fn deduce(matches: &getopts::Matches) -> Classify {
374+
if matches.opt_present("classify") { Classify::AddFileIndicators }
375+
else { Classify::JustFilenames }
376+
}
377+
}

src/output/details.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ use output::colours::Colours;
101101
use output::column::{Alignment, Column, Columns, SizeFormat};
102102
use output::cell::{TextCell, TextCellContents, DisplayWidth};
103103
use output::tree::TreeTrunk;
104-
use output::file_name::FileName;
104+
use output::file_name::{FileName, LinkStyle, Classify};
105105

106106

107107
/// With the **Details** view, the output gets formatted into columns, with
@@ -142,7 +142,7 @@ pub struct Details {
142142
pub colours: Colours,
143143

144144
/// Whether to show a file type indiccator.
145-
pub classify: bool,
145+
pub classify: Classify,
146146
}
147147

148148
/// The **environment** struct contains any data that could change between
@@ -310,7 +310,7 @@ impl Details {
310310
let row = Row {
311311
depth: depth,
312312
cells: Some(egg.cells),
313-
name: FileName::new(&egg.file, &self.colours).paint(true, self.classify).promote(),
313+
name: FileName::new(&egg.file, LinkStyle::FullLinkPaths, self.classify, &self.colours).paint().promote(),
314314
last: index == num_eggs - 1,
315315
};
316316

@@ -443,8 +443,8 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> {
443443
self.rows.push(row);
444444
}
445445

446-
pub fn filename(&self, file: File, links: bool) -> TextCellContents {
447-
FileName::new(&file, &self.opts.colours).paint(links, self.opts.classify)
446+
pub fn filename(&self, file: File, links: LinkStyle) -> TextCellContents {
447+
FileName::new(&file, links, self.opts.classify, &self.opts.colours).paint()
448448
}
449449

450450
pub fn add_file_with_cells(&mut self, cells: Vec<TextCell>, name_cell: TextCell, depth: usize, last: bool) {

src/output/file_name.rs

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,51 @@ use output::escape;
88
use output::cell::TextCellContents;
99

1010

11+
/// A **file name** holds all the information necessary to display the name
12+
/// of the given file. This is used in all of the views.
1113
pub struct FileName<'a, 'dir: 'a> {
12-
file: &'a File<'dir>,
14+
15+
/// A reference to the file that we're getting the name of.
16+
file: &'a File<'dir>,
17+
18+
/// The colours used to paint the file name and its surrounding text.
1319
colours: &'a Colours,
20+
21+
/// The file that this file points to if it's a link.
22+
target: Option<FileTarget<'dir>>,
23+
24+
/// How to handle displaying links.
25+
link_style: LinkStyle,
26+
27+
/// Whether to append file class characters to file names.
28+
classify: Classify,
1429
}
1530

31+
1632
impl<'a, 'dir> FileName<'a, 'dir> {
17-
pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
33+
34+
/// Create a new `FileName` that prints the given file’s name, painting it
35+
/// with the remaining arguments.
36+
pub fn new(file: &'a File<'dir>, link_style: LinkStyle, classify: Classify, colours: &'a Colours) -> FileName<'a, 'dir> {
37+
let target = if file.is_link() { Some(file.link_target()) }
38+
else { None };
1839
FileName {
1940
file: file,
2041
colours: colours,
42+
target: target,
43+
link_style: link_style,
44+
classify: classify,
2145
}
2246
}
2347

24-
pub fn paint(&self, links: bool, classify: bool) -> TextCellContents {
48+
49+
/// Paints the name of the file using the colours, resulting in a vector
50+
/// of coloured cells that can be printed to the terminal.
51+
///
52+
/// This method returns some `TextCellContents`, rather than a `TextCell`,
53+
/// because for the last cell in a table, it doesn’t need to have its
54+
/// width calculated.
55+
pub fn paint(&self) -> TextCellContents {
2556
let mut bits = Vec::new();
2657

2758
if self.file.dir.is_none() {
@@ -36,9 +67,9 @@ impl<'a, 'dir> FileName<'a, 'dir> {
3667
}
3768
}
3869

39-
if links && self.file.is_link() {
40-
match self.file.link_target() {
41-
FileTarget::Ok(target) => {
70+
if let (LinkStyle::FullLinkPaths, Some(ref target)) = (self.link_style, self.target.as_ref()) {
71+
match **target {
72+
FileTarget::Ok(ref target) => {
4273
bits.push(Style::default().paint(" "));
4374
bits.push(self.colours.punctuation.paint("->"));
4475
bits.push(Style::default().paint(" "));
@@ -48,14 +79,14 @@ impl<'a, 'dir> FileName<'a, 'dir> {
4879
}
4980

5081
if !target.name.is_empty() {
51-
let target = FileName::new(&target, self.colours);
82+
let target = FileName::new(&target, LinkStyle::FullLinkPaths, Classify::JustFilenames, self.colours);
5283
for bit in target.coloured_file_name() {
5384
bits.push(bit);
5485
}
5586
}
5687
},
5788

58-
FileTarget::Broken(broken_path) => {
89+
FileTarget::Broken(ref broken_path) => {
5990
bits.push(Style::default().paint(" "));
6091
bits.push(self.colours.broken_arrow.paint("->"));
6192
bits.push(Style::default().paint(" "));
@@ -64,10 +95,10 @@ impl<'a, 'dir> FileName<'a, 'dir> {
6495

6596
FileTarget::Err(_) => {
6697
// Do nothing -- the error gets displayed on the next line
67-
}
98+
},
6899
}
69100
}
70-
else if classify {
101+
else if let Classify::AddFileIndicators = self.classify {
71102
if let Some(class) = self.classify_char() {
72103
bits.push(Style::default().paint(class));
73104
}
@@ -76,6 +107,7 @@ impl<'a, 'dir> FileName<'a, 'dir> {
76107
bits.into()
77108
}
78109

110+
79111
/// Adds the bits of the parent path to the given bits vector.
80112
/// The path gets its characters escaped based on the colours.
81113
fn add_parent_bits(&self, bits: &mut Vec<ANSIString>, parent: &Path) {
@@ -90,6 +122,7 @@ impl<'a, 'dir> FileName<'a, 'dir> {
90122
}
91123
}
92124

125+
93126
/// The character to be displayed after a file when classifying is on, if
94127
/// the file’s type has one associated with it.
95128
fn classify_char(&self) -> Option<&'static str> {
@@ -108,6 +141,7 @@ impl<'a, 'dir> FileName<'a, 'dir> {
108141
}
109142
}
110143

144+
111145
/// Returns at least one ANSI-highlighted string representing this file’s
112146
/// name using the given set of colours.
113147
///
@@ -125,7 +159,25 @@ impl<'a, 'dir> FileName<'a, 'dir> {
125159
bits
126160
}
127161

162+
163+
/// Figures out which colour to paint the filename part of the output,
164+
/// depending on which “type” of file it appears to be -- either from the
165+
/// class on the filesystem or from its name.
128166
pub fn style(&self) -> Style {
167+
168+
// Override the style with the “broken link” style when this file is
169+
// a link that we can’t follow for whatever reason. This is used when
170+
// there’s no other place to show that the link doesn’t work.
171+
if let LinkStyle::JustFilenames = self.link_style {
172+
if let Some(ref target) = self.target {
173+
if target.is_broken() {
174+
return self.colours.broken_arrow;
175+
}
176+
}
177+
}
178+
179+
// Otherwise, just apply a bunch of rules in order. For example,
180+
// executable image files should be executable rather than images.
129181
match self.file {
130182
f if f.is_directory() => self.colours.filetypes.directory,
131183
f if f.is_executable_file() => self.colours.filetypes.executable,
@@ -149,3 +201,38 @@ impl<'a, 'dir> FileName<'a, 'dir> {
149201
}
150202
}
151203
}
204+
205+
206+
/// When displaying a file name, there needs to be some way to handle broken
207+
/// links, depending on how long the resulting Cell can be.
208+
#[derive(PartialEq, Debug, Copy, Clone)]
209+
pub enum LinkStyle {
210+
211+
/// Just display the file names, but colour them differently if they’re
212+
/// a broken link or can’t be followed.
213+
JustFilenames,
214+
215+
/// Display all files in their usual style, but follow each link with an
216+
/// arrow pointing to their path, colouring the path differently if it’s
217+
/// a broken link, and doing nothing if it can’t be followed.
218+
FullLinkPaths,
219+
}
220+
221+
222+
/// Whether to append file class characters to the file names.
223+
#[derive(PartialEq, Debug, Copy, Clone)]
224+
pub enum Classify {
225+
226+
/// Just display the file names, without any characters.
227+
JustFilenames,
228+
229+
/// Add a character after the file name depending on what class of file
230+
/// it is.
231+
AddFileIndicators,
232+
}
233+
234+
impl Default for Classify {
235+
fn default() -> Classify {
236+
Classify::JustFilenames
237+
}
238+
}

src/output/grid.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ use term_grid as grid;
44

55
use fs::File;
66
use output::colours::Colours;
7-
use output::file_name::FileName;
7+
use output::file_name::{FileName, LinkStyle, Classify};
88

99

1010
#[derive(PartialEq, Debug, Copy, Clone)]
1111
pub struct Grid {
1212
pub across: bool,
1313
pub console_width: usize,
1414
pub colours: Colours,
15-
pub classify: bool,
15+
pub classify: Classify,
1616
}
1717

1818
impl Grid {
@@ -28,7 +28,7 @@ impl Grid {
2828
grid.reserve(files.len());
2929

3030
for file in files.iter() {
31-
let filename = FileName::new(file, &self.colours).paint(false, self.classify);
31+
let filename = FileName::new(file, LinkStyle::JustFilenames, self.classify, &self.colours).paint();
3232
let width = filename.width();
3333

3434
grid.add(grid::Cell {
@@ -43,7 +43,7 @@ impl Grid {
4343
else {
4444
// File names too long for a grid - drop down to just listing them!
4545
for file in files.iter() {
46-
let name_cell = FileName::new(file, &self.colours).paint(false, self.classify);
46+
let name_cell = FileName::new(file, LinkStyle::JustFilenames, self.classify, &self.colours).paint();
4747
writeln!(w, "{}", name_cell.strings())?;
4848
}
4949
Ok(())

src/output/grid_details.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use output::cell::TextCell;
1212
use output::column::Column;
1313
use output::details::{Details, Table, Environment};
1414
use output::grid::Grid;
15+
use output::file_name::LinkStyle;
16+
1517

1618
#[derive(PartialEq, Debug, Clone)]
1719
pub struct GridDetails {
@@ -45,7 +47,7 @@ impl GridDetails {
4547
.collect::<Vec<_>>();
4648

4749
let file_names = files.into_iter()
48-
.map(|file| first_table.filename(file, false).promote())
50+
.map(|file| first_table.filename(file, LinkStyle::JustFilenames).promote())
4951
.collect::<Vec<_>>();
5052

5153
(cells, file_names)

src/output/lines.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ use ansi_term::ANSIStrings;
44

55
use fs::File;
66

7-
use output::file_name::FileName;
7+
use output::file_name::{FileName, LinkStyle, Classify};
88
use super::colours::Colours;
99

1010

1111
#[derive(Clone, Copy, Debug, PartialEq)]
1212
pub struct Lines {
1313
pub colours: Colours,
14-
pub classify: bool,
14+
pub classify: Classify,
1515
}
1616

1717
/// The lines view literally just displays each file, line-by-line.
1818
impl Lines {
1919
pub fn view<W: Write>(&self, files: Vec<File>, w: &mut W) -> IOResult<()> {
2020
for file in files {
21-
let name_cell = FileName::new(&file, &self.colours).paint(true, self.classify);
21+
let name_cell = FileName::new(&file, LinkStyle::FullLinkPaths, self.classify, &self.colours).paint();
2222
writeln!(w, "{}", ANSIStrings(&name_cell))?;
2323
}
2424
Ok(())

xtests/file_names_R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ emoji: [🆒] invalid-utf8-4: [�(�(] utf-8: pâté
66
escape: [\u{1b}] links vertical-tab: [\u{b}]
77

88
/testcases/file-names/links:
9-
another: [\n] broken subfile
9+
another: [\n] broken subfile
1010

1111
/testcases/file-names/new-line-dir: [\n]:
1212
another: [\n] subfile

xtests/links

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
broken forbidden parent_dir some_file some_file_relative
2-
current_dir itself root some_file_absolute usr
1+
broken forbidden parent_dir some_file some_file_relative
2+
current_dir itself root some_file_absolute usr

0 commit comments

Comments
 (0)