Skip to content

Commit

Permalink
improve completion
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfv committed Feb 3, 2025
1 parent 89af34b commit 74e4da7
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 26 deletions.
1 change: 1 addition & 0 deletions crates/deno_task_shell/src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub use types::ShellPipeReader;
pub use types::ShellPipeWriter;
pub use types::ShellState;

pub use commands::builtin_commands;
pub use commands::parse_arg_kinds;
pub use commands::ArgKind;

Expand Down
49 changes: 28 additions & 21 deletions crates/shell/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ use rustyline::hint::Hinter;
use rustyline::validate::Validator;
use rustyline::{Context, Helper};
use std::borrow::Cow::{self, Owned};
use std::collections::HashSet;
use std::env;
use std::fs;
use std::fs::Metadata;
use std::path::{Path, PathBuf};

pub struct ShellCompleter;
pub struct ShellCompleter {
builtins: HashSet<String>,
}

impl Default for ShellCompleter {
fn default() -> Self {
ShellCompleter
impl ShellCompleter {
pub fn new(builtins: HashSet<String>) -> Self {
Self { builtins }
}
}

Expand All @@ -35,11 +38,14 @@ impl Completer for ShellCompleter {
complete_filenames(is_start, word, &mut matches);

// Complete shell commands
complete_shell_commands(is_start, word, &mut matches);
complete_shell_commands(is_start, &self.builtins, word, &mut matches);

// Complete executables in PATH
complete_executables_in_path(is_start, word, &mut matches);

matches.sort_by(|a, b| a.display.cmp(&b.display));
matches.dedup_by(|a, b| a.display == b.display);

Ok((start, matches))
}
}
Expand Down Expand Up @@ -157,9 +163,9 @@ fn complete_filenames(is_start: bool, word: &str, matches: &mut Vec<Pair>) {
};

let search_dir = resolve_dir_path(dir_path);
let only_executable = word.starts_with("./") && is_start;
let only_executable = (word.starts_with("./") || word.starts_with("/")) && is_start;

let mut files: Vec<FileMatch> = fs::read_dir(&search_dir)
let files: Vec<FileMatch> = fs::read_dir(&search_dir)
.into_iter()
.flatten()
.flatten()
Expand All @@ -168,25 +174,23 @@ fn complete_filenames(is_start: bool, word: &str, matches: &mut Vec<Pair>) {
.filter(|f| !only_executable || f.is_executable || f.is_dir)
.collect();

// Sort directories first, then by name
files.sort_by(|a, b| match (a.is_dir, b.is_dir) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => a.name.cmp(&b.name),
});

matches.extend(files.into_iter().map(|f| Pair {
display: f.display_name(),
replacement: f.replacement(&dir_path),
}));
}

fn complete_shell_commands(is_start: bool, word: &str, matches: &mut Vec<Pair>) {
fn complete_shell_commands(
is_start: bool,
builtin_commands: &HashSet<String>,
word: &str,
matches: &mut Vec<Pair>,
) {
if !is_start {
return;
}
let shell_commands = ["ls", "cat", "cd", "pwd", "echo", "grep"];
for &cmd in &shell_commands {

for cmd in builtin_commands {
if cmd.starts_with(word) {
matches.push(Pair {
display: cmd.to_string(),
Expand All @@ -200,16 +204,19 @@ fn complete_executables_in_path(is_start: bool, word: &str, matches: &mut Vec<Pa
if !is_start {
return;
}
let mut found = HashSet::new();
if let Ok(paths) = env::var("PATH") {
for path in env::split_paths(&paths) {
if let Ok(entries) = fs::read_dir(path) {
for entry in entries.flatten() {
if let Ok(name) = entry.file_name().into_string() {
if name.starts_with(word) && entry.path().is_file() {
matches.push(Pair {
display: name.clone(),
replacement: name,
});
if found.insert(name.clone()) {
matches.push(Pair {
display: name.clone(),
replacement: name,
});
}
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions crates/shell/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustyline::{

use crate::completion;

use std::borrow::Cow::Borrowed;
use std::{borrow::Cow::Borrowed, collections::HashSet};

#[derive(Helper, Completer, Hinter, Validator)]
pub(crate) struct ShellPromptHelper {
Expand All @@ -18,10 +18,10 @@ pub(crate) struct ShellPromptHelper {
pub colored_prompt: String,
}

impl Default for ShellPromptHelper {
fn default() -> Self {
impl ShellPromptHelper {
pub fn new(builtin_commands: HashSet<String>) -> Self {
Self {
completer: completion::ShellCompleter,
completer: completion::ShellCompleter::new(builtin_commands),
validator: MatchingBracketValidator::new(),
colored_prompt: String::new(),
}
Expand Down
7 changes: 6 additions & 1 deletion crates/shell/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ async fn interactive(state: Option<ShellState>, norc: bool) -> miette::Result<()
.expect("Error setting Ctrl-C handler");

let mut rl = Editor::with_config(config).into_diagnostic()?;
let builtins = deno_task_shell::builtin_commands()
.keys()
.chain(commands::get_commands().keys())
.map(|s| s.to_string())
.collect();

let helper = helper::ShellPromptHelper::default();
let helper = helper::ShellPromptHelper::new(builtins);
rl.set_helper(Some(helper));

let mut state = state.unwrap_or_else(init_state);
Expand Down

0 comments on commit 74e4da7

Please sign in to comment.