diff --git a/Vagrantfile b/Vagrantfile index 20bf680b5ce1840f1137b8cfa4eb74aac0b2fb36..60477bcbfda7b1eeef1e058833559c16171c21bb 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -172,6 +172,13 @@ 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 + + mkdir "#{test_dir}/file-names/links" + ln -s "#{test_dir}/file-names/new-line-dir"*/* "#{test_dir}/file-names/links" EOF diff --git a/src/exa.rs b/src/exa.rs index dc858de7f2a535e073420fbc3e07695ec08fc412..1d3d2d437f04fa90be5805565d0ba55e8567f35c 100644 --- a/src/exa.rs +++ b/src/exa.rs @@ -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; @@ -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(); diff --git a/src/output/escape.rs b/src/output/escape.rs new file mode 100644 index 0000000000000000000000000000000000000000..161c3938c9388d5e64f1abacdc0b88b317e6b69c --- /dev/null +++ b/src/output/escape.rs @@ -0,0 +1,25 @@ +use ansi_term::{ANSIString, Style}; + + +pub fn escape<'a>(string: String, bits: &mut Vec>, 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::(); + bits.push(bad.paint(s)); + } + } + } +} diff --git a/src/output/file_name.rs b/src/output/file_name.rs index e235b9bf2d3d0121c4aa07d6dbfe5df23b670c36..8b4e80fc722132eb08add08e6c5565791543fd5c 100644 --- a/src/output/file_name.rs +++ b/src/output/file_name.rs @@ -2,6 +2,7 @@ use ansi_term::{ANSIString, Style}; use fs::{File, FileTarget}; use output::Colours; +use output::escape; use output::cell::TextCellContents; @@ -29,7 +30,7 @@ impl<'a, 'dir> FileName<'a, 'dir> { bits.push(self.colours.symlink_path.paint("/")); } else if coconut >= 1 { - bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string())); + escape(parent.to_string_lossy().to_string(), &mut bits, self.colours.symlink_path, self.colours.control_char); bits.push(self.colours.symlink_path.paint("/")); } } @@ -55,7 +56,7 @@ impl<'a, 'dir> FileName<'a, 'dir> { bits.push(self.colours.symlink_path.paint("/")); } else if coconut >= 1 { - bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string())); + escape(parent.to_string_lossy().to_string(), &mut bits, self.colours.symlink_path, self.colours.control_char); bits.push(self.colours.symlink_path.paint("/")); } } @@ -118,28 +119,7 @@ impl<'a, 'dir> FileName<'a, 'dir> { fn coloured_file_name<'unused>(&self) -> Vec> { let file_style = self.style(); let mut bits = Vec::new(); - - if self.file.name.chars().all(|c| c >= 0x20 as char) { - bits.push(file_style.paint(self.file.name.clone())); - } - else { - for c in self.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(file_style.paint(s)); - } else { - let s = c.escape_default().collect::(); - bits.push(self.colours.control_char.paint(s)); - } - } - } - + escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char); bits } diff --git a/src/output/mod.rs b/src/output/mod.rs index 8f8a1496768e0a0fdc2785abcb8ec1b4e3cd110b..48b886eb89eb5719c213003c810d6bbce47f6b58 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -4,6 +4,7 @@ pub use self::details::Details; pub use self::grid_details::GridDetails; pub use self::grid::Grid; pub use self::lines::Lines; +pub use self::escape::escape; mod grid; pub mod details; @@ -14,3 +15,4 @@ mod cell; mod colours; mod tree; pub mod file_name; +mod escape; diff --git a/xtests/file_names b/xtests/file_names index 5120bb49680cc7aeeccff35d484e736e926eeb83..fcb50ced41cef220fd250c62147ce3d81b807a7f 100644 --- a/xtests/file_names +++ b/xtests/file_names @@ -1,6 +1,6 @@ -ansi: [\u{1b}[34mblue\u{1b}[0m] form-feed: [\u{c}] return: [\r] -ascii: hello invalid-utf8-1: [�] tab: [\t] -backspace: [\u{8}] invalid-utf8-2: [�(] utf-8: pâté -bell: [\u{7}] invalid-utf8-3: [�(] vertical-tab: [\u{b}] -emoji: [🆒] invalid-utf8-4: [�(�(] -escape: [\u{1b}] new-line: [\n] +ansi: [\u{1b}[34mblue\u{1b}[0m] form-feed: [\u{c}] new-line-dir: [\n] +ascii: hello invalid-utf8-1: [�] new-line: [\n] +backspace: [\u{8}] invalid-utf8-2: [�(] return: [\r] +bell: [\u{7}] invalid-utf8-3: [�(] tab: [\t] +emoji: [🆒] invalid-utf8-4: [�(�(] utf-8: pâté +escape: [\u{1b}] links vertical-tab: [\u{b}] diff --git a/xtests/file_names_1 b/xtests/file_names_1 index b0c25085e202ec7dea2759388efc9153c125ce7e..eb2b5d2d150fdc9cf27e5046c0d7c0e37a713625 100644 --- a/xtests/file_names_1 +++ b/xtests/file_names_1 @@ -9,6 +9,8 @@ invalid-utf8-1: [�] invalid-utf8-2: [�(] invalid-utf8-3: [�(] invalid-utf8-4: [�(�(] +links +new-line-dir: [\n] new-line: [\n] return: [\r] tab: [\t] diff --git a/xtests/file_names_R b/xtests/file_names_R new file mode 100644 index 0000000000000000000000000000000000000000..75cb640dca729bcd9f0b423ef5b68a7cb5edda7d --- /dev/null +++ b/xtests/file_names_R @@ -0,0 +1,12 @@ +ansi: [\u{1b}[34mblue\u{1b}[0m] form-feed: [\u{c}] new-line-dir: [\n] +ascii: hello invalid-utf8-1: [�] new-line: [\n] +backspace: [\u{8}] invalid-utf8-2: [�(] return: [\r] +bell: [\u{7}] invalid-utf8-3: [�(] tab: [\t] +emoji: [🆒] invalid-utf8-4: [�(�(] utf-8: pâté +escape: [\u{1b}] links vertical-tab: [\u{b}] + +/testcases/file-names/links: +another: [\n] subfile + +/testcases/file-names/new-line-dir: [\n]: +another: [\n] subfile diff --git a/xtests/file_names_T b/xtests/file_names_T new file mode 100644 index 0000000000000000000000000000000000000000..f185266bbf8150b5733ef25ae70a5b87b449cd9d --- /dev/null +++ b/xtests/file_names_T @@ -0,0 +1,27 @@ +/testcases/file-names +├── ansi: [\u{1b}[34mblue\u{1b}[0m] +├── ascii: hello +├── backspace: [\u{8}] +├── bell: [\u{7}] +├── emoji: [🆒] +├── escape: [\u{1b}] +├── form-feed: [\u{c}] +├── invalid-utf8-1: [�] +│ └──  +├── invalid-utf8-2: [�(] +│ └──  +├── invalid-utf8-3: [�(] +│ └──  +├── invalid-utf8-4: [�(�(] +│ └──  +├── links +│ ├── another: [\n] -> /testcases/file-names/new-line-dir: [\n]/another: [\n] +│ └── subfile -> /testcases/file-names/new-line-dir: [\n]/subfile +├── new-line-dir: [\n] +│ ├── another: [\n] +│ └── subfile +├── new-line: [\n] +├── return: [\r] +├── tab: [\t] +├── utf-8: pâté +└── vertical-tab: [\u{b}] diff --git a/xtests/file_names_x b/xtests/file_names_x index f5f22bc66a03fd9a55a4ae1e577a45a0f5e57255..0264fb388902082e57d9595cd32f5a9a30cd924c 100644 --- a/xtests/file_names_x +++ b/xtests/file_names_x @@ -1,6 +1,6 @@ ansi: [\u{1b}[34mblue\u{1b}[0m] ascii: hello backspace: [\u{8}] bell: [\u{7}] emoji: [🆒] escape: [\u{1b}] form-feed: [\u{c}] invalid-utf8-1: [�] invalid-utf8-2: [�(] -invalid-utf8-3: [�(] invalid-utf8-4: [�(�(] new-line: [\n] -return: [\r] tab: [\t] utf-8: pâté -vertical-tab: [\u{b}] +invalid-utf8-3: [�(] invalid-utf8-4: [�(�(] links +new-line-dir: [\n] new-line: [\n] return: [\r] +tab: [\t] utf-8: pâté vertical-tab: [\u{b}] diff --git a/xtests/run.sh b/xtests/run.sh index b234f16a1e3ace366aecfb17abb8d2d750abdad2..2b0237c2a7fb2d91b533e83833002efaea46dc71 100755 --- a/xtests/run.sh +++ b/xtests/run.sh @@ -55,9 +55,12 @@ sudo -u cassowary $exa $testcases/permissions -lghR 2>&1 | diff -q - $results/pe $exa $testcases/permissions -lghR 2>&1 | diff -q - $results/permissions || exit 1 # File names +# (Mostly escaping control characters in file names) COLUMNS=80 $exa $testcases/file-names 2>&1 | diff -q - $results/file_names || exit 1 COLUMNS=80 $exa $testcases/file-names -x 2>&1 | diff -q - $results/file_names_x || exit 1 +COLUMNS=80 $exa $testcases/file-names -R 2>&1 | diff -q - $results/file_names_R || exit 1 $exa $testcases/file-names -1 2>&1 | diff -q - $results/file_names_1 || exit 1 + $exa $testcases/file-names -T 2>&1 | diff -q - $results/file_names_T || exit 1 # File types $exa $testcases/file-names-exts -1 2>&1 | diff -q - $results/file-names-exts || exit 1