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
64
65
66
67
68
69
70
71
72
73
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
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> {
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() {
Some(parent) if start != "" && !start_owned.ends_with("/") &&
!full_path.ends_with("..") => {
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();
completing_dir = start == "" || start.ends_with("/") || full_path.ends_with("..");
}
}
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
}
}