ソースを参照

improved escape function

dankeyy 2 年 前
コミット
874aa6c91f
1 ファイル変更48 行追加33 行削除
  1. 48 33
      src/output/file_name.rs

+ 48 - 33
src/output/file_name.rs

@@ -161,7 +161,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         	// indicate this fact. But when showing targets, we can just
         	// colour the path instead (see below), and leave the broken
         	// link’s filename as the link colour.
-            for bit in self.coloured_file_name() {
+            for bit in self.escaped_file_name() {
                 bits.push(bit);
             }
         }
@@ -192,7 +192,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
                             options: target_options,
                         };
 
-                        for bit in target_name.coloured_file_name() {
+                        for bit in target_name.escaped_file_name() {
                             bits.push(bit);
                         }
 
@@ -287,7 +287,36 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         }
     }
 
-    pub fn escape(&self, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
+    /// Returns at least one ANSI-highlighted string representing this file’s
+    /// name using the given set of colours.
+    ///
+    /// If --hyperlink flag is provided, it will escape the filename accordingly.
+    ///
+    /// 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 escaped_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
+        let file_style = self.style();
+        let mut bits = Vec::new();
+
+        self.escape_color_and_hyperlinks(
+            &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 colored_file_name the call to escape needs to 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.to_owned();
 
         if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
@@ -302,47 +331,33 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
             return;
         }
 
+        // 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 {
-                // TODO: This allocates way too much,
-                // hence the `all` check above.
-                let mut s = String::new();
-                s.push(c);
-                bits.push(good.paint(s));
+                buff.push(c);
             }
             else {
-                let s = c.escape_default().collect::<String>();
-                bits.push(bad.paint(s));
+                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)));
             }
         }
     }
 
-    /// 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();
-
-        self.escape(
-            &mut bits,
-            file_style,
-            self.colours.control_char(),
-        );
-
-        bits
-    }
-
     /// 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. (Or the broken link colour,