diff --git a/Cargo.lock b/Cargo.lock index 02bf066e0d92ec8b3b1e985e44ba1dfa9576ac1b..7763e96f01ff5a71f76d7cb2c99517d812198779 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -795,26 +795,35 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.6" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1957aa4a5fb388f0a0a73ce7556c5b42025b874e5cdc2c670775e346e97adec0" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", "lazy_static", - "os_str_bytes", "strsim", "termcolor", - "textwrap 0.14.2", + "textwrap 0.15.0", +] + +[[package]] +name = "clap_complete" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da92e6facd8d73c22745a5d3cbb59bdf8e46e3235c923e516527d8e81eec14a4" +dependencies = [ + "clap", ] [[package]] name = "clap_derive" -version = "3.0.6" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153" +checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -823,6 +832,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "common-runtime" version = "0.8.0-dev" @@ -1271,6 +1289,7 @@ version = "3.0.0" dependencies = [ "async-io", "clap", + "clap_complete", "common-runtime", "frame-benchmarking", "frame-benchmarking-cli", @@ -4319,9 +4338,6 @@ name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] [[package]] name = "owning_ref" @@ -8093,9 +8109,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" diff --git a/Cargo.toml b/Cargo.toml index a7d8042684aaaeb75e0ef3957083c91d22057792..8b4b392ab2984e8d44d25f4a37bbae240df983d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ sp-membership = { path = 'primitives/membership' } # crates.io dependencies async-io = "1.3" clap = { version = "3.0", features = ["derive"] } +clap_complete = { version = "3" } futures = { version = "0.3.1", features = ["compat"] } hex = "0.4.3" jsonrpc-core = '18.0.0' diff --git a/README.md b/README.md index 11a84eff08d697f10797011cc0d5a2158fe242b6..d4b8a89ce4fbf85737b0f9b662e17bdeaf8a8375 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,39 @@ subcommands: ./target/release/duniter -h ``` +### Autocompletion + +One can generate autocompletion for its favorite shell using the following option: + +```sh +cargo run --release -- completion --generator <GENERATOR> +``` + +Where `GENERATOR` can be any of `bash`, `elvish`, `fish`, `powershell` and `zsh`. + +#### Bash + +First, get the completion file in a known place: + +```sh +mkdir -p ~/.local/share/duniter +cargo run --release -- completion --generator bash > ~/.local/share/duniter/completion.bash +``` + +You can now manually source the file when needed: + +```sh +source ~/.local/share/duniter/completion.bash +``` + +Or you can automatically source it at `bash` startup by adding this to your `~/.bashrc` file: + +```sh +[[ -f $HOME/.local/share/duniter/completion.bash ]] && source $HOME/.local/share/duniter/completion.bash +``` + +You can now enjoy semantic completion of the `./target/release/duniter` command using `<Tab>` key. + ## Single-Node Development Chain This command will start the single-node development chain with persistent state: diff --git a/node/src/cli.rs b/node/src/cli.rs index 5ddfb644c7830cca816d3d8b28c87b8d423abf54..46b235dce3dfdfed0b59e87dd542cde2017a86ac 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -72,6 +72,9 @@ pub enum Subcommand { /// The custom benchmark subcommmand benchmarking runtime pallets. #[clap(name = "benchmark", about = "Benchmark runtime pallets.")] Benchmark(frame_benchmarking_cli::BenchmarkCmd), + + /// Generate completion for various shell interpreters + Completion(Completion), } /// Block authoring scheme to be used by the node @@ -84,6 +87,10 @@ pub enum Sealing { /// Author a block upon receiving an RPC command Manual, /// Author blocks at a regular interval specified in milliseconds + // Clap limitiation with non-unit variant. + // While it compiles just fine with clap alone, clap_complete emits a compile-time error. + // See https://github.com/clap-rs/clap/issues/3543 + #[clap(skip)] Interval(u64), } @@ -110,3 +117,9 @@ impl std::str::FromStr for Sealing { }) } } + +#[derive(Debug, clap::Args)] +pub struct Completion { + #[clap(short, long, arg_enum)] + pub generator: clap_complete::Shell, +} diff --git a/node/src/command.rs b/node/src/command.rs index e8923c77571d646b3b0ecc6e22b18eaa8c7b9992..3b5897b8bcad6a69185f8586446ef0ecd558290c 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -27,6 +27,7 @@ use crate::service::GDevExecutor; use crate::service::GTestExecutor; use crate::service::{IdentifyRuntimeType, RuntimeType}; use crate::{chain_spec, service}; +use clap::CommandFactory; use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; impl SubstrateCli for Cli { @@ -259,6 +260,16 @@ pub fn run() -> sc_cli::Result<()> { .into()) } } + Some(Subcommand::Completion(cmd)) => { + let command = &mut Cli::command(); + clap_complete::generate( + cmd.generator, + command, + command.get_name().to_string(), + &mut std::io::stdout(), + ); + Ok(()) + } None => { let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|mut config| async move {