Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Feb 10, 2024
1 parent 3934813 commit 0088a4d
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 15 deletions.
66 changes: 51 additions & 15 deletions cli/src/cli/complete_word.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,46 @@ pub struct CompleteWord {
impl CompleteWord {
pub fn run(&self) -> miette::Result<()> {
let spec = generate::file_or_spec(&self.file, &self.spec)?;
let choices = self.complete_word(&spec)?;
for c in &choices {
println!("{}", c);
}

Ok(())
}

fn complete_word(&self, spec: &Spec) -> miette::Result<Vec<String>> {
let cword = self.cword.unwrap_or(self.words.len().max(1) - 1);
let ctoken = self
.ctoken
.as_ref()
.or(self.words.get(cword))
.cloned()
.unwrap_or_default();

let words: VecDeque<_> = self.words.iter().take(cword).cloned().collect();

trace!(
"cword: {cword} ctoken: {ctoken} words: {}",
words.iter().join(" ")
);

let parsed = parse(&spec, words)?;

let mut choices = vec![];
choices.extend(complete_subcommands(parsed.cmd, &ctoken));
if let Some(arg) = parsed.cmd.args.get(parsed.args.len()) {
choices.extend(complete_arg(&spec, arg, &ctoken)?);
}

for c in &choices {
println!("{}", c);
}

Ok(())
let parsed = parse(spec, words)?;
let choices = if !parsed.cmd.subcommands.is_empty() {
complete_subcommands(parsed.cmd, &ctoken)
} else if ctoken == "-" {
let shorts = complete_short_flag_names(parsed.cmd, "");
let longs = complete_long_flag_names(parsed.cmd, "");
shorts.into_iter().chain(longs).collect()
} else if ctoken.starts_with("--") {
complete_long_flag_names(parsed.cmd, &ctoken)
} else if ctoken.starts_with('-') {
complete_short_flag_names(parsed.cmd, &ctoken)
} else if let Some(arg) = parsed.cmd.args.get(parsed.args.len()) {
complete_arg(spec, arg, &ctoken)?
} else {
vec![]
};
Ok(choices)
}
}

Expand Down Expand Up @@ -141,7 +153,6 @@ fn parse(spec: &Spec, mut words: VecDeque<String>) -> miette::Result<ParseOutput
}
panic!("unexpected word: {:?}", words[0]);
}
dbg!(&flags);

Ok(ParseOutput {
cmd,
Expand All @@ -152,6 +163,7 @@ fn parse(spec: &Spec, mut words: VecDeque<String>) -> miette::Result<ParseOutput
}

fn complete_subcommands(cmd: &SpecCommand, ctoken: &str) -> Vec<String> {
trace!("complete_subcommands: {ctoken}");
let mut choices = vec![];
for subcommand in cmd.subcommands.values() {
if subcommand.hide {
Expand All @@ -169,7 +181,30 @@ fn complete_subcommands(cmd: &SpecCommand, ctoken: &str) -> Vec<String> {
.collect()
}

fn complete_long_flag_names(cmd: &SpecCommand, ctoken: &str) -> Vec<String> {
trace!("complete_long_flag_names: {ctoken}");
let ctoken = ctoken.strip_prefix("--").unwrap_or(ctoken);
cmd.list_visible_long_flags()
.into_iter()
.filter(|c| c.starts_with(ctoken))
.map(|c| format!("--{}", c))
.sorted()
.collect()
}

fn complete_short_flag_names(cmd: &SpecCommand, ctoken: &str) -> Vec<String> {
trace!("complete_short_flag_names: {ctoken}");
let cur = ctoken.chars().nth(1);
cmd.list_visible_short_flags()
.into_iter()
.filter(|c| cur.is_none() || cur == Some(**c))
.map(|c| format!("-{}", c))
.sorted()
.collect()
}

fn complete_arg(spec: &Spec, arg: &SpecArg, ctoken: &str) -> miette::Result<Vec<String>> {
trace!("complete_arg: {} {ctoken}", &arg.name);
let name = arg.name.to_lowercase();

if let Ok(cwd) = env::current_dir() {
Expand Down Expand Up @@ -198,6 +233,7 @@ fn complete_path(
ctoken: &str,
filter: impl Fn(&Path) -> bool,
) -> miette::Result<Vec<String>> {
trace!("complete_path: {ctoken}");
let path = PathBuf::from(ctoken);
let mut dir = path.parent().unwrap_or(&path).to_path_buf();
if dir.is_relative() {
Expand Down
10 changes: 10 additions & 0 deletions cli/tests/complete_word.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,22 @@ fn complete_word_cword() {

#[test]
fn complete_word_long_flag() {
assert_cmd(&["--", "plugins", "install", "--"]).stdout("--dir\n--global\n");
assert_cmd(&["--", "plugins", "install", "--g"]).stdout("--global\n");
assert_cmd(&["--", "plugins", "install", "--global", "pl"])
.stdout(predicate::str::contains("plugin-2"));
}

// #[test]
// fn complete_word_long_flag_val() {
// assert_cmd(&["--", "plugins", "install", "--dir", "mydir"])
// .stdout(predicate::str::contains("plugin-2"));
// }

#[test]
fn complete_word_short_flag() {
assert_cmd(&["--", "plugins", "install", "-"]).stdout("-d\n-g\n--dir\n--global\n");
assert_cmd(&["--", "plugins", "install", "-g"]).stdout("-g\n");
assert_cmd(&["--", "plugins", "install", "-g", "pl"])
.stdout(predicate::str::contains("plugin-2"));
}
Expand Down
1 change: 1 addition & 0 deletions examples/basic.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ cmd "plugins" {
arg "plugin"
arg "version"
flag "-g --global"
flag "-d --dir <dir>"
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/parse/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,23 @@ impl SpecCommand {
self.subcommands.insert(name, cmd);
}
}

pub fn list_visible_long_flags(&self) -> Vec<String> {
self.flags
.iter()
.filter(|f| !f.hide)
.flat_map(|f| f.long.iter())
.map(|s| s.to_string())
.collect()
}

pub fn list_visible_short_flags(&self) -> Vec<&char> {
self.flags
.iter()
.filter(|f| !f.hide)
.flat_map(|f| f.short.iter())
.collect()
}
}

impl From<&SpecCommand> for KdlNode {
Expand Down

0 comments on commit 0088a4d

Please sign in to comment.