mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
feat(Completions): adds completion support for Elvish.
This commit is contained in:
parent
4b5ba7d750
commit
3d3d4b1e9a
4 changed files with 254 additions and 2 deletions
126
src/completions/elvish.rs
Normal file
126
src/completions/elvish.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Std
|
||||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use app::parser::Parser;
|
||||
use INTERNAL_ERROR_MSG;
|
||||
|
||||
pub struct ElvishGen<'a, 'b>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
p: &'b Parser<'a, 'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> ElvishGen<'a, 'b> {
|
||||
pub fn new(p: &'b Parser<'a, 'b>) -> Self { ElvishGen { p: p } }
|
||||
|
||||
pub fn generate_to<W: Write>(&self, buf: &mut W) {
|
||||
let bin_name = self.p.meta.bin_name.as_ref().unwrap();
|
||||
|
||||
let mut names = vec![];
|
||||
let subcommands_cases =
|
||||
generate_inner(self.p, "", &mut names);
|
||||
|
||||
let result = format!(r#"
|
||||
edit:completion:arg-completer[{bin_name}] = [@words]{{
|
||||
fn spaces [n]{{
|
||||
repeat $n ' ' | joins ''
|
||||
}}
|
||||
fn cand [text desc]{{
|
||||
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
|
||||
}}
|
||||
command = '{bin_name}'
|
||||
for word $words[1:-1] {{
|
||||
if (has-prefix $word '-') {{
|
||||
break
|
||||
}}
|
||||
command = $command';'$word
|
||||
}}
|
||||
completions = [{subcommands_cases}
|
||||
]
|
||||
$completions[$command]
|
||||
}}
|
||||
"#,
|
||||
bin_name = bin_name,
|
||||
subcommands_cases = subcommands_cases
|
||||
);
|
||||
|
||||
w!(buf, result.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Escape string inside single quotes
|
||||
fn escape_string(string: &str) -> String { string.replace("'", "''") }
|
||||
|
||||
fn get_tooltip<T : ToString>(help: Option<&str>, data: T) -> String {
|
||||
match help {
|
||||
Some(help) => escape_string(help),
|
||||
_ => data.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_inner<'a, 'b, 'p>(
|
||||
p: &'p Parser<'a, 'b>,
|
||||
previous_command_name: &str,
|
||||
names: &mut Vec<&'p str>,
|
||||
) -> String {
|
||||
debugln!("ElvishGen::generate_inner;");
|
||||
let command_name = if previous_command_name.is_empty() {
|
||||
p.meta.bin_name.as_ref().expect(INTERNAL_ERROR_MSG).clone()
|
||||
} else {
|
||||
format!("{};{}", previous_command_name, &p.meta.name)
|
||||
};
|
||||
|
||||
let mut completions = String::new();
|
||||
let preamble = String::from("\n cand ");
|
||||
|
||||
for option in p.opts() {
|
||||
if let Some(data) = option.s.short {
|
||||
let tooltip = get_tooltip(option.b.help, data);
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("-{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
if let Some(data) = option.s.long {
|
||||
let tooltip = get_tooltip(option.b.help, data);
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("--{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
for flag in p.flags() {
|
||||
if let Some(data) = flag.s.short {
|
||||
let tooltip = get_tooltip(flag.b.help, data);
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("-{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
if let Some(data) = flag.s.long {
|
||||
let tooltip = get_tooltip(flag.b.help, data);
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("--{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let data = &subcommand.p.meta.name;
|
||||
let tooltip = get_tooltip(subcommand.p.meta.about, data);
|
||||
completions.push_str(&preamble);
|
||||
completions.push_str(format!("{} '{}'", data, tooltip).as_str());
|
||||
}
|
||||
|
||||
let mut subcommands_cases = format!(
|
||||
r"
|
||||
&'{}'= {{{}
|
||||
}}",
|
||||
&command_name,
|
||||
completions
|
||||
);
|
||||
|
||||
for subcommand in &p.subcommands {
|
||||
let subcommand_subcommands_cases =
|
||||
generate_inner(&subcommand.p, &command_name, names);
|
||||
subcommands_cases.push_str(&subcommand_subcommands_cases);
|
||||
}
|
||||
|
||||
subcommands_cases
|
||||
}
|
|
@ -4,6 +4,7 @@ mod bash;
|
|||
mod fish;
|
||||
mod zsh;
|
||||
mod powershell;
|
||||
mod elvish;
|
||||
mod shell;
|
||||
|
||||
// Std
|
||||
|
@ -15,6 +16,7 @@ use self::bash::BashGen;
|
|||
use self::fish::FishGen;
|
||||
use self::zsh::ZshGen;
|
||||
use self::powershell::PowerShellGen;
|
||||
use self::elvish::ElvishGen;
|
||||
pub use self::shell::Shell;
|
||||
|
||||
pub struct ComplGen<'a, 'b>(&'b App<'a, 'b>)
|
||||
|
@ -26,6 +28,7 @@ impl<'a, 'b> ComplGen<'a, 'b> {
|
|||
|
||||
pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
|
||||
match for_shell {
|
||||
Shell::Elvish => ElvishGen::new(self.p).generate_to(buf),
|
||||
Shell::Bash => BashGen::new(self.0).generate_to(buf),
|
||||
Shell::Fish => FishGen::new(self.0).generate_to(buf),
|
||||
Shell::Zsh => ZshGen::new(self.0).generate_to(buf),
|
||||
|
|
|
@ -15,11 +15,13 @@ pub enum Shell {
|
|||
Zsh,
|
||||
/// Generates a completion file for PowerShell
|
||||
PowerShell,
|
||||
/// Generates a completion file for Elvish
|
||||
Elvish,
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
/// A list of possible variants in `&'static str` form
|
||||
pub fn variants() -> [&'static str; 4] { ["zsh", "bash", "fish", "powershell"] }
|
||||
pub fn variants() -> [&'static str; 5] { ["zsh", "bash", "fish", "powershell", "elvish"] }
|
||||
}
|
||||
|
||||
impl FromStr for Shell {
|
||||
|
@ -31,7 +33,8 @@ impl FromStr for Shell {
|
|||
"FISH" | _ if s.eq_ignore_ascii_case("fish") => Ok(Shell::Fish),
|
||||
"BASH" | _ if s.eq_ignore_ascii_case("bash") => Ok(Shell::Bash),
|
||||
"POWERSHELL" | _ if s.eq_ignore_ascii_case("powershell") => Ok(Shell::PowerShell),
|
||||
_ => Err(String::from("[valid values: bash, fish, zsh, powershell]")),
|
||||
"ELVISH" | _ if s.eq_ignore_ascii_case("elvish") => Ok(Shell::Elvish),
|
||||
_ => Err(String::from("[valid values: bash, fish, zsh, powershell, elvish]")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +46,7 @@ impl fmt::Display for Shell {
|
|||
Shell::Fish => write!(f, "FISH"),
|
||||
Shell::Zsh => write!(f, "ZSH"),
|
||||
Shell::PowerShell => write!(f, "POWERSHELL"),
|
||||
Shell::Elvish => write!(f, "ELVISH"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,6 +228,105 @@ Register-ArgumentCompleter -Native -CommandName 'my_app' -ScriptBlock {
|
|||
}
|
||||
"#;
|
||||
|
||||
static ELVISH: &'static str = r#"
|
||||
edit:completion:arg-completer[my_app] = [@words]{
|
||||
fn spaces [n]{
|
||||
repeat $n ' ' | joins ''
|
||||
}
|
||||
fn cand [text desc]{
|
||||
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
|
||||
}
|
||||
command = 'my_app'
|
||||
for word $words[1:-1] {
|
||||
if (has-prefix $word '-') {
|
||||
break
|
||||
}
|
||||
command = $command';'$word
|
||||
}
|
||||
completions = [
|
||||
&'my_app'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
cand test 'tests things'
|
||||
cand help 'Prints this message or the help of the given subcommand(s)'
|
||||
}
|
||||
&'my_app;test'= {
|
||||
cand --case 'the case to test'
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;help'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
]
|
||||
$completions[$command]
|
||||
}
|
||||
"#;
|
||||
|
||||
static ELVISH_SPECIAL_CMDS: &'static str = r#"
|
||||
edit:completion:arg-completer[my_app] = [@words]{
|
||||
fn spaces [n]{
|
||||
repeat $n ' ' | joins ''
|
||||
}
|
||||
fn cand [text desc]{
|
||||
edit:complex-candidate $text &display-suffix=' '(spaces (- 14 (wcswidth $text)))$desc
|
||||
}
|
||||
command = 'my_app'
|
||||
for word $words[1:-1] {
|
||||
if (has-prefix $word '-') {
|
||||
break
|
||||
}
|
||||
command = $command';'$word
|
||||
}
|
||||
completions = [
|
||||
&'my_app'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
cand test 'tests things'
|
||||
cand some_cmd 'tests other things'
|
||||
cand some-cmd-with-hypens 'some-cmd-with-hypens'
|
||||
cand help 'Prints this message or the help of the given subcommand(s)'
|
||||
}
|
||||
&'my_app;test'= {
|
||||
cand --case 'the case to test'
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;some_cmd'= {
|
||||
cand --config 'the other case to test'
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;some-cmd-with-hypens'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
&'my_app;help'= {
|
||||
cand -h 'Prints help information'
|
||||
cand --help 'Prints help information'
|
||||
cand -V 'Prints version information'
|
||||
cand --version 'Prints version information'
|
||||
}
|
||||
]
|
||||
$completions[$command]
|
||||
}
|
||||
"#;
|
||||
|
||||
static POWERSHELL_SPECIAL_CMDS: &'static str = r#"
|
||||
using namespace System.Management.Automation
|
||||
using namespace System.Management.Automation.Language
|
||||
|
@ -703,6 +802,26 @@ fn powershell() {
|
|||
assert!(compare(&*string, POWERSHELL));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elvish() {
|
||||
let mut app = build_app();
|
||||
let mut buf = vec![];
|
||||
app.gen_completions_to("my_app", Shell::Elvish, &mut buf);
|
||||
let string = String::from_utf8(buf).unwrap();
|
||||
|
||||
assert!(compare(&*string, ELVISH));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elvish_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
let mut buf = vec![];
|
||||
app.gen_completions_to("my_app", Shell::Elvish, &mut buf);
|
||||
let string = String::from_utf8(buf).unwrap();
|
||||
|
||||
assert!(compare(&*string, ELVISH_SPECIAL_CMDS));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn powershell_with_special_commands() {
|
||||
let mut app = build_app_special_commands();
|
||||
|
|
Loading…
Reference in a new issue