From 98e3021b9ae8c4cb05749872e40dedf4a8673942 Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:13:25 -0400 Subject: [PATCH 1/7] build: properly use `$OUT_DIR` as the destination for generated files --- build/main.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/build/main.rs b/build/main.rs index 505bc13..482241c 100644 --- a/build/main.rs +++ b/build/main.rs @@ -17,36 +17,27 @@ type DynResult = Result<(), Box>; fn main() -> DynResult { println!("cargo:rerun-if-changed=build/"); - mangen() + let out_dir = Path::new(&env::var("OUT_DIR")?).to_path_buf(); + mangen(out_dir) } /// Generate man page for binary and subcommands -fn mangen() -> DynResult { +fn mangen(out_dir: PathBuf) -> DynResult { println!("cargo:rerun-if-env-changed=PROJECT_NAME"); println!("cargo:rerun-if-env-changed=PROJECT_EXECUTABLE"); println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); eprintln!("Generating man pages"); - let out_dir = release_dir().join("manual/man1"); + let dest_dir = out_dir.join("manual/man1"); let cmd = Cmd::command().name("handlr"); - create_dir_all(&out_dir)?; + create_dir_all(&dest_dir)?; - clap_mangen::generate_to(cmd, &out_dir)?; + clap_mangen::generate_to(cmd, &dest_dir)?; // Remove hidden subcommand's manpage - remove_file(out_dir.join("handlr-autocomplete.1"))?; + remove_file(dest_dir.join("handlr-autocomplete.1"))?; Ok(()) } - -// Project root -fn project_root() -> PathBuf { - Path::new(&env!("CARGO_MANIFEST_DIR")).to_path_buf() -} - -/// Output directory for generated assets -fn release_dir() -> PathBuf { - project_root().join("release") -} From a1fb977ad086e61d86b72dddc400e0e5b29de01a Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:56:21 -0400 Subject: [PATCH 2/7] chore: add `clap_complete` crate --- Cargo.lock | 38 ++++++++++++++++++++++++++++++-------- Cargo.toml | 2 ++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88b332f..1a22506 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" @@ -207,9 +207,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.8" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -217,9 +217,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -227,11 +227,23 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb" +dependencies = [ + "clap", + "clap_lex", + "is_executable", + "shlex", +] + [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -630,6 +642,7 @@ version = "0.11.2" dependencies = [ "aho-corasick 0.7.20", "clap", + "clap_complete", "clap_mangen", "confy", "derive_more", @@ -825,6 +838,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_executable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2" +dependencies = [ + "winapi", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" diff --git a/Cargo.toml b/Cargo.toml index 898e68b..dcc5322 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ serde_ini = "0.2.0" serde_with = "3.8.3" wildmatch = "2.3.4" mutants = "0.0.3" +clap_complete = { version = "4.5.33", features = ["unstable-dynamic"] } [[bin]] name = "handlr" @@ -44,6 +45,7 @@ pretty_assertions = "1.4.0" [build-dependencies] clap = { version = "4.5.2", features = ["derive"] } +clap_complete = { version = "4.5.33", features = ["unstable-dynamic"] } clap_mangen = "0.2.20" [profile.release] From b246394bb74f2cee46628e88989b86637572955a Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sat, 12 Oct 2024 20:12:47 -0400 Subject: [PATCH 3/7] chore: add nix flake to help test completion scripts --- .envrc | 1 + .github/workflows/build_nix.yml | 13 +++++ .gitignore | 2 +- default.nix | 7 +++ flake.lock | 94 +++++++++++++++++++++++++++++++++ flake.nix | 58 ++++++++++++++++++++ shell.nix | 7 +++ 7 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 .envrc create mode 100644 .github/workflows/build_nix.yml create mode 100644 default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 shell.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/build_nix.yml b/.github/workflows/build_nix.yml new file mode 100644 index 0000000..87d2eba --- /dev/null +++ b/.github/workflows/build_nix.yml @@ -0,0 +1,13 @@ +name: "Build legacy Nix package on Ubuntu" + +on: + push: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: cachix/install-nix-action@v26 + - name: Building package + run: nix build diff --git a/.gitignore b/.gitignore index 7648e25..407ba64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target /mutants.out* -/release +/result diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..39bacff --- /dev/null +++ b/default.nix @@ -0,0 +1,7 @@ +(import ( + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; + sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; } +) { + src = ./.; +}).defaultNix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..232fa31 --- /dev/null +++ b/flake.lock @@ -0,0 +1,94 @@ +{ + "nodes": { + "naersk": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1721727458, + "narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=", + "owner": "nix-community", + "repo": "naersk", + "rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1728018373, + "narHash": "sha256-NOiTvBbRLIOe5F6RbHaAh6++BNjsb149fGZd1T4+KBg=", + "path": "/nix/store/sdzpqjwx7pdx6lsq6llyfqqf7hspp83c-source", + "rev": "bc947f541ae55e999ffdb4013441347d83b00feb", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "naersk": "naersk", + "nixpkgs": "nixpkgs_2", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..8d6184f --- /dev/null +++ b/flake.nix @@ -0,0 +1,58 @@ +{ + inputs = { + naersk.url = "github:nix-community/naersk/master"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + self, + nixpkgs, + utils, + naersk, + }: + utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system; }; + naersk-lib = pkgs.callPackage naersk { }; + in + { + defaultPackage = naersk-lib.buildPackage { + src = ./.; + nativeBuildInputs = with pkgs; [ + installShellFiles + shared-mime-info + ]; + buildInputs = with pkgs; [ libiconv ]; + + precheck = '' + export HOME=$TEMPDIR + ''; + + postInstall = '' + installShellCompletion --cmd handlr \ + --zsh <(COMPLETE=zsh $out/bin/handlr) \ + --bash <(COMPLETE=bash $out/bin/handlr) \ + --fish <(COMPLETE=fish $out/bin/handlr) \ + + installManPage target/release/build/handlr-regex-*/out/manual/man1/* + ''; + }; + devShell = + with pkgs; + mkShell { + buildInputs = [ + cargo + rustc + rustfmt + pre-commit + rustPackages.clippy + cargo-mutants + ]; + RUST_SRC_PATH = rustPlatform.rustLibSrc; + }; + } + ); +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..77db547 --- /dev/null +++ b/shell.nix @@ -0,0 +1,7 @@ +(import ( + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; + sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; } +) { + src = ./.; +}).shellNix From 733d03ae2caa9387a625f7001ab5879cb5fc6ff8 Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:56:46 -0400 Subject: [PATCH 4/7] feat: generate shell completion scripts --- Cargo.toml | 2 ++ build/apps.rs | 19 +++++++++++++++++++ build/common.rs | 4 ++++ build/main.rs | 15 ++++++++------- src/apps/system.rs | 12 +----------- src/cli.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++-- src/common/db.rs | 28 --------------------------- src/common/mod.rs | 2 +- src/main.rs | 20 +++++++++++--------- 9 files changed, 91 insertions(+), 58 deletions(-) create mode 100644 build/apps.rs diff --git a/Cargo.toml b/Cargo.toml index dcc5322..ef356ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,8 @@ pretty_assertions = "1.4.0" clap = { version = "4.5.2", features = ["derive"] } clap_complete = { version = "4.5.33", features = ["unstable-dynamic"] } clap_mangen = "0.2.20" +mime-db = "1.3.0" +mutants = "0.0.3" [profile.release] opt-level = "z" diff --git a/build/apps.rs b/build/apps.rs new file mode 100644 index 0000000..b038c18 --- /dev/null +++ b/build/apps.rs @@ -0,0 +1,19 @@ +// This file exists solely to trick build script into working +// These types are used by cli.rs, which cannot be transitively imported +// because they rely on their own dependencies and so on + +use std::error::Error; + +pub struct SystemApps; +pub struct DesktopEntry { + pub name: String, +} + +impl SystemApps { + pub fn get_entries( + ) -> Result, Box> + { + let name = "".to_string(); + Ok(vec![(name.clone(), DesktopEntry { name })].into_iter()) + } +} diff --git a/build/common.rs b/build/common.rs index b87fd3e..73e2054 100644 --- a/build/common.rs +++ b/build/common.rs @@ -5,3 +5,7 @@ pub type DesktopHandler = String; pub type MimeOrExtension = String; pub type UserPath = String; + +pub fn mime_types() -> Vec { + vec!["".to_string()] +} diff --git a/build/main.rs b/build/main.rs index 482241c..964e5ed 100644 --- a/build/main.rs +++ b/build/main.rs @@ -1,7 +1,10 @@ mod cli { include!("../src/cli.rs"); } -mod common; // Trick the cli module into cooperating + +// Trick the cli module into cooperating +mod apps; +mod common; use cli::Cmd; @@ -10,7 +13,7 @@ use std::{ env, error::Error, fs::{create_dir_all, remove_file}, - path::{Path, PathBuf}, + path::Path, }; type DynResult = Result<(), Box>; @@ -18,11 +21,11 @@ type DynResult = Result<(), Box>; fn main() -> DynResult { println!("cargo:rerun-if-changed=build/"); let out_dir = Path::new(&env::var("OUT_DIR")?).to_path_buf(); - mangen(out_dir) + mangen(&out_dir) } /// Generate man page for binary and subcommands -fn mangen(out_dir: PathBuf) -> DynResult { +fn mangen(out_dir: &Path) -> DynResult { println!("cargo:rerun-if-env-changed=PROJECT_NAME"); println!("cargo:rerun-if-env-changed=PROJECT_EXECUTABLE"); println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); @@ -30,11 +33,9 @@ fn mangen(out_dir: PathBuf) -> DynResult { eprintln!("Generating man pages"); let dest_dir = out_dir.join("manual/man1"); - let cmd = Cmd::command().name("handlr"); - create_dir_all(&dest_dir)?; - clap_mangen::generate_to(cmd, &dest_dir)?; + clap_mangen::generate_to(Cmd::command().name("handlr"), &dest_dir)?; // Remove hidden subcommand's manpage remove_file(dest_dir.join("handlr-autocomplete.1"))?; diff --git a/src/apps/system.rs b/src/apps/system.rs index 59f061f..2d992d8 100644 --- a/src/apps/system.rs +++ b/src/apps/system.rs @@ -4,7 +4,7 @@ use crate::{ error::Result, }; use mime::Mime; -use std::{collections::BTreeMap, convert::TryFrom, ffi::OsString, io::Write}; +use std::{collections::BTreeMap, convert::TryFrom, ffi::OsString}; #[derive(Debug, Default, Clone)] pub struct SystemApps { @@ -80,16 +80,6 @@ impl SystemApps { .find(|h| h.is_terminal_emulator()) } - /// List the available handlers - #[mutants::skip] // Cannot test directly, depends on system state - pub fn list_handlers(writer: &mut W) -> Result<()> { - Self::get_entries()?.try_for_each(|(_, e)| { - writeln!(writer, "{}\t{}", e.file_name.to_string_lossy(), e.name) - })?; - - Ok(()) - } - #[cfg(test)] /// Internal helper function for testing pub fn add_unassociated(&mut self, handler: DesktopHandler) { diff --git a/src/cli.rs b/src/cli.rs index 5a5aeca..5790496 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,11 @@ -pub use crate::common::{DesktopHandler, MimeOrExtension, UserPath}; -use clap::{Args, Parser}; +use std::fmt::Write; + +use crate::{ + apps::SystemApps, + common::{mime_types, DesktopHandler, MimeOrExtension, UserPath}, +}; +use clap::{builder::StyledStr, Args, Parser}; +use clap_complete::engine::{ArgValueCompleter, CompletionCandidate}; /// A better xdg-utils /// @@ -85,8 +91,10 @@ pub enum Cmd { /// /// Currently does not support regex handlers. Set { + #[clap(add = ArgValueCompleter::new(autocomplete_mimes))] /// Mimetype or file extension to operate on. mime: MimeOrExtension, + #[clap(add = ArgValueCompleter::new(autocomplete_desktop_files))] /// Desktop file of handler program handler: DesktopHandler, }, @@ -100,6 +108,7 @@ pub enum Cmd { /// /// Currently does not support regex handlers. Unset { + #[clap(add = ArgValueCompleter::new(autocomplete_mimes))] /// Mimetype or file extension to unset the default handler of mime: MimeOrExtension, }, @@ -112,6 +121,7 @@ pub enum Cmd { /// you will be prompted to select one using `selector` from ~/.config/handlr/handlr.toml. /// Otherwise, the default handler will be opened. Launch { + #[clap(add = ArgValueCompleter::new(autocomplete_mimes))] /// Mimetype or file extension to launch the handler of mime: MimeOrExtension, /// Arguments to pass to handler program @@ -143,6 +153,7 @@ pub enum Cmd { #[clap(long)] /// Output handler info as json json: bool, + #[clap(add = ArgValueCompleter::new(autocomplete_mimes))] /// Mimetype to get the handler of mime: MimeOrExtension, #[command(flatten)] @@ -159,8 +170,10 @@ pub enum Cmd { /// This subcommand adds secondary handlers that coexist with the default /// and does not overwrite existing handlers. Add { + #[clap(add = ArgValueCompleter::new(autocomplete_mimes))] /// Mimetype to add handler to mime: MimeOrExtension, + #[clap(add = ArgValueCompleter::new(autocomplete_desktop_files))] /// Desktop file of handler program handler: DesktopHandler, }, @@ -172,8 +185,10 @@ pub enum Cmd { /// Literal wildcards (e.g. `text/*`) will be favored over matching mimetypes if present. /// Otherwise, mimes matching wildcards (e.g. `text/plain`, etc.) will have their handlers removed. Remove { + #[clap(add = ArgValueCompleter::new(autocomplete_mimes))] /// Mimetype to remove handler from mime: MimeOrExtension, + #[clap(add = ArgValueCompleter::new(autocomplete_desktop_files))] /// Desktop file of handler program to remove handler: DesktopHandler, }, @@ -235,3 +250,31 @@ pub struct SelectorArgs { /// Disable selector, overrides `enable_selector` pub disable_selector: bool, } + +/// Generate candidates for mimes and file extensions to use +#[mutants::skip] // TODO: figure out how to test with golden tests +fn autocomplete_mimes(_current: &std::ffi::OsStr) -> Vec { + let mut mimes = mime_db::EXTENSIONS + .iter() + .map(|(ext, _)| CompletionCandidate::new(format!(".{ext}"))) + .chain(mime_types().iter().map(CompletionCandidate::new)) + .collect::>(); + mimes.sort(); + mimes +} + +/// Generate candidates for desktop files +#[mutants::skip] // Cannot test directly, relies on system state +fn autocomplete_desktop_files( + _current: &std::ffi::OsStr, +) -> Vec { + SystemApps::get_entries() + .expect("Could not get system desktop entries") + .map(|(path, entry)| { + let mut name = StyledStr::new(); + write!(name, "{}", entry.name) + .expect("Could not write desktop entry name"); + CompletionCandidate::new(path).help(Some(name)) + }) + .collect() +} diff --git a/src/common/db.rs b/src/common/db.rs index 892fc92..94223fe 100644 --- a/src/common/db.rs +++ b/src/common/db.rs @@ -1,8 +1,5 @@ use itertools::Itertools; -use crate::error::Result; -use std::io::Write; - static CUSTOM_MIMES: &[&str] = &[ "inode/directory", "x-scheme-handler/http", @@ -22,28 +19,3 @@ pub fn mime_types() -> Vec { ) .collect_vec() } - -pub fn autocomplete(writer: &mut W) -> Result<()> { - mime_db::EXTENSIONS - .iter() - .try_for_each(|(ext, _)| writeln!(writer, ".{}", ext))?; - - mime_types() - .iter() - .try_for_each(|mime| writeln!(writer, "{}", mime))?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn autocomplete_mimes_and_extensions() -> Result<()> { - let mut buffer = Vec::new(); - autocomplete(&mut buffer)?; - goldie::assert!(String::from_utf8(buffer)?); - Ok(()) - } -} diff --git a/src/common/mod.rs b/src/common/mod.rs index 2465378..5abb9fe 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -5,7 +5,7 @@ mod mime_types; mod path; mod table; -pub use self::db::{autocomplete as db_autocomplete, mime_types}; +pub use self::db::mime_types; pub use desktop_entry::{DesktopEntry, Mode as ExecMode}; pub use handler::{ DesktopHandler, Handleable, Handler, RegexApps, RegexHandler, diff --git a/src/main.rs b/src/main.rs index 739563f..49945d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,18 @@ mod config; mod error; mod utils; -use apps::SystemApps; use cli::Cmd; use common::mime_table; use config::Config; use error::Result; -use clap::Parser; +use clap::{CommandFactory, Parser}; +use clap_complete::CompleteEnv; #[mutants::skip] // Cannot test directly at the moment fn main() -> Result<()> { + CompleteEnv::with_factory(|| Cmd::command().name("handlr")).complete(); + let mut config = Config::new()?; let mut stdout = std::io::stdout().lock(); @@ -51,14 +53,14 @@ fn main() -> Result<()> { Cmd::Unset { mime } => config.unset_handler(&mime), Cmd::Remove { mime, handler } => config.remove_handler(&mime, &handler), Cmd::Autocomplete { - desktop_files, - mimes, + desktop_files: _, + mimes: _, } => { - if desktop_files { - SystemApps::list_handlers(&mut stdout)?; - } else if mimes { - common::db_autocomplete(&mut stdout)?; - } + // if desktop_files { + // SystemApps::list_handlers(&mut stdout)?; + // } else if mimes { + // common::db_autocomplete(&mut stdout)?; + // } Ok(()) } }; From 23dc3fc6650388ccb77028bda52e3c78b92a8ac3 Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:45:48 -0400 Subject: [PATCH 5/7] refactor: remove redundant `autocomplete` subcommand --- build/main.rs | 3 --- src/cli.rs | 16 ---------------- src/main.rs | 11 ----------- 3 files changed, 30 deletions(-) diff --git a/build/main.rs b/build/main.rs index 964e5ed..dd185c9 100644 --- a/build/main.rs +++ b/build/main.rs @@ -37,8 +37,5 @@ fn mangen(out_dir: &Path) -> DynResult { clap_mangen::generate_to(Cmd::command().name("handlr"), &dest_dir)?; - // Remove hidden subcommand's manpage - remove_file(dest_dir.join("handlr-autocomplete.1"))?; - Ok(()) } diff --git a/src/cli.rs b/src/cli.rs index 5790496..1e1a27b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -219,22 +219,6 @@ pub enum Cmd { /// Output mimetype info as json json: bool, }, - - #[clap(hide = true)] - /// Helper subcommand for autocompletion scripts; should be hidden - /// - /// This should not be visible in `handlr --help` output, autocompletion, or man pages. - /// If you see this there, please open an issue on GitHub. - /// - /// However it is fine if this shows up in the output of `handlr autocomplete --help`. - Autocomplete { - #[clap(short)] - /// Autocomplete for desktop files present on system - desktop_files: bool, - #[clap(short)] - /// Autocomplete for mimetypes/file extensions - mimes: bool, - }, } #[derive(Clone, Args)] diff --git a/src/main.rs b/src/main.rs index 49945d7..52c488b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,17 +52,6 @@ fn main() -> Result<()> { Cmd::List { all, json } => config.print(&mut stdout, all, json), Cmd::Unset { mime } => config.unset_handler(&mime), Cmd::Remove { mime, handler } => config.remove_handler(&mime, &handler), - Cmd::Autocomplete { - desktop_files: _, - mimes: _, - } => { - // if desktop_files { - // SystemApps::list_handlers(&mut stdout)?; - // } else if mimes { - // common::db_autocomplete(&mut stdout)?; - // } - Ok(()) - } }; // Issue a notification if handlr is not being run in a terminal From 902eb4b85b1aae38fde9d6126f72d915e6e8c278 Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:46:54 -0400 Subject: [PATCH 6/7] chore: delete redundant completion scripts --- assets/completions/_handlr | 77 ---------------------------------- assets/completions/handlr | 34 --------------- assets/completions/handlr.fish | 32 -------------- 3 files changed, 143 deletions(-) delete mode 100644 assets/completions/_handlr delete mode 100644 assets/completions/handlr delete mode 100644 assets/completions/handlr.fish diff --git a/assets/completions/_handlr b/assets/completions/_handlr deleted file mode 100644 index ad741d8..0000000 --- a/assets/completions/_handlr +++ /dev/null @@ -1,77 +0,0 @@ -#compdef handlr - -_handlr_types() { - declare -a types - types=(${${(f)"$(handlr autocomplete -m)"}}) - _describe -t types "types" types -} - -_handlr_desktops() { - declare -a desktops - desktops=(${${(f)"$(handlr autocomplete -d | tr '\t' ':')"}}) - _describe -t desktops "desktops" desktops -} - -_handlr_commands() { - declare -a subcommands - subcommands=( - 'list:List default apps and the associated handlers' - 'open:Open a path/URL with its default handler' - 'set:Set the default handler for mime/extension' - 'unset:Unset the default handler for mime/extension' - 'launch:Launch the handler for specified extension/mime with optional arguments' - 'get:Get handler for this mime/extension' - 'add:Add a handler for given mime/extension; note that the first handler is the default' - 'remove:Remove a given handler from a given mime/extension' - 'mime:Get the mimetype of a path/URL' - ) - _describe -t handlr-commands "command" subcommands -} - -_handlr_subcommand () { - case "$words[1]" in - (list) - ;; - (open|mime) - _alternative '1:filename/path:_files' - ;; - (get|unset) - _arguments ':types:_handlr_types' - ;; - (launch) - _arguments \ - '1:types:_handlr_types' \ - '2:filename/path:_files' - ;; - (set|add|remove) - _arguments \ - '1:type:_handlr_types' \ - '2:desktop:_handlr_desktops' - ;; - (*) - _message 'Unknown subcommand' - esac -} - -_handlr () { - local curcontext="$curcontext" state line - typeset -A opt_args - - _arguments -C \ - '--version[get version]:' \ - '--help[get help]:' \ - '(-): :->command' \ - '(-)*:: :->arguments' - - case $state in - (command) - _handlr_commands - ;; - (arguments) - curcontext=${curcontext%:*:*}:handlr-$words[1]: - _handlr_subcommand - ;; - esac -} - -_handlr "$@" diff --git a/assets/completions/handlr b/assets/completions/handlr deleted file mode 100644 index 8d136ae..0000000 --- a/assets/completions/handlr +++ /dev/null @@ -1,34 +0,0 @@ -_handlr() -{ - local cur prev words cword - _init_completion || return - - if ((cword == 1)); then - COMPREPLY=($(compgen -W 'get help launch list open set unset mime' -- "$cur")) - else - case ${words[1]} in - set | add | remove) - if ((cword == 2)); then - COMPREPLY=($(compgen -W '$(handlr autocomplete -m)' -- "$cur")) - elif ((cword == 3)); then - COMPREPLY=($(compgen -W '$(handlr autocomplete -d | cut -f1)' -- "$cur")) - fi - ;; - open | mime) - _filedir - ;; - unset | get) - COMPREPLY=($(compgen -W '$(handlr autocomplete -m)' -- "$cur")) - ;; - launch) - if ((cword == 2)); then - COMPREPLY=($(compgen -W '$(handlr autocomplete -m)' -- "$cur")) - else - _filedir - fi - ;; - esac - fi -} - -complete -F _handlr handlr diff --git a/assets/completions/handlr.fish b/assets/completions/handlr.fish deleted file mode 100644 index e16fb73..0000000 --- a/assets/completions/handlr.fish +++ /dev/null @@ -1,32 +0,0 @@ -function __handlr_autocomplete - function subcommands - set -l handlr_commands 'add get help launch list open set unset mime' - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "add" -d "Add a handler for given mime/extension" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "get" -d "Show handler for mime" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "launch" -d "Launch given handler with path/args" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "list" -d "Show handlers (default applications)" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "open" -d "Open path/URL with default handler (like xdg-open)" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "remove" -d "Remove a given handler from a given mime/extension" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "set" -d "Set handler for extension (e.g. pdf) or mime type" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "unset" -d "Unset handler" - complete -f -c handlr -n "not __fish_seen_subcommand_from $handlr_commands" -a "mime" -d "Get mimetype of path/URL" - end - - function _set_add - complete -f -c handlr -n '__fish_seen_subcommand_from set; __fish_prev_arg_in "set"' -a '(handlr autocomplete -m)' - complete -f -c handlr -n '__fish_seen_subcommand_from set; set -l last (commandline -pco)[-2]; [ "$last" = "set" ]' -a '(handlr autocomplete -d)' - - complete -f -c handlr -n '__fish_seen_subcommand_from add; __fish_prev_arg_in "add"' -a '(handlr autocomplete -m)' - complete -f -c handlr -n '__fish_seen_subcommand_from add; set -l last (commandline -pco)[-2]; [ "$last" = "add" ]' -a '(handlr autocomplete -d)' - end - - subcommands - _set_add - complete -f -c handlr -n '__fish_seen_subcommand_from get' -a '(handlr autocomplete -m)' - complete -f -c handlr -n '__fish_seen_subcommand_from get' -l 'json' - complete -f -c handlr -n '__fish_seen_subcommand_from unset' -a '(handlr autocomplete -m)' - complete -f -c handlr -n '__fish_seen_subcommand_from launch; __fish_prev_arg_in launch' -a '(handlr autocomplete -m)' - -end - -__handlr_autocomplete From 6e2132891ec125adb14f9948c7fff86ec916a2e5 Mon Sep 17 00:00:00 2001 From: Anomalocaridid <29845794+Anomalocaridid@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:56:15 -0400 Subject: [PATCH 7/7] docs: update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 4fc7d00..9546300 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,17 @@ This is off by default and will not automatically expand wildcards already prese In addition, regardless of settings, literal wildcards are preferred when using `handlr remove` and `handlr unset`. (e.g. When using `handlr remove text/*`, if `text/*` is present, it will be removed, but `text/plain`, etc. will not be.) +## Completion scripts + +To generate a shell completion script, run `COMPLETE= handlr`, where `` is the name of the target shell (e.g. bash, zsh, fish, elvish, powershell, etc.). Note that this will only print it to stdout rather than creating a file or installing the script automatically. + +If you usually install `handlr-regex` from your distribution's repository, and you are not involved with packaging it, you probably do not need to worry about this. + +See [`clap_complete`'s documentation](https://docs.rs/clap_complete/latest/clap_complete/aot/enum.Shell.html) for the list of currently supported shells. + +> [!NOTE] +> This currently relies on unstable features of the `clap_complete` crate and may potentially change in the future. + ## Screenshots