nvim_gtk/
misc.rs

1use std::borrow::Cow;
2use std::mem;
3
4use lazy_static::lazy_static;
5
6use percent_encoding::percent_decode;
7use regex::Regex;
8
9use crate::shell;
10
11/// Split comma separated parameters with ',' except escaped '\\,'
12pub fn split_at_comma(source: &str) -> Vec<String> {
13    let mut items = Vec::new();
14
15    let mut escaped = false;
16    let mut item = String::new();
17
18    for ch in source.chars() {
19        if ch == ',' && !escaped {
20            item = item.replace("\\,", ",");
21
22            let mut new_item = String::new();
23            mem::swap(&mut item, &mut new_item);
24
25            items.push(new_item);
26        } else {
27            item.push(ch);
28        }
29        escaped = ch == '\\';
30    }
31
32    if !item.is_empty() {
33        items.push(item.replace("\\,", ","));
34    }
35
36    items
37}
38
39/// Escape special ASCII characters with a backslash.
40pub fn escape_filename<'t>(filename: &'t str) -> Cow<'t, str> {
41    lazy_static! {
42        static ref SPECIAL_CHARS: Regex = if cfg!(target_os = "windows") {
43            // On Windows, don't escape `:` and `\`, as these are valid components of the path.
44            Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._:\\-]]").unwrap()
45        } else {
46            // Similarly, don't escape `/` on other platforms.
47            Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._/-]]").unwrap()
48        };
49    }
50    SPECIAL_CHARS.replace_all(&*filename, r"\$0")
51}
52
53/// Decode a file URI.
54///
55///   - On UNIX: `file:///path/to/a%20file.ext` -> `/path/to/a file.ext`
56///   - On Windows: `file:///C:/path/to/a%20file.ext` -> `C:\path\to\a file.ext`
57pub fn decode_uri(uri: &str) -> Option<String> {
58    let path = match uri.split_at(8) {
59        ("file:///", path) => path,
60        _ => return None,
61    };
62    let path = percent_decode(path.as_bytes()).decode_utf8().ok()?;
63    if cfg!(target_os = "windows") {
64        lazy_static! {
65            static ref SLASH: Regex = Regex::new(r"/").unwrap();
66        }
67        Some(String::from(SLASH.replace_all(&*path, r"\")))
68    } else {
69        Some("/".to_owned() + &path)
70    }
71}
72
73/// info text
74pub fn about_comments() -> String {
75    format!(
76        "Build on top of neovim\n\
77         Minimum supported neovim version: {}",
78        shell::MINIMUM_SUPPORTED_NVIM_VERSION
79    )
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_comma_split() {
88        let res = split_at_comma("a,b");
89        assert_eq!(2, res.len());
90        assert_eq!("a", res[0]);
91        assert_eq!("b", res[1]);
92
93        let res = split_at_comma("a,b\\,c");
94        assert_eq!(2, res.len());
95        assert_eq!("a", res[0]);
96        assert_eq!("b,c", res[1]);
97    }
98}