diff --git a/Cargo.lock b/Cargo.lock index 645d7e6c..68283c33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,6 +273,15 @@ dependencies = [ "terminal_size 0.3.0", ] +[[package]] +name = "clap_complete" +version = "4.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb745187d7f4d76267b37485a65e0149edd0e91a4cfcdd3f27524ad86cee9f3" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.4.7" @@ -636,6 +645,7 @@ dependencies = [ "backoff", "camino", "clap", + "clap_complete", "clearscreen", "command-group", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index a28d4ed2..1f1e8e0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ backoff = { version = "0.4.0", default-features = false } camino = "1.1.4" # Clap 4.4 is the last version supporting Rust 1.72. clap = { version = "~4.4", features = ["derive", "wrap_help", "env", "string"] } +clap_complete = "~4.4" clearscreen = "2.0.1" command-group = { version = "2.1.0", features = ["tokio", "with-tokio"] } crossterm = { version = "0.27.0", features = ["event-stream"] } diff --git a/nix/packages/ghciwatch.nix b/nix/packages/ghciwatch.nix index 97d5c0ee..cd06c517 100644 --- a/nix/packages/ghciwatch.nix +++ b/nix/packages/ghciwatch.nix @@ -3,6 +3,7 @@ stdenv, libiconv, darwin, + buildPackages, haskell, haskellPackages, ghc, @@ -12,6 +13,7 @@ rustPlatform, rust-analyzer, mdbook, + installShellFiles, # Versions of GHC to include in the environment for integration tests. # These should be attributes of `haskell.compiler`. ghcVersions ? null, @@ -103,6 +105,9 @@ inherit cargoArtifacts; }; + can-run-ghciwatch = stdenv.hostPlatform.emulatorAvailable buildPackages; + run-ghciwatch = "${stdenv.hostPlatform.emulator buildPackages} $out/bin/ghciwatch"; + releaseArgs = commonArgs // { @@ -114,6 +119,17 @@ # Only build `ghciwatch`, not the test macros. cargoBuildCommand = "cargoWithProfile build"; + nativeBuildInputs = (commonArgs.nativeBuildInputs or []) ++ [installShellFiles]; + + postInstall = + (commonArgs.postInstall or "") + + lib.optionalString can-run-ghciwatch '' + installShellCompletion --cmd ghciwatch \ + --bash <(${run-ghciwatch} --completions bash) \ + --fish <(${run-ghciwatch} --completions fish) \ + --zsh <(${run-ghciwatch} --completions zsh) + ''; + passthru = { inherit GHC_VERSIONS checks devShell user-manual user-manual-tar-xz; }; diff --git a/src/cli.rs b/src/cli.rs index 6dcda658..14e6431b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,6 +4,7 @@ use std::time::Duration; use camino::Utf8PathBuf; use clap::builder::ValueParserFactory; use clap::Parser; +use clap_complete::Shell; use tracing_subscriber::fmt::format::FmtSpan; use crate::clap::FmtSpanParserFactory; @@ -100,6 +101,10 @@ pub struct Opts { #[arg(long, hide = true)] pub generate_markdown_help: bool, + /// Generate shell completions for the given shell. + #[arg(long)] + pub completions: Option, + /// Lifecycle hooks and commands to run at various points. #[command(flatten)] pub hooks: crate::hooks::HookOpts, diff --git a/src/main.rs b/src/main.rs index 52a2face..00d48b2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::time::Duration; +use clap::CommandFactory; use clap::Parser; use ghciwatch::cli; use ghciwatch::run_ghci; @@ -30,6 +31,12 @@ async fn main() -> miette::Result<()> { return Ok(()); } + if let Some(shell) = opts.completions { + let mut command = cli::Opts::command(); + clap_complete::generate(shell, &mut command, "ghciwatch", &mut std::io::stdout()); + return Ok(()); + } + std::env::set_var("IN_GHCIWATCH", "1"); let (ghci_sender, ghci_receiver) = mpsc::channel(32);