on master help sign in
Implement change_current_directory command
b56174dLionel Flandrin
helix-term/src/commands.rs+33
@@ -1360,6 +1360,32 @@ mod cmd {
1360 editor.set_status(editor.clipboard_provider.name().into());
1360 editor.set_status(editor.clipboard_provider.name().into());
1361 }
1361 }
1362
1362
1363+ fn change_current_directory(editor: &mut Editor, args: &[&str], _: PromptEvent) {
1364+ let dir = match args.first() {
1365+ Some(dir) => dir,
1366+ None => {
1367+ editor.set_error("target directory not provided".into());
1368+ return;
1369+ }
1370+ };
1371+
1372+ if let Err(e) = std::env::set_current_dir(dir) {
1373+ editor.set_error(format!(
1374+ "Couldn't change the current working directory: {:?}",
1375+ e
1376+ ));
1377+ return;
1378+ }
1379+
1380+ match std::env::current_dir() {
1381+ Ok(cwd) => editor.set_status(format!(
1382+ "Current working directory is now {}",
1383+ cwd.display()
1384+ )),
1385+ Err(e) => editor.set_error(format!("Couldn't get the new working directory: {}", e)),
1386+ }
1387+ }
1388+
1363 pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1389 pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
1364 TypableCommand {
1390 TypableCommand {
1365 name: "quit",
1391 name: "quit",
@@ -1529,6 +1555,13 @@ mod cmd {
1529 fun: show_clipboard_provider,
1555 fun: show_clipboard_provider,
1530 completer: None,
1556 completer: None,
1531 },
1557 },
1558+ TypableCommand {
1559+ name: "change-current-directory",
1560+ alias: Some("cd"),
1561+ doc: "Change the current working directory (:cd <dir>).",
1562+ fun: change_current_directory,
1563+ completer: Some(completers::directory),
1564+ },
1532 ];
1565 ];
1533
1566
1534 pub static COMMANDS: Lazy<HashMap<&'static str, &'static TypableCommand>> = Lazy::new(|| {
1567 pub static COMMANDS: Lazy<HashMap<&'static str, &'static TypableCommand>> = Lazy::new(|| {
helix-term/src/ui/mod.rs+49−4
@@ -152,8 +152,46 @@ pub mod completers {
152 names
152 names
153 }
153 }
154
154
155− // TODO: we could return an iter/lazy thing so it can fetch as many as it needs.
156 pub fn filename(input: &str) -> Vec<Completion> {
155 pub fn filename(input: &str) -> Vec<Completion> {
156+ filename_impl(input, |entry| {
157+ let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
158+
159+ if is_dir {
160+ FileMatch::AcceptIncomplete
161+ } else {
162+ FileMatch::Accept
163+ }
164+ })
165+ }
166+
167+ pub fn directory(input: &str) -> Vec<Completion> {
168+ filename_impl(input, |entry| {
169+ let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
170+
171+ if is_dir {
172+ FileMatch::Accept
173+ } else {
174+ FileMatch::Reject
175+ }
176+ })
177+ }
178+
179+ #[derive(Copy, Clone, PartialEq, Eq)]
180+ enum FileMatch {
181+ /// Entry should be ignored
182+ Reject,
183+ /// Entry is usable but can't be the end (for instance if the entry is a directory and we
184+ /// try to match a file)
185+ AcceptIncomplete,
186+ /// Entry is usable and can be the end of the match
187+ Accept,
188+ }
189+
190+ // TODO: we could return an iter/lazy thing so it can fetch as many as it needs.
191+ fn filename_impl<F>(input: &str, filter_fn: F) -> Vec<Completion>
192+ where
193+ F: Fn(&ignore::DirEntry) -> FileMatch,
194+ {
157 // Rust's filename handling is really annoying.
195 // Rust's filename handling is really annoying.
158
196
159 use ignore::WalkBuilder;
197 use ignore::WalkBuilder;
@@ -184,7 +222,13 @@ pub mod completers {
184 .max_depth(Some(1))
222 .max_depth(Some(1))
185 .build()
223 .build()
186 .filter_map(|file| {
224 .filter_map(|file| {
187− file.ok().map(|entry| {
225+ file.ok().and_then(|entry| {
226+ let fmatch = filter_fn(&entry);
227+
228+ if fmatch == FileMatch::Reject {
229+ return None;
230+ }
231+
188 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
232 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
189
233
190 let path = entry.path();
234 let path = entry.path();
@@ -199,11 +243,12 @@ pub mod completers {
199 path.strip_prefix(&dir).unwrap_or(path).to_path_buf()
243 path.strip_prefix(&dir).unwrap_or(path).to_path_buf()
200 };
244 };
201
245
202− if is_dir {
246+ if fmatch == FileMatch::AcceptIncomplete {
203 path.push("");
247 path.push("");
204 }
248 }
249+
205 let path = path.to_str().unwrap().to_owned();
250 let path = path.to_str().unwrap().to_owned();
206− (end.clone(), Cow::from(path))
251+ Some((end.clone(), Cow::from(path)))
207 })
252 })
208 }) // TODO: unwrap or skip
253 }) // TODO: unwrap or skip
209 .filter(|(_, path)| !path.is_empty()) // TODO
254 .filter(|(_, path)| !path.is_empty()) // TODO