Преглед изворни кода

fix(hyperlink): escape spaces in file path to make them work correctly

Some terminal emulator do it themselves, but we want to be correct everywhere
ariasuni пре 9 месеци
родитељ
комит
dc6075474f
2 измењених фајлова са 34 додато и 17 уклоњено
  1. 32 0
      src/output/escape.rs
  2. 2 17
      src/output/file_name.rs

+ 32 - 0
src/output/escape.rs

@@ -6,6 +6,7 @@
 // SPDX-License-Identifier: MIT
 // SPDX-License-Identifier: MIT
 use super::file_name::QuoteStyle;
 use super::file_name::QuoteStyle;
 use nu_ansi_term::{AnsiString as ANSIString, Style};
 use nu_ansi_term::{AnsiString as ANSIString, Style};
+use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
 
 
 pub fn escape(
 pub fn escape(
     string: String,
     string: String,
@@ -43,3 +44,34 @@ pub fn escape(
         bits.push(quote_bit);
         bits.push(quote_bit);
     }
     }
 }
 }
+
+const HYPERLINK_ESCAPE_CHARS: &AsciiSet = &CONTROLS.add(b' ');
+const HYPERLINK_OPENING_START: &str = "\x1B]8;;";
+const HYPERLINK_OPENING_END: &str = "\x1B\x5C";
+// Combination of both above tags
+pub const HYPERLINK_CLOSING: &str = "\x1B]8;;\x1B\x5C";
+
+pub fn get_hyperlink_start_tag(abs_path: &str) -> String {
+    let abs_path = utf8_percent_encode(abs_path, HYPERLINK_ESCAPE_CHARS).to_string();
+
+    // On Windows, `std::fs::canonicalize` adds the Win32 File prefix, which we need to remove
+    #[cfg(target_os = "windows")]
+    let abs_path = abs_path.strip_prefix("\\\\?\\").unwrap_or(&abs_path);
+
+    format!("{HYPERLINK_OPENING_START}file://{abs_path}{HYPERLINK_OPENING_END}")
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn hyperlink_start_tag_escape_spaces() {
+        assert_eq!(
+            get_hyperlink_start_tag("/folder name/file name").to_string(),
+            format!(
+                "{HYPERLINK_OPENING_START}file:///folder%20name/file%20name{HYPERLINK_OPENING_END}"
+            ),
+        );
+    }
+}

+ 2 - 17
src/output/file_name.rs

@@ -408,11 +408,6 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         &self,
         &self,
         style_override: Option<Style>,
         style_override: Option<Style>,
     ) -> Vec<ANSIString<'unused>> {
     ) -> Vec<ANSIString<'unused>> {
-        use percent_encoding::{utf8_percent_encode, CONTROLS};
-
-        const HYPERLINK_START: &str = "\x1B]8;;";
-        const HYPERLINK_END: &str = "\x1B\x5C";
-
         let file_style = style_override.unwrap_or(self.style());
         let file_style = style_override.unwrap_or(self.style());
         let mut bits = Vec::new();
         let mut bits = Vec::new();
 
 
@@ -423,15 +418,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
                 .absolute_path()
                 .absolute_path()
                 .and_then(|p| p.as_os_str().to_str())
                 .and_then(|p| p.as_os_str().to_str())
             {
             {
-                let abs_path = utf8_percent_encode(abs_path, CONTROLS).to_string();
-
-                // On Windows, `std::fs::canonicalize` adds the Win32 File prefix, which we need to remove
-                #[cfg(target_os = "windows")]
-                let abs_path = abs_path.strip_prefix("\\\\?\\").unwrap_or(&abs_path);
-
-                bits.push(ANSIString::from(format!(
-                    "{HYPERLINK_START}file://{abs_path}{HYPERLINK_END}"
-                )));
+                bits.push(ANSIString::from(escape::get_hyperlink_start_tag(abs_path)));
 
 
                 display_hyperlink = true;
                 display_hyperlink = true;
             }
             }
@@ -446,9 +433,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         );
         );
 
 
         if display_hyperlink {
         if display_hyperlink {
-            bits.push(ANSIString::from(format!(
-                "{HYPERLINK_START}{HYPERLINK_END}"
-            )));
+            bits.push(ANSIString::from(escape::HYPERLINK_CLOSING));
         }
         }
 
 
         bits
         bits