Merge pull request #65 from tertsdiepraam/nu-completions

Basic nushell completions
This commit is contained in:
Terts Diepraam
2023-12-19 12:08:40 +01:00
committed by GitHub
3 changed files with 129 additions and 0 deletions
+2
View File
@@ -16,6 +16,7 @@
mod fish;
mod man;
mod md;
mod nu;
mod zsh;
/// A description of a CLI command
@@ -72,6 +73,7 @@ pub fn render(c: &Command, shell: &str) -> String {
"md" => md::render(c),
"fish" => fish::render(c),
"zsh" => zsh::render(c),
"nu" | "nushell" => nu::render(c),
"man" => man::render(c),
"sh" | "bash" | "csh" | "elvish" | "powershell" => panic!("shell '{shell}' completion is not implemented yet!"),
_ => panic!("unknown option '{shell}'! Expected one of: \"md\", \"fish\", \"zsh\", \"man\", \"sh\", \"bash\", \"csh\", \"elvish\", \"powershell\""),
+85
View File
@@ -0,0 +1,85 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use crate::{Arg, Command, Flag, Value, ValueHint};
use std::fmt::Write;
/// Create completion script for `nushell`
pub fn render(c: &Command) -> String {
let mut args = Vec::new();
let command_name = c.name;
let mut complete_commands = Vec::new();
let indent = " ".repeat(4);
for arg in &c.args {
let hint = if let Some((cmd, hint_name)) = render_completion_command(command_name, arg) {
complete_commands.push(cmd);
hint_name
} else {
"".into()
};
for Flag { flag, value } in &arg.short {
let value = if let Value::Required(_) | Value::Optional(_) = value {
format!(": string{hint}")
} else {
"".into()
};
args.push((format!("-{flag}{value}"), arg.help));
}
for Flag { flag, value } in &arg.long {
let value = if let Value::Required(_) | Value::Optional(_) = value {
format!(": string{hint}")
} else {
"".into()
};
args.push((format!("--{flag}{value}"), arg.help));
}
}
let longest_arg = args.iter().map(|a| a.0.len()).max().unwrap_or_default();
let mut arg_str = String::new();
for (a, h) in args {
writeln!(arg_str, "{indent}{a:<longest_arg$} # {h}").unwrap();
}
template(c.name, &complete_commands.join("\n"), &arg_str)
}
fn render_completion_command(command_name: &str, arg: &Arg) -> Option<(String, String)> {
let val = arg.value.as_ref()?;
// It could be that there is only a `dd` style argument. In that case, nu won't support it;
let arg_name = arg.long.first().or(arg.short.first())?.flag;
render_value_hint(val).map(|hint| {
let name = format!("nu-complete {command_name} {arg_name}");
let cmd = format!("def \"{name}\" [] {{\n {hint}\n}}");
let hint_str = format!("@\"{name}\"");
(cmd, hint_str)
})
}
fn render_value_hint(value: &ValueHint) -> Option<String> {
match value {
ValueHint::Strings(s) => {
let vals = s
.iter()
.map(|s| format!("\"{s}\""))
.collect::<Vec<_>>()
.join(", ");
Some(format!("[{vals}]"))
}
// The path arguments could be improved, but nu currently does not give
// us enough context to improve the default completions.
ValueHint::Unknown
| ValueHint::AnyPath
| ValueHint::FilePath
| ValueHint::ExecutablePath
| ValueHint::DirPath
| ValueHint::Username
| ValueHint::Hostname => None,
}
}
fn template(name: &str, complete_commands: &str, args: &str) -> String {
format!("{complete_commands}\n\nexport extern \"{name}\" [\n{args}]\n")
}
+42
View File
@@ -0,0 +1,42 @@
use std::path::PathBuf;
use uutils_args::{Arguments, Options, Value};
#[derive(Value)]
enum Number {
#[value]
One,
#[value]
Two,
#[value]
Three,
}
#[derive(Arguments)]
enum Arg {
/// Give it nothing!
#[arg("-f", "--flag")]
Flag,
// Completion is derived from the `Number` type, through the `Value` trait
/// Give it a number!
#[arg("-n N", "--number=N")]
Number(Number),
// Completion is derived from the `PathBuf` type
/// Give it a path!
#[arg("-p P", "--path=P")]
Path(PathBuf),
}
struct Settings;
impl Options<Arg> for Settings {
fn apply(&mut self, _arg: Arg) {
panic!("Compile with the 'parse-is-complete' feature!")
}
}
fn main() {
Settings.parse(std::env::args_os());
}