Skip to content

Commit 6c5ade0

Browse files
JonasKruckenberglucasfernogsimonhyll
authored
feat(cli.rs) Shell completions (#4537)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio> Co-authored-by: Simon Hyll <hyllsimon@gmail.com> Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent 1b8001b commit 6c5ade0

File tree

6 files changed

+124
-6
lines changed

6 files changed

+124
-6
lines changed

.changes/feat-shell-completions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri-cli': 'minor:feat'
3+
'@tauri-apps/cli': 'minor:feat'
4+
---
5+
6+
Added `tauri completions` to generate shell completions scripts.

tooling/cli/Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tooling/cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ name = "cargo-tauri"
3939
path = "src/main.rs"
4040

4141
[dependencies]
42+
clap_complete = "4"
4243
clap = { version = "4.0", features = [ "derive" ] }
4344
anyhow = "1.0"
4445
tauri-bundler = { version = "1.2.1", path = "../bundler", default-features = false }

tooling/cli/src/completions.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use crate::Result;
6+
use anyhow::Context;
7+
use clap::{Command, Parser};
8+
use clap_complete::{generate, Shell};
9+
use log::info;
10+
11+
use std::{fs::write, path::PathBuf};
12+
13+
const PKG_MANAGERS: &[&str] = &["cargo", "pnpm", "npm", "yarn"];
14+
15+
#[derive(Debug, Clone, Parser)]
16+
#[clap(about = "Shell completions")]
17+
pub struct Options {
18+
/// Shell to generate a completion script for.
19+
#[clap(short, long, verbatim_doc_comment)]
20+
shell: Shell,
21+
/// Output file for the shell completions. By default the completions are printed to stdout.
22+
#[clap(short, long)]
23+
output: Option<PathBuf>,
24+
}
25+
26+
fn completions_for(shell: Shell, manager: &'static str, cmd: Command) -> Vec<u8> {
27+
let tauri = cmd.name("tauri");
28+
let mut command = if manager == "npm" {
29+
Command::new(manager)
30+
.bin_name(manager)
31+
.subcommand(Command::new("run").subcommand(tauri))
32+
} else {
33+
Command::new(manager).bin_name(manager).subcommand(tauri)
34+
};
35+
36+
let mut buf = Vec::new();
37+
generate(shell, &mut command, manager, &mut buf);
38+
buf
39+
}
40+
41+
fn get_completions(shell: Shell, cmd: Command) -> Result<String> {
42+
let completions = if shell == Shell::Bash {
43+
let mut completions =
44+
String::from_utf8_lossy(&completions_for(shell, "cargo", cmd)).into_owned();
45+
for manager in PKG_MANAGERS {
46+
completions.push_str(&format!(
47+
"complete -F _cargo -o bashdefault -o default {} tauri\n",
48+
if manager == &"npm" {
49+
"npm run"
50+
} else {
51+
manager
52+
}
53+
));
54+
}
55+
completions
56+
} else {
57+
let mut buffer = String::new();
58+
59+
for (i, manager) in PKG_MANAGERS.iter().enumerate() {
60+
let buf = String::from_utf8_lossy(&completions_for(shell, manager, cmd.clone())).into_owned();
61+
62+
let completions = match shell {
63+
Shell::PowerShell => {
64+
if i != 0 {
65+
// namespaces have already been imported
66+
buf
67+
.replace("using namespace System.Management.Automation.Language", "")
68+
.replace("using namespace System.Management.Automation", "")
69+
} else {
70+
buf
71+
}
72+
}
73+
_ => buf,
74+
};
75+
76+
buffer.push_str(&completions);
77+
buffer.push('\n');
78+
}
79+
80+
buffer
81+
};
82+
83+
Ok(completions)
84+
}
85+
86+
pub fn command(options: Options, cmd: Command) -> Result<()> {
87+
info!("Generating completion file for {}...", options.shell);
88+
89+
let completions = get_completions(options.shell, cmd)?;
90+
if let Some(output) = options.output {
91+
write(output, completions).context("failed to write to output path")?;
92+
} else {
93+
print!("{completions}");
94+
}
95+
96+
Ok(())
97+
}

tooling/cli/src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
pub use anyhow::Result;
66

77
mod build;
8+
mod completions;
89
mod dev;
910
mod helpers;
1011
mod icon;
@@ -51,7 +52,7 @@ pub struct PackageJson {
5152
propagate_version(true),
5253
no_binary_name(true)
5354
)]
54-
struct Cli {
55+
pub(crate) struct Cli {
5556
/// Enables verbose logging
5657
#[clap(short, long, global = true, action = ArgAction::Count)]
5758
verbose: u8,
@@ -68,6 +69,7 @@ enum Commands {
6869
Init(init::Options),
6970
Plugin(plugin::Cli),
7071
Signer(signer::Cli),
72+
Completions(completions::Options),
7173
}
7274

7375
fn format_error<I: CommandFactory>(err: clap::Error) -> clap::Error {
@@ -106,11 +108,12 @@ where
106108
I: IntoIterator<Item = A>,
107109
A: Into<OsString> + Clone,
108110
{
109-
let matches = match bin_name {
111+
let cli = match bin_name {
110112
Some(bin_name) => Cli::command().bin_name(bin_name),
111113
None => Cli::command(),
112-
}
113-
.get_matches_from(args);
114+
};
115+
let cli_ = cli.clone();
116+
let matches = cli.get_matches_from(args);
114117

115118
let res = Cli::from_arg_matches(&matches).map_err(format_error::<Cli>);
116119
let cli = match res {
@@ -167,6 +170,7 @@ where
167170
Commands::Init(options) => init::command(options)?,
168171
Commands::Plugin(cli) => plugin::command(cli)?,
169172
Commands::Signer(cli) => signer::command(cli)?,
173+
Commands::Completions(options) => completions::command(options, cli_)?,
170174
}
171175

172176
Ok(())

tooling/cli/src/signer/sign.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use tauri_utils::display_path;
1717
#[clap(about = "Sign a file")]
1818
pub struct Options {
1919
/// Load the private key from a file
20-
#[clap(short = 'k', long, conflicts_with("private-key-path"))]
20+
#[clap(short = 'k', long, conflicts_with("private_key_path"))]
2121
private_key: Option<String>,
2222
/// Load the private key from a string
23-
#[clap(short = 'f', long, conflicts_with("private-key"))]
23+
#[clap(short = 'f', long, conflicts_with("private_key"))]
2424
private_key_path: Option<PathBuf>,
2525
/// Set private key password when signing
2626
#[clap(short, long)]

0 commit comments

Comments
 (0)