mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +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:
|
||||
///
|
||||
/// ```ignore
|
||||
/// src/main.rs
|
||||
/// // src/main.rs
|
||||
///
|
||||
/// mod cli;
|
||||
///
|
||||
|
@ -1023,6 +1023,44 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
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
|
||||
/// 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,
|
||||
|
|
|
@ -26,6 +26,8 @@ use app::meta::AppMeta;
|
|||
use args::MatchedArg;
|
||||
use shell::Shell;
|
||||
use completions::ComplGen;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[doc(hidden)]
|
||||
|
@ -99,10 +101,25 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
.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.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
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::ffi::OsString;
|
||||
use std::io::Write;
|
||||
|
||||
use app::parser::Parser;
|
||||
|
@ -8,8 +5,8 @@ use shell::Shell;
|
|||
use args::{ArgSettings, OptBuilder};
|
||||
|
||||
macro_rules! w {
|
||||
($_self:ident, $f:ident, $to_w:expr) => {
|
||||
match $f.write_all($to_w) {
|
||||
($buf:expr, $to_w:expr) => {
|
||||
match $buf.write_all($to_w) {
|
||||
Ok(..) => (),
|
||||
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 {
|
||||
p: &'b Parser<'a, 'b>,
|
||||
out_dir: OsString,
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
Shell::Bash => self.gen_bash(),
|
||||
Shell::Bash => self.gen_bash(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_bash(&self) {
|
||||
use std::error::Error;
|
||||
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!(
|
||||
fn gen_bash<W: Write>(&self, buf: &mut W) {
|
||||
w!(buf, format!(
|
||||
"_{name}() {{
|
||||
local i cur prev opts cmds
|
||||
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