on master help sign in
Implement change_current_directory command
commitb56174d7380fd299c4bcb8ded2f0127821dbe588 authorLionel Flandrin <lfg@svkt.eu> dateMon 21 Jun 2021, 16:40 (5 years ago) parent866b32b
2 files changed+82−4
helix-term/src/commands.rs+33 helix-term/src/ui/mod.rs+49−4
helix-term/src/commands.rs+33
@@ -1360,6 +1360,32 @@ mod cmd {
13601360 editor.set_status(editor.clipboard_provider.name().into());
13611361 }
13621362
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+
13631389 pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
13641390 TypableCommand {
13651391 name: "quit",
@@ -1529,6 +1555,13 @@ mod cmd {
15291555 fun: show_clipboard_provider,
15301556 completer: None,
15311557 },
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+ },
15321565 ];
15331566
15341567 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 {
152152 names
153153 }
154154
155− // TODO: we could return an iter/lazy thing so it can fetch as many as it needs.
156155 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+ {
157195 // Rust's filename handling is really annoying.
158196
159197 use ignore::WalkBuilder;
@@ -184,7 +222,13 @@ pub mod completers {
184222 .max_depth(Some(1))
185223 .build()
186224 .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+
188232 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
189233
190234 let path = entry.path();
@@ -199,11 +243,12 @@ pub mod completers {
199243 path.strip_prefix(&dir).unwrap_or(path).to_path_buf()
200244 };
201245
202− if is_dir {
246+ if fmatch == FileMatch::AcceptIncomplete {
203247 path.push("");
204248 }
249+
205250 let path = path.to_str().unwrap().to_owned();
206− (end.clone(), Cow::from(path))
251+ Some((end.clone(), Cow::from(path)))
207252 })
208253 }) // TODO: unwrap or skip
209254 .filter(|(_, path)| !path.is_empty()) // TODO