diff --git a/Cargo.lock b/Cargo.lock index 4d67d19..637365c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,7 +148,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -234,10 +234,22 @@ checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", - "clap_lex", + "clap_lex 0.7.4", "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031" +dependencies = [ + "clap", + "clap_lex 1.0.0", + "is_executable", + "shlex", +] + [[package]] name = "clap_derive" version = "4.5.28" @@ -256,6 +268,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + [[package]] name = "colorchoice" version = "1.0.3" @@ -642,7 +660,7 @@ dependencies = [ "cfg-if", "libc", "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -821,6 +839,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" +[[package]] +name = "is_executable" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4" +dependencies = [ + "windows-sys 0.60.2", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1495,6 +1522,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "similar" version = "2.7.0" @@ -2022,13 +2055,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2037,7 +2076,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -2046,14 +2094,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2062,48 +2127,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.6.26" @@ -2143,6 +2256,7 @@ dependencies = [ "assert_fs", "cached", "clap", + "clap_complete", "cov-mark", "dirs", "dunce", diff --git a/Cargo.toml b/Cargo.toml index 59bc4b5..8efbdbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,10 @@ notify-debouncer-full = "0.5.0" owo-colors = { version = "4.1.0", features = ["supports-colors"] } regex = "1.11.1" rhai = { version = "1.21.0", features = [ - "std", - "internals", - "no_custom_syntax", - "sync", + "std", + "internals", + "no_custom_syntax", + "sync", ], default-features = false } tracing = "0.1.41" rhai-autodocs = { version = "0.8.0", optional = true } @@ -54,6 +54,7 @@ symlink = "0.1.0" hex = "0.4.3" walkdir = "2.5.0" tracing-tree = "0.4.0" +clap_complete = { version = "4.5.66", features = ["unstable-dynamic"] } # rhai-autodocs = { version = "0.7.0", path = "../../clones/rhai-autodocs" } [dev-dependencies] @@ -62,14 +63,14 @@ rstest = { version = "0.24.0", default-features = false } # tracing-tree = "0.4.0" assert_fs = "1.1.2" insta = { version = "1.42.1", default-features = false, features = [ - "colors", - "redactions", - "filters", + "colors", + "redactions", + "filters", ] } predicates = "3.1.3" test-log = { version = "0.2.17", default-features = false, features = [ - "color", - "trace", + "color", + "trace", ] } assert_cmd = "2.0.16" diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md index 615a2c1..9807ab9 100644 --- a/docs/src/getting_started.md +++ b/docs/src/getting_started.md @@ -30,6 +30,35 @@ $ yolk init This will create the yolk directory, with a default `yolk.rhai` file, and an `eggs` directory. +### Shell completions + +`yolk` comes with built-in shell completions for `bash`, `zsh`, `fish`, `elvish` and `powershell`. +To enable those, for `bash` run +```bash +echo "source <(COMPLETE=bash yolk)" >> ~/.bashrc +``` +for `zsh`, run +```zsh +echo "source <(COMPLETE=zsh yolk)" >> ~/.zshrc +``` +for `fish` run +```fish +echo "COMPLETE=fish yolk | source" >> ~/.config/fish/completions/yolk.fish +``` +for `elvish` run +```elvish +echo "eval (E:COMPLETE=elvish yolk | slurp)" >> ~/.elvish/rc.elv +``` +and for `powershell` run +```powershell +echo '$env:COMPLETE = "powershell"; yolk | Out-String | Invoke-Expression; Remove-Item Env:\COMPLETE' >> $PROFILE +``` + +Static completions may also be generated using +```sh +yolk shell-completions +``` + ### Adding your first egg let's say we want to manage the configuration for the `alacritty` terminal emulator. diff --git a/src/main.rs b/src/main.rs index dd82f50..080911e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,8 @@ use std::{ str::FromStr, }; -use clap::{Parser, Subcommand}; +use clap::{CommandFactory, Parser, Subcommand, ValueHint}; +use clap_complete::{env::CompleteEnv, generate, Shell}; use fs_err::PathExt as _; use miette::{Context as _, IntoDiagnostic, Result}; use notify_debouncer_full::{new_debouncer, notify::RecursiveMode, DebounceEventResult}; @@ -64,10 +65,10 @@ enum Command { /// Run a given shell command within a canonical context. I.e.: `yolk exec-canonical gitui`. #[clap(name = "exec-canonical")] ExecCanonical { - #[clap(allow_hyphen_values = true)] + #[clap(allow_hyphen_values = true, last=true, value_hint=ValueHint::CommandWithArguments)] command: Vec, }, - /// Evaluate a rhai expression. + /// Evaluate a Rhai expression. /// /// The expression is executed in the same scope that template tag expression are evaluated in. Eval { @@ -95,6 +96,7 @@ enum Command { canonical: bool, /// The path to the file you want to evaluate /// If not provided, the program will read from stdin + #[arg(value_hint=ValueHint::FilePath)] path: Option, }, @@ -116,24 +118,33 @@ enum Command { /// Run a git-command within the yolk directory. #[clap(alias = "g")] Git { - #[clap(allow_hyphen_values = true)] + // TODO: Test whether the command is being completed (works only in bash currently). + // Possibly would need to pre-fill with the `git` command + #[clap(allow_hyphen_values = true, last=true, value_hint=ValueHint::CommandWithArguments)] command: Vec, /// Force yolk to run the command with canonicalized files, regardless of what command it is. #[arg(long)] force_canonical: bool, }, + /// Generate shell completions + #[command(hide(true))] + ShellCompletions { shell: Option }, + #[command(hide(true))] RootManageSymlinks { #[arg(long, value_names = ["ORIGINAL::::SYMLINK_PATH"], required = false, value_parser=parse_symlink_pair)] create_symlink: Vec<(PathBuf, PathBuf)>, - #[arg(long, value_names = ["SYMLINK_PATH"], required = false)] + #[arg(long, value_names = ["SYMLINK_PATH"], required = false, value_hint=ValueHint::AnyPath)] delete_symlink: Vec, }, #[cfg(feature = "docgen")] #[command(hide(true))] - Docs { dir: PathBuf }, + Docs { + #[arg(value_hint=ValueHint::DirPath)] + dir: PathBuf, + }, } fn parse_symlink_pair(s: &str) -> Result<(PathBuf, PathBuf), String> { @@ -146,6 +157,9 @@ fn parse_symlink_pair(s: &str) -> Result<(PathBuf, PathBuf), String> { } pub(crate) fn main() -> Result<()> { + CompleteEnv::with_factory(Args::command) + .bin("yolk") + .complete(); let args = Args::parse(); init_logging(&args); @@ -220,7 +234,7 @@ fn run_command(args: Args) -> Result<()> { let yolk = Yolk::new(yolk_paths); match &args.command { Command::Init => yolk.init_yolk(None)?, - // TODO: we shoul likely also do this as part of init, maybe + // TODO: we should likely also do this as part of init, maybe Command::Safeguard => yolk.paths().safeguard_git_dir()?, Command::Status => { yolk.init_git_config(None)?; @@ -253,6 +267,18 @@ fn run_command(args: Args) -> Result<()> { println!("{}", text); } } + Command::ShellCompletions { shell } => { + if let Some(shell) = shell { + generate(*shell, &mut Args::command(), "yolk", &mut std::io::stdout()); + } else { + generate( + Shell::from_env().unwrap_or(Shell::Bash), + &mut Args::command(), + "yolk", + &mut std::io::stdout(), + ); + } + } Command::Sync { canonical } => { // Lets always ensure that the yolk dir is in a properly set up state. // This should later be replaced with some sort of version-aware compatibility check.