Explorar el Código

fix: filename escaping (last character lost sometimes, no hyperlink)

Mélanie Chauvel hace 2 años
padre
commit
7a04b30d5a
Se han modificado 4 ficheros con 62 adiciones y 61 borrados
  1. 22 4
      Cargo.lock
  2. 2 0
      Cargo.toml
  3. 17 11
      src/output/escape.rs
  4. 21 46
      src/output/file_name.rs

+ 22 - 4
Cargo.lock

@@ -84,6 +84,7 @@ version = "0.11.0"
 dependencies = [
  "ansiterm",
  "datetime",
+ "gethostname",
  "git2",
  "glob",
  "lazy_static",
@@ -99,6 +100,7 @@ dependencies = [
  "terminal_size",
  "timeago",
  "unicode-width",
+ "urlencoding",
  "uzers",
  "zoneinfo_compiled",
 ]
@@ -113,6 +115,16 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets",
+]
+
 [[package]]
 name = "git2"
 version = "0.18.0"
@@ -129,9 +141,9 @@ dependencies = [
 
 [[package]]
 name = "glob"
-version = "0.3.1"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
 [[package]]
 name = "hermit-abi"
@@ -409,9 +421,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
 
 [[package]]
 name = "syn"
-version = "2.0.29"
+version = "2.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -500,6 +512,12 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
 [[package]]
 name = "uzers"
 version = "0.11.2"

+ 2 - 0
Cargo.toml

@@ -37,6 +37,7 @@ name = "eza"
 
 [dependencies]
 ansiterm = "0.12.2"
+gethostname = "0.4.3"
 glob = "0.3"
 lazy_static = "1.3"
 libc = "0.2"
@@ -51,6 +52,7 @@ term_grid = "0.1"
 terminal_size = "0.2.6"
 timeago = { version = "0.4.1", default-features = false }
 unicode-width = "0.1"
+urlencoding = "2.1.3"
 zoneinfo_compiled = "0.5.1"
 
 [dependencies.datetime]

+ 17 - 11
src/output/escape.rs

@@ -2,25 +2,31 @@ use ansiterm::{ANSIString, Style};
 
 
 pub fn escape(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
-    if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
+    // if the string has no control character
+    if string.chars().all(|c| !c.is_control()) {
         bits.push(good.paint(string));
         return;
     }
 
+    // the lengthier string of non control character can’t be bigger than the whole string
+    let mut regular_char_buff = String::with_capacity(string.len());
     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 && c != 0x7f 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));
+        if c.is_control() {
+            if !regular_char_buff.is_empty() {
+                bits.push(good.paint(std::mem::take(&mut regular_char_buff)));
+            }
+            regular_char_buff.extend(c.escape_default());
+            // biased towards regular characters, we push control characters immediately
+            bits.push(bad.paint(std::mem::take(&mut regular_char_buff)));
+        } else {
+            regular_char_buff.push(c);
         }
     }
+    // if last character was not a control character, the buffer is not empty!
+    if !regular_char_buff.is_empty() {
+        bits.push(good.paint(std::mem::take(&mut regular_char_buff)));
+    }
 }

+ 21 - 46
src/output/file_name.rs

@@ -9,6 +9,8 @@ use crate::output::escape;
 use crate::output::icons::{icon_for_file, iconify_style};
 use crate::output::render::FiletypeColours;
 
+const HYPERLINK_START: &str = "\x1B]8;;";
+const HYPERLINK_END: &str = "\x1B\x5C";
 
 /// Basically a file name factory.
 #[derive(Debug, Copy, Clone)]
@@ -303,59 +305,32 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         let file_style = self.style();
         let mut bits = Vec::new();
 
-        self.escape_color_and_hyperlinks(
+        let mut display_hyperlink = false;
+        if self.options.embed_hyperlinks == EmbedHyperlinks::On {
+            if let Some(abs_path) = self.file.path.canonicalize().unwrap().as_os_str().to_str() {
+                bits.insert(0, ANSIString::from(format!(
+                    "{}file://{}{}{}",
+                    HYPERLINK_START,
+                    gethostname::gethostname().to_str().unwrap_or(""),
+                    urlencoding::encode(abs_path).replace("%2F", "/"),
+                    HYPERLINK_END,
+                )));
+                display_hyperlink = true;
+            }
+        }
+
+        escape(
+            self.file.name.clone(),
             &mut bits,
             file_style,
             self.colours.control_char(),
         );
 
-        bits
-    }
-
-    // An adapted version of escape::escape.
-    // afaik of all the calls to escape::escape, only for escaped_file_name, the call to escape needs to be checked for hyper links
-    // and if that's the case then I think it's best to not try and generalize escape::escape to this case,
-    // as this adaptation would incur some unneeded operations there
-    pub fn escape_color_and_hyperlinks(&self, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
-        let string = self.file.name.clone();
-
-        if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
-            let painted = good.paint(string);
-
-            let adjusted_filename = if let EmbedHyperlinks::On = self.options.embed_hyperlinks {
-                ANSIString::from(format!("\x1B]8;;{}\x1B\x5C{}\x1B]8;;\x1B\x5C", self.file.path.display(), painted))
-            } else {
-                painted
-            };
-            bits.push(adjusted_filename);
-            return;
+        if display_hyperlink {
+            bits.push(ANSIString::from(format!("{HYPERLINK_START}{HYPERLINK_END}")));
         }
 
-        // again adapted from escape::escape
-        // still a slow route, but slightly improved to at least not reallocate buff + have a predetermined buff size
-        //
-        // also note that buff would never need more than len,
-        // even tho 'in total' it will be lenghier than len (as we expand with escape_default),
-        // because we clear it after an irregularity
-        let mut buff = String::with_capacity(string.len());
-        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 && c != 0x7f as char {
-                buff.push(c);
-            }
-            else {
-                if ! buff.is_empty() {
-                    bits.push(good.paint(std::mem::take(&mut buff)));
-                }
-                // biased towards regular characters, so we still collect on first sight of bad char
-                for e in c.escape_default() {
-                    buff.push(e);
-                }
-                bits.push(bad.paint(std::mem::take(&mut buff)));
-            }
-        }
+        bits
     }
 
     /// Figures out which colour to paint the filename part of the output,