mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 06:42:33 +00:00
feat(Completions): adds the ability to generate completions to io::Write object
This commit is contained in:
parent
691ef58dfb
commit
9f62cf7378
4 changed files with 90 additions and 23 deletions
|
@ -978,7 +978,7 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
/// `get_matches()`, or any of the other normal methods directly after. For example:
|
/// `get_matches()`, or any of the other normal methods directly after. For example:
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// src/main.rs
|
/// // src/main.rs
|
||||||
///
|
///
|
||||||
/// mod cli;
|
/// mod cli;
|
||||||
///
|
///
|
||||||
|
@ -1023,6 +1023,44 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
self.p.gen_completions(for_shell, out_dir.into());
|
self.p.gen_completions(for_shell, out_dir.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Generate a completions file for a specified shell at runtime. Until `cargo install` can
|
||||||
|
/// install extra files like a completion script, this may be used e.g. in a command that
|
||||||
|
/// outputs the contents of the completion script, to be redirected into a file by the user.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Assuming a separate `cli.rs` like the [example above](./struct.App.html#method.gen_completions),
|
||||||
|
/// we can let users generate a completion script using a command:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// // src/main.rs
|
||||||
|
///
|
||||||
|
/// mod cli;
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let matches = cli::build_cli().get_matches();
|
||||||
|
///
|
||||||
|
/// if matches.is_present("generate-bash-completions") {
|
||||||
|
/// cli::build_cli().gen_completions_to("myapp", Shell::Bash, &mut io::stdout());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // normal logic continues...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
///
|
||||||
|
/// ```shell
|
||||||
|
/// $ myapp generate-bash-completions > /etc/bash_completion.d/myapp
|
||||||
|
/// ```
|
||||||
|
pub fn gen_completions_to<W: Write, S: Into<String>>(&mut self, bin_name: S, for_shell: Shell, buf: &mut W) {
|
||||||
|
self.p.meta.bin_name = Some(bin_name.into());
|
||||||
|
self.p.gen_completions_to(for_shell, buf);
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
|
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
|
||||||
/// the process will exit with the appropriate error code. By default this method gets all user
|
/// the process will exit with the appropriate error code. By default this method gets all user
|
||||||
/// provided arguments from [`env::args_os`] in order to allow for invalid UTF-8 code points,
|
/// provided arguments from [`env::args_os`] in order to allow for invalid UTF-8 code points,
|
||||||
|
|
|
@ -26,6 +26,8 @@ use app::meta::AppMeta;
|
||||||
use args::MatchedArg;
|
use args::MatchedArg;
|
||||||
use shell::Shell;
|
use shell::Shell;
|
||||||
use completions::ComplGen;
|
use completions::ComplGen;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -99,10 +101,25 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
.nth(0);
|
.nth(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) {
|
pub fn gen_completions_to<W: Write>(&mut self, for_shell: Shell, buf: &mut W) {
|
||||||
|
|
||||||
self.propogate_help_version();
|
self.propogate_help_version();
|
||||||
self.build_bin_names();
|
self.build_bin_names();
|
||||||
ComplGen::new(self, od).generate(for_shell)
|
|
||||||
|
ComplGen::new(self).generate(for_shell, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) {
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
let out_dir = PathBuf::from(od);
|
||||||
|
|
||||||
|
let mut file = match File::create(out_dir.join(format!("{}_bash.sh", &*self.meta.bin_name.as_ref().unwrap()))) {
|
||||||
|
Err(why) => panic!("couldn't create bash completion file: {}",
|
||||||
|
why.description()),
|
||||||
|
Ok(file) => file,
|
||||||
|
};
|
||||||
|
self.gen_completions_to(for_shell, &mut file)
|
||||||
}
|
}
|
||||||
|
|
||||||
// actually adds the arguments
|
// actually adds the arguments
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use app::parser::Parser;
|
use app::parser::Parser;
|
||||||
|
@ -8,8 +5,8 @@ use shell::Shell;
|
||||||
use args::{ArgSettings, OptBuilder};
|
use args::{ArgSettings, OptBuilder};
|
||||||
|
|
||||||
macro_rules! w {
|
macro_rules! w {
|
||||||
($_self:ident, $f:ident, $to_w:expr) => {
|
($buf:expr, $to_w:expr) => {
|
||||||
match $f.write_all($to_w) {
|
match $buf.write_all($to_w) {
|
||||||
Ok(..) => (),
|
Ok(..) => (),
|
||||||
Err(..) => panic!(format!("Failed to write to file completions file")),
|
Err(..) => panic!(format!("Failed to write to file completions file")),
|
||||||
}
|
}
|
||||||
|
@ -18,33 +15,23 @@ macro_rules! w {
|
||||||
|
|
||||||
pub struct ComplGen<'a, 'b> where 'a: 'b {
|
pub struct ComplGen<'a, 'b> where 'a: 'b {
|
||||||
p: &'b Parser<'a, 'b>,
|
p: &'b Parser<'a, 'b>,
|
||||||
out_dir: OsString,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ComplGen<'a, 'b> {
|
impl<'a, 'b> ComplGen<'a, 'b> {
|
||||||
pub fn new(p: &'b Parser<'a, 'b>, od: OsString) -> Self {
|
pub fn new(p: &'b Parser<'a, 'b>) -> Self {
|
||||||
ComplGen {
|
ComplGen {
|
||||||
p: p,
|
p: p,
|
||||||
out_dir: od,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(&self, for_shell: Shell) {
|
pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
|
||||||
match for_shell {
|
match for_shell {
|
||||||
Shell::Bash => self.gen_bash(),
|
Shell::Bash => self.gen_bash(buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_bash(&self) {
|
fn gen_bash<W: Write>(&self, buf: &mut W) {
|
||||||
use std::error::Error;
|
w!(buf, format!(
|
||||||
let out_dir = PathBuf::from(&self.out_dir);
|
|
||||||
|
|
||||||
let mut file = match File::create(out_dir.join(format!("{}_bash.sh", &*self.p.meta.bin_name.as_ref().unwrap()))) {
|
|
||||||
Err(why) => panic!("couldn't create bash completion file: {}",
|
|
||||||
why.description()),
|
|
||||||
Ok(file) => file,
|
|
||||||
};
|
|
||||||
w!(self, file, format!(
|
|
||||||
"_{name}() {{
|
"_{name}() {{
|
||||||
local i cur prev opts cmds
|
local i cur prev opts cmds
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
|
|
25
tests/completions.rs
Normal file
25
tests/completions.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use clap::{App, Arg, SubCommand, Shell};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_generation() {
|
||||||
|
let mut app = App::new("myapp")
|
||||||
|
.about("Tests completions")
|
||||||
|
.arg(Arg::with_name("file")
|
||||||
|
.help("some input file"))
|
||||||
|
.subcommand(SubCommand::with_name("test")
|
||||||
|
.about("tests things")
|
||||||
|
.arg(Arg::with_name("case")
|
||||||
|
.long("case")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("the case to test")));
|
||||||
|
let mut buf = vec![];
|
||||||
|
app.gen_completions_to("myapp", Shell::Bash, &mut buf);
|
||||||
|
let string = String::from_utf8(buf).unwrap();
|
||||||
|
let first_line = string.lines().nth(0).unwrap();
|
||||||
|
let last_line = string.lines().rev().nth(0).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(first_line, "_myapp() {");
|
||||||
|
assert_eq!(last_line, "complete -F _myapp myapp");
|
||||||
|
}
|
Loading…
Reference in a new issue