complete.rs 3.42 KB
Newer Older
MovingtoMars's avatar
MovingtoMars committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use std::path::PathBuf;

pub trait Completer {
    fn completions(&self, start: &str) -> Vec<String>;
}

pub struct BasicCompleter {
    prefixes: Vec<String>,
}

impl BasicCompleter {
    pub fn new<T: Into<String>>(prefixes: Vec<T>) -> BasicCompleter {
        BasicCompleter { prefixes: prefixes.into_iter().map(|s| s.into()).collect() }
    }
}

impl Completer for BasicCompleter {
    fn completions(&self, start: &str) -> Vec<String> {
        self.prefixes.iter().filter(|s| s.starts_with(start)).cloned().collect()
    }
}

pub struct FilenameCompleter {
    working_dir: Option<PathBuf>,
}

impl FilenameCompleter {
    pub fn new<T: Into<PathBuf>>(working_dir: Option<T>) -> Self {
        FilenameCompleter { working_dir: working_dir.map(|p| p.into()) }
    }
}

impl Completer for FilenameCompleter {
    fn completions(&self, mut start: &str) -> Vec<String> {
        // XXX: this function is really bad, TODO rewrite

        let start_owned;
        if start.starts_with("\"") || start.starts_with("'") {
            start = &start[1..];
            if start.len() >= 1 {
                start = &start[..start.len() - 1];
            }
            start_owned = start.into();
        } else {
            start_owned = start.replace("\\ ", " ");
        }

        let full_path;
        let start_path = PathBuf::from(&start_owned[..]);

        if let Some(ref wd) = self.working_dir {
            let mut fp = PathBuf::from(wd);
            fp.push(start_owned.clone());
            full_path = fp;
        } else {
            full_path = PathBuf::from(start_owned.clone());
        }

        let p;
        let start_name;
        let completing_dir;
        match full_path.parent() {
            // XXX non-unix separaor
64
65
            Some(parent) if start != "" && !start_owned.ends_with("/") &&
                            !full_path.ends_with("..") => {
MovingtoMars's avatar
MovingtoMars committed
66
67
68
69
70
71
72
                p = PathBuf::from(parent);
                start_name = full_path.file_name().unwrap().to_string_lossy().into_owned();
                completing_dir = false;
            }
            _ => {
                p = full_path.clone();
                start_name = "".into();
73
                completing_dir = start == "" || start.ends_with("/") || full_path.ends_with("..");
MovingtoMars's avatar
MovingtoMars committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
            }
        }


        let read_dir = match p.read_dir() {
            Ok(x) => x,
            Err(_) => return vec![],
        };

        let mut matches = vec![];
        for dir in read_dir {
            let dir = match dir {
                Ok(x) => x,
                Err(_) => continue,
            };
            let file_name = dir.file_name();
            let file_name = file_name.to_string_lossy();

            if start_name == "" || file_name.starts_with(&*start_name) {
                let mut a = start_path.clone();
                if !a.is_absolute() {
                    a = PathBuf::new();
                } else if !completing_dir && !a.pop() {
                    return vec![];
                }
                a.push(dir.file_name());
                let mut s = a.to_string_lossy().into_owned();
                if dir.path().is_dir() {
                    s = s + "/";
                }

                let mut b = PathBuf::from(start_owned.clone());
                if !completing_dir {
                    b.pop();
                }
                b.push(s);

                matches.push(b.to_string_lossy().to_owned().replace(" ", "\\ "));
            }
        }

        matches
    }
}