Merge remote-tracking branch 'origin/upstream' am: f0eba62f7c am: a0b694e0bb am: 09f72c1a06

Original change: undetermined

Change-Id: I79206c8bb7caad4dc4dcf22f17d579c315cbc66b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..2df91bd
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "9bfa5a338c6532419e2477e89708395fbb02ca06"
+  },
+  "path_in_vcs": "clap_complete"
+}
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..f11d5ad
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,21 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file as changes will be overridden on upgrade.
+
+// TODO: Add license.
+rust_library {
+    name: "libclap_complete",
+    host_supported: true,
+    crate_name: "clap_complete",
+    cargo_env_compat: true,
+    cargo_pkg_version: "4.4.4",
+    srcs: ["src/lib.rs"],
+    edition: "2021",
+    features: ["default"],
+    rustlibs: ["libclap"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    product_available: true,
+    vendor_available: true,
+}
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..452559b
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,1039 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "anstream"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff2cf94a3dbe2d57cbd56485e1bd7436455058034d6c2d47be51d4e5e4bc6ab"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
+dependencies = [
+ "anstyle",
+ "backtrace",
+ "bitflags",
+ "clap_lex 0.5.0",
+]
+
+[[package]]
+name = "clap_complete"
+version = "4.4.4"
+dependencies = [
+ "clap",
+ "clap_lex 0.6.0",
+ "completest",
+ "is_executable",
+ "pathdiff",
+ "shlex",
+ "snapbox",
+ "trycmd",
+ "unicode-xid",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "completest"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8084b60ec7306f1e9b4d855061147a5721eabbd860854213dd69679000cc86c"
+dependencies = [
+ "ptyprocess",
+ "vt100",
+]
+
+[[package]]
+name = "content_inspector"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset 0.6.5",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "dunce"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
+
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
+[[package]]
+name = "errno"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "escargot"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6"
+dependencies = [
+ "log",
+ "once_cell",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "humantime-serde"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
+dependencies = [
+ "humantime",
+ "serde",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
+dependencies = [
+ "libc",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "is_executable"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "memoffset 0.7.1",
+ "pin-utils",
+ "static_assertions",
+]
+
+[[package]]
+name = "normalize-line-endings"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+
+[[package]]
+name = "num_cpus"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.30.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "os_pipe"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "ptyprocess"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e05aef7befb11a210468a2d77d978dde2c6381a0381e33beb575e91f57fe8cf"
+dependencies = [
+ "nix",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "rustix"
+version = "0.37.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "shlex"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+
+[[package]]
+name = "similar"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
+
+[[package]]
+name = "snapbox"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b439536a42c43be148b610c7f7f968fb79a457254910a9cb20900da73cd3271"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "content_inspector",
+ "dunce",
+ "escargot",
+ "filetime",
+ "libc",
+ "normalize-line-endings",
+ "os_pipe",
+ "similar",
+ "snapbox-macros",
+ "tempfile",
+ "wait-timeout",
+ "walkdir",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "snapbox-macros"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed1559baff8a696add3322b9be3e940d433e7bb4e38d79017205fd37ff28b28e"
+dependencies = [
+ "anstream",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "syn"
+version = "2.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d964908cec0d030b812013af25a0e57fddfadb1e066ecc6681d86253129d4f"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "trycmd"
+version = "0.14.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5bff680f217f2c7cc246aa5313ef9c1802449b1b8f859d28765355fda1c421f"
+dependencies = [
+ "glob",
+ "humantime",
+ "humantime-serde",
+ "rayon",
+ "serde",
+ "shlex",
+ "snapbox",
+ "toml_edit",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "vt100"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
+dependencies = [
+ "itoa",
+ "log",
+ "unicode-width",
+ "vte",
+]
+
+[[package]]
+name = "vte"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
+dependencies = [
+ "arrayvec",
+ "utf8parse",
+ "vte_generate_state_changes",
+]
+
+[[package]]
+name = "vte_generate_state_changes"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "winnow"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
+dependencies = [
+ "memchr",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..d71f151
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,154 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.70.0"
+name = "clap_complete"
+version = "4.4.4"
+include = [
+    "build.rs",
+    "src/**/*",
+    "Cargo.toml",
+    "LICENSE*",
+    "README.md",
+    "benches/**/*",
+    "examples/**/*",
+]
+description = "Generate shell completion scripts for your clap::Command"
+readme = "README.md"
+keywords = [
+    "clap",
+    "cli",
+    "completion",
+    "bash",
+]
+categories = ["command-line-interface"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/clap-rs/clap/tree/master/clap_complete"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+min = 1
+replace = "{{version}}"
+search = "Unreleased"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = "...{{tag_name}}"
+search = '\.\.\.HEAD'
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+min = 1
+replace = "{{date}}"
+search = "ReleaseDate"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = """
+<!-- next-header -->
+## [Unreleased] - ReleaseDate
+"""
+search = "<!-- next-header -->"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = """
+<!-- next-url -->
+[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD"""
+search = "<!-- next-url -->"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 4
+file = "README.md"
+prerelease = true
+replace = "github.com/clap-rs/clap/blob/{{tag_name}}/"
+search = "github.com/clap-rs/clap/blob/[^/]+/"
+
+[lib]
+bench = false
+
+[[example]]
+name = "dynamic"
+required-features = ["unstable-dynamic"]
+
+[dependencies.clap]
+version = "4.1.0"
+features = ["std"]
+default-features = false
+
+[dependencies.clap_lex]
+version = "0.6.0"
+optional = true
+
+[dependencies.is_executable]
+version = "1.0.1"
+optional = true
+
+[dependencies.pathdiff]
+version = "0.2.1"
+optional = true
+
+[dependencies.shlex]
+version = "1.1.0"
+optional = true
+
+[dependencies.unicode-xid]
+version = "0.2.2"
+optional = true
+
+[dev-dependencies.clap]
+version = "4.0.0"
+features = [
+    "std",
+    "derive",
+    "help",
+]
+default-features = false
+
+[dev-dependencies.completest]
+version = "0.1.0"
+
+[dev-dependencies.snapbox]
+version = "0.4.13"
+features = [
+    "diff",
+    "path",
+    "examples",
+]
+
+[dev-dependencies.trycmd]
+version = "0.14.18"
+features = [
+    "color-auto",
+    "diff",
+    "examples",
+]
+default-features = false
+
+[features]
+debug = ["clap/debug"]
+default = []
+unstable-dynamic = [
+    "dep:clap_lex",
+    "dep:shlex",
+    "dep:unicode-xid",
+    "clap/derive",
+    "dep:is_executable",
+    "dep:pathdiff",
+]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..c059a33
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,56 @@
+[package]
+name = "clap_complete"
+version = "4.4.4"
+description = "Generate shell completion scripts for your clap::Command"
+repository = "https://github.com/clap-rs/clap/tree/master/clap_complete"
+categories = ["command-line-interface"]
+keywords = [
+  "clap",
+  "cli",
+  "completion",
+  "bash",
+]
+license.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+include.workspace = true
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[package.metadata.release]
+pre-release-replacements = [
+  {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
+  {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1},
+  {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
+  {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1},
+  {file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD", exactly=1},
+  {file="README.md", search="github.com/clap-rs/clap/blob/[^/]+/", replace="github.com/clap-rs/clap/blob/{{tag_name}}/", exactly=4, prerelease = true},
+]
+
+[lib]
+bench = false
+
+[dependencies]
+clap = { path = "../", version = "4.1.0", default-features = false, features = ["std"] }
+clap_lex = { path = "../clap_lex", version = "0.6.0", optional = true }
+is_executable = { version = "1.0.1", optional = true }
+pathdiff = { version = "0.2.1", optional = true }
+shlex = { version = "1.1.0", optional = true }
+unicode-xid = { version = "0.2.2", optional = true }
+
+[dev-dependencies]
+snapbox = { version = "0.4.13", features = ["diff", "path", "examples"] }
+# Cutting out `filesystem` feature
+trycmd = { version = "0.14.18", default-features = false, features = ["color-auto", "diff", "examples"] }
+completest = "0.1.0"
+clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "derive", "help"] }
+
+[[example]]
+name = "dynamic"
+required-features = ["unstable-dynamic"]
+
+[features]
+default = []
+unstable-dynamic = ["dep:clap_lex", "dep:shlex", "dep:unicode-xid", "clap/derive", "dep:is_executable", "dep:pathdiff"]
+debug = ["clap/debug"]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..6b579aa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-APACHE
\ No newline at end of file
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..7b05b84
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2022 Kevin B. Knapp and Clap Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d2f34a9
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "clap_complete"
+description: "Generate shell completion scripts for your clap::Command"
+third_party {
+  identifier {
+    type: "crates.io"
+    value: "https://crates.io/crates/clap_complete"
+  }
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/clap_complete/clap_complete-4.4.4.crate"
+  }
+  version: "4.4.4"
+  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2023
+    month: 11
+    day: 20
+  }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..5a2b844
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:main:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cc283ce
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+<!-- omit in TOC -->
+# clap_complete
+
+> **Shell completion generation for `clap`**
+
+[![Crates.io](https://img.shields.io/crates/v/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete)
+[![Crates.io](https://img.shields.io/crates/d/clap_complete?style=flat-square)](https://crates.io/crates/clap_complete)
+[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.4.4/LICENSE-APACHE)
+[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_complete-v4.4.4/LICENSE-MIT)
+
+Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
+
+1. [About](#about)
+2. [API Reference](https://docs.rs/clap_complete)
+3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
+4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.4.4/clap_complete/CONTRIBUTING.md)
+5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.4.4/README.md#sponsors)
+
+## About
+
+### Related Projects
+
+- [clap_complete_fig](https://crates.io/crates/clap_complete_fig) for [fig](https://fig.io/) shell completion support
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..fca634f
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,3 @@
+{
+    "run_cargo": false
+}
diff --git a/examples/completion-derive.rs b/examples/completion-derive.rs
new file mode 100644
index 0000000..9f1a55d
--- /dev/null
+++ b/examples/completion-derive.rs
@@ -0,0 +1,83 @@
+//! How to use value hints and generate shell completions.
+//!
+//! Usage with zsh:
+//! ```console
+//! $ cargo run --example completion-derive -- --generate=zsh > /usr/local/share/zsh/site-functions/_completion_derive
+//! $ compinit
+//! $ ./target/debug/examples/completion_derive --<TAB>
+//! ```
+//! fish:
+//! ```console
+//! $ cargo run --example completion-derive -- --generate=fish > completion_derive.fish
+//! $ . ./completion_derive.fish
+//! $ ./target/debug/examples/completion_derive --<TAB>
+//! ```
+use clap::{Args, Command, CommandFactory, Parser, Subcommand, ValueHint};
+use clap_complete::{generate, Generator, Shell};
+use std::ffi::OsString;
+use std::io;
+use std::path::PathBuf;
+
+#[derive(Parser, Debug, PartialEq)]
+#[command(name = "completion-derive")]
+struct Opt {
+    // If provided, outputs the completion file for given shell
+    #[arg(long = "generate", value_enum)]
+    generator: Option<Shell>,
+    #[command(subcommand)]
+    command: Option<Commands>,
+}
+
+#[derive(Subcommand, Debug, PartialEq)]
+enum Commands {
+    #[command(visible_alias = "hint")]
+    ValueHint(ValueHintOpt),
+}
+
+#[derive(Args, Debug, PartialEq)]
+struct ValueHintOpt {
+    // Showcasing all possible ValueHints:
+    #[arg(long, value_hint = ValueHint::Unknown)]
+    unknown: Option<String>,
+    #[arg(long, value_hint = ValueHint::Other)]
+    other: Option<String>,
+    #[arg(short, long, value_hint = ValueHint::AnyPath)]
+    path: Option<PathBuf>,
+    #[arg(short, long, value_hint = ValueHint::FilePath)]
+    file: Option<PathBuf>,
+    #[arg(short, long, value_hint = ValueHint::DirPath)]
+    dir: Option<PathBuf>,
+    #[arg(short, long, value_hint = ValueHint::ExecutablePath)]
+    exe: Option<PathBuf>,
+    #[arg(long, value_hint = ValueHint::CommandName)]
+    cmd_name: Option<OsString>,
+    #[arg(short, long, value_hint = ValueHint::CommandString)]
+    cmd: Option<String>,
+    // Command::trailing_var_ar is required to use ValueHint::CommandWithArguments
+    #[arg(trailing_var_arg = true, value_hint = ValueHint::CommandWithArguments)]
+    command_with_args: Vec<String>,
+    #[arg(short, long, value_hint = ValueHint::Username)]
+    user: Option<String>,
+    #[arg(long, value_hint = ValueHint::Hostname)]
+    host: Option<String>,
+    #[arg(long, value_hint = ValueHint::Url)]
+    url: Option<String>,
+    #[arg(long, value_hint = ValueHint::EmailAddress)]
+    email: Option<String>,
+}
+
+fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
+    generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+}
+
+fn main() {
+    let opt = Opt::parse();
+
+    if let Some(generator) = opt.generator {
+        let mut cmd = Opt::command();
+        eprintln!("Generating completion file for {generator:?}...");
+        print_completions(generator, &mut cmd);
+    } else {
+        println!("{opt:#?}");
+    }
+}
diff --git a/examples/completion.rs b/examples/completion.rs
new file mode 100644
index 0000000..0890542
--- /dev/null
+++ b/examples/completion.rs
@@ -0,0 +1,109 @@
+//! Example to test arguments with different ValueHint values.
+//!
+//! Usage with zsh:
+//! ```console
+//! $ cargo run --example completion -- --generate=zsh > /usr/local/share/zsh/site-functions/_completion$
+//! $ compinit
+//! $ ./target/debug/examples/completion --<TAB>
+//! ```
+//! fish:
+//! ```console
+//! $ cargo run --example completion -- --generate=fish > completion.fish
+//! $ . ./completion.fish
+//! $ ./target/debug/examples/completion --<TAB>
+//! ```
+use clap::{value_parser, Arg, Command, ValueHint};
+use clap_complete::{generate, Generator, Shell};
+use std::io;
+
+fn build_cli() -> Command {
+    let value_hint_command = Command::new("value-hint")
+        .visible_alias("hint")
+        .arg(
+            Arg::new("unknown")
+                .long("unknown")
+                .value_hint(ValueHint::Unknown),
+        )
+        .arg(Arg::new("other").long("other").value_hint(ValueHint::Other))
+        .arg(
+            Arg::new("path")
+                .long("path")
+                .short('p')
+                .value_hint(ValueHint::AnyPath),
+        )
+        .arg(
+            Arg::new("file")
+                .long("file")
+                .short('f')
+                .value_hint(ValueHint::FilePath),
+        )
+        .arg(
+            Arg::new("dir")
+                .long("dir")
+                .short('d')
+                .value_hint(ValueHint::DirPath),
+        )
+        .arg(
+            Arg::new("exe")
+                .long("exe")
+                .short('e')
+                .value_hint(ValueHint::ExecutablePath),
+        )
+        .arg(
+            Arg::new("cmd_name")
+                .long("cmd-name")
+                .value_hint(ValueHint::CommandName),
+        )
+        .arg(
+            Arg::new("cmd")
+                .long("cmd")
+                .short('c')
+                .value_hint(ValueHint::CommandString),
+        )
+        .arg(
+            Arg::new("command_with_args")
+                .num_args(1..)
+                // AppSettings::TrailingVarArg is required to use ValueHint::CommandWithArguments
+                .trailing_var_arg(true)
+                .value_hint(ValueHint::CommandWithArguments),
+        )
+        .arg(
+            Arg::new("user")
+                .short('u')
+                .long("user")
+                .value_hint(ValueHint::Username),
+        )
+        .arg(
+            Arg::new("host")
+                .long("host")
+                .value_hint(ValueHint::Hostname),
+        )
+        .arg(Arg::new("url").long("url").value_hint(ValueHint::Url))
+        .arg(
+            Arg::new("email")
+                .long("email")
+                .value_hint(ValueHint::EmailAddress),
+        );
+
+    Command::new("completion")
+        .arg(
+            Arg::new("generator")
+                .long("generate")
+                .value_parser(value_parser!(Shell)),
+        )
+        .subcommand(value_hint_command)
+}
+
+fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
+    generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+}
+
+fn main() {
+    let matches = build_cli().get_matches();
+
+    if let Some(generator) = matches.get_one::<Shell>("generator") {
+        let mut cmd = build_cli();
+        eprintln!("Generating completion file for {generator}...");
+        print_completions(*generator, &mut cmd);
+    }
+}
diff --git a/examples/dynamic.rs b/examples/dynamic.rs
new file mode 100644
index 0000000..ccaf7d8
--- /dev/null
+++ b/examples/dynamic.rs
@@ -0,0 +1,37 @@
+use clap::FromArgMatches;
+use clap::Subcommand;
+
+fn command() -> clap::Command {
+    let cmd = clap::Command::new("dynamic")
+        .arg(
+            clap::Arg::new("input")
+                .long("input")
+                .short('i')
+                .value_hint(clap::ValueHint::FilePath),
+        )
+        .arg(
+            clap::Arg::new("format")
+                .long("format")
+                .short('F')
+                .value_parser(["json", "yaml", "toml"]),
+        )
+        .args_conflicts_with_subcommands(true);
+    clap_complete::dynamic::shells::CompleteCommand::augment_subcommands(cmd)
+}
+
+fn main() {
+    let cmd = command();
+    let matches = cmd.get_matches();
+    if let Ok(completions) =
+        clap_complete::dynamic::shells::CompleteCommand::from_arg_matches(&matches)
+    {
+        completions.complete(&mut command());
+    } else {
+        println!("{matches:#?}");
+    }
+}
+
+#[test]
+fn verify_cli() {
+    command().debug_assert();
+}
diff --git a/examples/exhaustive.rs b/examples/exhaustive.rs
new file mode 100644
index 0000000..de00da6
--- /dev/null
+++ b/examples/exhaustive.rs
@@ -0,0 +1,203 @@
+use clap::builder::PossibleValue;
+#[cfg(feature = "unstable-dynamic")]
+use clap::{FromArgMatches, Subcommand};
+use clap_complete::{generate, Generator, Shell};
+
+fn main() {
+    let matches = cli().get_matches();
+    if let Some(generator) = matches.get_one::<Shell>("generate") {
+        let mut cmd = cli();
+        eprintln!("Generating completion file for {generator}...");
+        print_completions(*generator, &mut cmd);
+        return;
+    }
+
+    #[cfg(feature = "unstable-dynamic")]
+    if let Ok(completions) =
+        clap_complete::dynamic::shells::CompleteCommand::from_arg_matches(&matches)
+    {
+        completions.complete(&mut cli());
+        return;
+    };
+
+    println!("{:?}", matches);
+}
+
+fn print_completions<G: Generator>(gen: G, cmd: &mut clap::Command) {
+    generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
+}
+
+#[allow(clippy::let_and_return)]
+fn cli() -> clap::Command {
+    let cli = clap::Command::new("exhaustive")
+        .version("3.0")
+        .propagate_version(true)
+        .args([
+            clap::Arg::new("global")
+                .long("global")
+                .global(true)
+                .action(clap::ArgAction::SetTrue)
+                .help("everywhere"),
+            clap::Arg::new("generate")
+                .long("generate")
+                .value_name("SHELL")
+                .value_parser(clap::value_parser!(Shell))
+                .help("generate"),
+        ])
+        .subcommands([
+            clap::Command::new("action").args([
+                clap::Arg::new("set-true")
+                    .long("set-true")
+                    .action(clap::ArgAction::SetTrue)
+                    .help("bool"),
+                clap::Arg::new("set")
+                    .long("set")
+                    .action(clap::ArgAction::Set)
+                    .help("value"),
+                clap::Arg::new("count")
+                    .long("count")
+                    .action(clap::ArgAction::Count)
+                    .help("number"),
+                clap::Arg::new("choice")
+                    .long("choice")
+                    .value_parser(["first", "second"])
+                    .help("enum"),
+            ]),
+            clap::Command::new("quote")
+                .args([
+                    clap::Arg::new("single-quotes")
+                        .long("single-quotes")
+                        .action(clap::ArgAction::SetTrue)
+                        .help("Can be 'always', 'auto', or 'never'"),
+                    clap::Arg::new("double-quotes")
+                        .long("double-quotes")
+                        .action(clap::ArgAction::SetTrue)
+                        .help("Can be \"always\", \"auto\", or \"never\""),
+                    clap::Arg::new("backticks")
+                        .long("backticks")
+                        .action(clap::ArgAction::SetTrue)
+                        .help("For more information see `echo test`"),
+                    clap::Arg::new("backslash")
+                        .long("backslash")
+                        .action(clap::ArgAction::SetTrue)
+                        .help("Avoid '\\n'"),
+                    clap::Arg::new("brackets")
+                        .long("brackets")
+                        .action(clap::ArgAction::SetTrue)
+                        .help("List packages [filter]"),
+                    clap::Arg::new("expansions")
+                        .long("expansions")
+                        .action(clap::ArgAction::SetTrue)
+                        .help("Execute the shell command with $SHELL"),
+                    clap::Arg::new("choice")
+                        .long("choice")
+                        .action(clap::ArgAction::Set)
+                        .value_parser(clap::builder::PossibleValuesParser::new([
+                            PossibleValue::new("bash").help("bash (shell)"),
+                            PossibleValue::new("fish").help("fish shell"),
+                            PossibleValue::new("zsh").help("zsh shell"),
+                        ])),
+                ])
+                .subcommands([
+                    clap::Command::new("cmd-single-quotes")
+                        .about("Can be 'always', 'auto', or 'never'"),
+                    clap::Command::new("cmd-double-quotes")
+                        .about("Can be \"always\", \"auto\", or \"never\""),
+                    clap::Command::new("cmd-backticks")
+                        .about("For more information see `echo test`"),
+                    clap::Command::new("cmd-backslash").about("Avoid '\\n'"),
+                    clap::Command::new("cmd-brackets").about("List packages [filter]"),
+                    clap::Command::new("cmd-expansions")
+                        .about("Execute the shell command with $SHELL"),
+                    clap::Command::new("escape-help").about("\\tab\t\"'\nNew Line"),
+                ]),
+            clap::Command::new("value").args([
+                clap::Arg::new("delim").long("delim").value_delimiter(','),
+                clap::Arg::new("tuple").long("tuple").num_args(2),
+                clap::Arg::new("require-eq")
+                    .long("require-eq")
+                    .require_equals(true),
+                clap::Arg::new("term").num_args(1..).value_terminator(";"),
+            ]),
+            clap::Command::new("pacman").subcommands([
+                clap::Command::new("one").long_flag("one").short_flag('o'),
+                clap::Command::new("two").long_flag("two").short_flag('t'),
+            ]),
+            clap::Command::new("last")
+                .args([clap::Arg::new("first"), clap::Arg::new("free").last(true)]),
+            clap::Command::new("alias").args([
+                clap::Arg::new("flag")
+                    .short('f')
+                    .visible_short_alias('F')
+                    .long("flag")
+                    .action(clap::ArgAction::SetTrue)
+                    .visible_alias("flg")
+                    .help("cmd flag"),
+                clap::Arg::new("option")
+                    .short('o')
+                    .visible_short_alias('O')
+                    .long("option")
+                    .visible_alias("opt")
+                    .help("cmd option")
+                    .action(clap::ArgAction::Set),
+                clap::Arg::new("positional"),
+            ]),
+            clap::Command::new("hint").args([
+                clap::Arg::new("choice")
+                    .long("choice")
+                    .action(clap::ArgAction::Set)
+                    .value_parser(["bash", "fish", "zsh"]),
+                clap::Arg::new("unknown")
+                    .long("unknown")
+                    .value_hint(clap::ValueHint::Unknown),
+                clap::Arg::new("other")
+                    .long("other")
+                    .value_hint(clap::ValueHint::Other),
+                clap::Arg::new("path")
+                    .long("path")
+                    .short('p')
+                    .value_hint(clap::ValueHint::AnyPath),
+                clap::Arg::new("file")
+                    .long("file")
+                    .short('f')
+                    .value_hint(clap::ValueHint::FilePath),
+                clap::Arg::new("dir")
+                    .long("dir")
+                    .short('d')
+                    .value_hint(clap::ValueHint::DirPath),
+                clap::Arg::new("exe")
+                    .long("exe")
+                    .short('e')
+                    .value_hint(clap::ValueHint::ExecutablePath),
+                clap::Arg::new("cmd_name")
+                    .long("cmd-name")
+                    .value_hint(clap::ValueHint::CommandName),
+                clap::Arg::new("cmd")
+                    .long("cmd")
+                    .short('c')
+                    .value_hint(clap::ValueHint::CommandString),
+                clap::Arg::new("command_with_args")
+                    .action(clap::ArgAction::Set)
+                    .num_args(1..)
+                    .trailing_var_arg(true)
+                    .value_hint(clap::ValueHint::CommandWithArguments),
+                clap::Arg::new("user")
+                    .short('u')
+                    .long("user")
+                    .value_hint(clap::ValueHint::Username),
+                clap::Arg::new("host")
+                    .short('H')
+                    .long("host")
+                    .value_hint(clap::ValueHint::Hostname),
+                clap::Arg::new("url")
+                    .long("url")
+                    .value_hint(clap::ValueHint::Url),
+                clap::Arg::new("email")
+                    .long("email")
+                    .value_hint(clap::ValueHint::EmailAddress),
+            ]),
+        ]);
+    #[cfg(feature = "unstable-dynamic")]
+    let cli = clap_complete::dynamic::shells::CompleteCommand::augment_subcommands(cli);
+    cli
+}
diff --git a/src/dynamic/completer.rs b/src/dynamic/completer.rs
new file mode 100644
index 0000000..8c8cb93
--- /dev/null
+++ b/src/dynamic/completer.rs
@@ -0,0 +1,341 @@
+use std::ffi::OsStr;
+use std::ffi::OsString;
+
+use clap::builder::StyledStr;
+use clap_lex::OsStrExt as _;
+
+/// Shell-specific completions
+pub trait Completer {
+    /// The recommended file name for the registration code
+    fn file_name(&self, name: &str) -> String;
+    /// Register for completions
+    fn write_registration(
+        &self,
+        name: &str,
+        bin: &str,
+        completer: &str,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error>;
+    /// Complete the command
+    fn write_complete(
+        &self,
+        cmd: &mut clap::Command,
+        args: Vec<std::ffi::OsString>,
+        current_dir: Option<&std::path::Path>,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error>;
+}
+
+/// Complete the command specified
+pub fn complete(
+    cmd: &mut clap::Command,
+    args: Vec<std::ffi::OsString>,
+    arg_index: usize,
+    current_dir: Option<&std::path::Path>,
+) -> Result<Vec<(std::ffi::OsString, Option<StyledStr>)>, std::io::Error> {
+    cmd.build();
+
+    let raw_args = clap_lex::RawArgs::new(args);
+    let mut cursor = raw_args.cursor();
+    let mut target_cursor = raw_args.cursor();
+    raw_args.seek(
+        &mut target_cursor,
+        clap_lex::SeekFrom::Start(arg_index as u64),
+    );
+    // As we loop, `cursor` will always be pointing to the next item
+    raw_args.next_os(&mut target_cursor);
+
+    // TODO: Multicall support
+    if !cmd.is_no_binary_name_set() {
+        raw_args.next_os(&mut cursor);
+    }
+
+    let mut current_cmd = &*cmd;
+    let mut pos_index = 1;
+    let mut is_escaped = false;
+    while let Some(arg) = raw_args.next(&mut cursor) {
+        if cursor == target_cursor {
+            return complete_arg(&arg, current_cmd, current_dir, pos_index, is_escaped);
+        }
+
+        debug!("complete::next: Begin parsing '{:?}'", arg.to_value_os(),);
+
+        if let Ok(value) = arg.to_value() {
+            if let Some(next_cmd) = current_cmd.find_subcommand(value) {
+                current_cmd = next_cmd;
+                pos_index = 1;
+                continue;
+            }
+        }
+
+        if is_escaped {
+            pos_index += 1;
+        } else if arg.is_escape() {
+            is_escaped = true;
+        } else if let Some(_long) = arg.to_long() {
+        } else if let Some(_short) = arg.to_short() {
+        } else {
+            pos_index += 1;
+        }
+    }
+
+    Err(std::io::Error::new(
+        std::io::ErrorKind::Other,
+        "no completion generated",
+    ))
+}
+
+fn complete_arg(
+    arg: &clap_lex::ParsedArg<'_>,
+    cmd: &clap::Command,
+    current_dir: Option<&std::path::Path>,
+    pos_index: usize,
+    is_escaped: bool,
+) -> Result<Vec<(std::ffi::OsString, Option<StyledStr>)>, std::io::Error> {
+    debug!(
+        "complete_arg: arg={:?}, cmd={:?}, current_dir={:?}, pos_index={}, is_escaped={}",
+        arg,
+        cmd.get_name(),
+        current_dir,
+        pos_index,
+        is_escaped
+    );
+    let mut completions = Vec::new();
+
+    if !is_escaped {
+        if let Some((flag, value)) = arg.to_long() {
+            if let Ok(flag) = flag {
+                if let Some(value) = value {
+                    if let Some(arg) = cmd.get_arguments().find(|a| a.get_long() == Some(flag)) {
+                        completions.extend(
+                            complete_arg_value(value.to_str().ok_or(value), arg, current_dir)
+                                .into_iter()
+                                .map(|(os, help)| {
+                                    // HACK: Need better `OsStr` manipulation
+                                    (format!("--{}={}", flag, os.to_string_lossy()).into(), help)
+                                }),
+                        )
+                    }
+                } else {
+                    completions.extend(longs_and_visible_aliases(cmd).into_iter().filter_map(
+                        |(f, help)| f.starts_with(flag).then(|| (format!("--{f}").into(), help)),
+                    ));
+                }
+            }
+        } else if arg.is_escape() || arg.is_stdio() || arg.is_empty() {
+            // HACK: Assuming knowledge of is_escape / is_stdio
+            completions.extend(
+                longs_and_visible_aliases(cmd)
+                    .into_iter()
+                    .map(|(f, help)| (format!("--{f}").into(), help)),
+            );
+        }
+
+        if arg.is_empty() || arg.is_stdio() || arg.is_short() {
+            let dash_or_arg = if arg.is_empty() {
+                "-".into()
+            } else {
+                arg.to_value_os().to_string_lossy()
+            };
+            // HACK: Assuming knowledge of is_stdio
+            completions.extend(
+                shorts_and_visible_aliases(cmd)
+                    .into_iter()
+                    // HACK: Need better `OsStr` manipulation
+                    .map(|(f, help)| (format!("{}{}", dash_or_arg, f).into(), help)),
+            );
+        }
+    }
+
+    if let Some(positional) = cmd
+        .get_positionals()
+        .find(|p| p.get_index() == Some(pos_index))
+    {
+        completions.extend(complete_arg_value(arg.to_value(), positional, current_dir));
+    }
+
+    if let Ok(value) = arg.to_value() {
+        completions.extend(complete_subcommand(value, cmd));
+    }
+
+    Ok(completions)
+}
+
+fn complete_arg_value(
+    value: Result<&str, &OsStr>,
+    arg: &clap::Arg,
+    current_dir: Option<&std::path::Path>,
+) -> Vec<(OsString, Option<StyledStr>)> {
+    let mut values = Vec::new();
+    debug!("complete_arg_value: arg={arg:?}, value={value:?}");
+
+    if let Some(possible_values) = possible_values(arg) {
+        if let Ok(value) = value {
+            values.extend(possible_values.into_iter().filter_map(|p| {
+                let name = p.get_name();
+                name.starts_with(value)
+                    .then(|| (name.into(), p.get_help().cloned()))
+            }));
+        }
+    } else {
+        let value_os = match value {
+            Ok(value) => OsStr::new(value),
+            Err(value_os) => value_os,
+        };
+        match arg.get_value_hint() {
+            clap::ValueHint::Other => {
+                // Should not complete
+            }
+            clap::ValueHint::Unknown | clap::ValueHint::AnyPath => {
+                values.extend(complete_path(value_os, current_dir, |_| true));
+            }
+            clap::ValueHint::FilePath => {
+                values.extend(complete_path(value_os, current_dir, |p| p.is_file()));
+            }
+            clap::ValueHint::DirPath => {
+                values.extend(complete_path(value_os, current_dir, |p| p.is_dir()));
+            }
+            clap::ValueHint::ExecutablePath => {
+                use is_executable::IsExecutable;
+                values.extend(complete_path(value_os, current_dir, |p| p.is_executable()));
+            }
+            clap::ValueHint::CommandName
+            | clap::ValueHint::CommandString
+            | clap::ValueHint::CommandWithArguments
+            | clap::ValueHint::Username
+            | clap::ValueHint::Hostname
+            | clap::ValueHint::Url
+            | clap::ValueHint::EmailAddress => {
+                // No completion implementation
+            }
+            _ => {
+                // Safe-ish fallback
+                values.extend(complete_path(value_os, current_dir, |_| true));
+            }
+        }
+        values.sort();
+    }
+
+    values
+}
+
+fn complete_path(
+    value_os: &OsStr,
+    current_dir: Option<&std::path::Path>,
+    is_wanted: impl Fn(&std::path::Path) -> bool,
+) -> Vec<(OsString, Option<StyledStr>)> {
+    let mut completions = Vec::new();
+
+    let current_dir = match current_dir {
+        Some(current_dir) => current_dir,
+        None => {
+            // Can't complete without a `current_dir`
+            return Vec::new();
+        }
+    };
+    let (existing, prefix) = value_os
+        .split_once("\\")
+        .unwrap_or((OsStr::new(""), value_os));
+    let root = current_dir.join(existing);
+    debug!("complete_path: root={root:?}, prefix={prefix:?}");
+    let prefix = prefix.to_string_lossy();
+
+    for entry in std::fs::read_dir(&root)
+        .ok()
+        .into_iter()
+        .flatten()
+        .filter_map(Result::ok)
+    {
+        let raw_file_name = entry.file_name();
+        if !raw_file_name.starts_with(&prefix) {
+            continue;
+        }
+
+        if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
+            let path = entry.path();
+            let mut suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
+            suggestion.push(""); // Ensure trailing `/`
+            completions.push((suggestion.as_os_str().to_owned(), None));
+        } else {
+            let path = entry.path();
+            if is_wanted(&path) {
+                let suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
+                completions.push((suggestion.as_os_str().to_owned(), None));
+            }
+        }
+    }
+
+    completions
+}
+
+fn complete_subcommand(value: &str, cmd: &clap::Command) -> Vec<(OsString, Option<StyledStr>)> {
+    debug!(
+        "complete_subcommand: cmd={:?}, value={:?}",
+        cmd.get_name(),
+        value
+    );
+
+    let mut scs = subcommands(cmd)
+        .into_iter()
+        .filter(|x| x.0.starts_with(value))
+        .map(|x| (OsString::from(&x.0), x.1))
+        .collect::<Vec<_>>();
+    scs.sort();
+    scs.dedup();
+    scs
+}
+
+/// Gets all the long options, their visible aliases and flags of a [`clap::Command`].
+/// Includes `help` and `version` depending on the [`clap::Command`] settings.
+fn longs_and_visible_aliases(p: &clap::Command) -> Vec<(String, Option<StyledStr>)> {
+    debug!("longs: name={}", p.get_name());
+
+    p.get_arguments()
+        .filter_map(|a| {
+            a.get_long_and_visible_aliases().map(|longs| {
+                longs
+                    .into_iter()
+                    .map(|s| (s.to_string(), a.get_help().cloned()))
+            })
+        })
+        .flatten()
+        .collect()
+}
+
+/// Gets all the short options, their visible aliases and flags of a [`clap::Command`].
+/// Includes `h` and `V` depending on the [`clap::Command`] settings.
+fn shorts_and_visible_aliases(p: &clap::Command) -> Vec<(char, Option<StyledStr>)> {
+    debug!("shorts: name={}", p.get_name());
+
+    p.get_arguments()
+        .filter_map(|a| {
+            a.get_short_and_visible_aliases()
+                .map(|shorts| shorts.into_iter().map(|s| (s, a.get_help().cloned())))
+        })
+        .flatten()
+        .collect()
+}
+
+/// Get the possible values for completion
+fn possible_values(a: &clap::Arg) -> Option<Vec<clap::builder::PossibleValue>> {
+    if !a.get_num_args().expect("built").takes_values() {
+        None
+    } else {
+        a.get_value_parser()
+            .possible_values()
+            .map(|pvs| pvs.collect())
+    }
+}
+
+/// Gets subcommands of [`clap::Command`] in the form of `("name", "bin_name")`.
+///
+/// Subcommand `rustup toolchain install` would be converted to
+/// `("install", "rustup toolchain install")`.
+fn subcommands(p: &clap::Command) -> Vec<(String, Option<StyledStr>)> {
+    debug!("subcommands: name={}", p.get_name());
+    debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
+
+    p.get_subcommands()
+        .map(|sc| (sc.get_name().to_string(), sc.get_about().cloned()))
+        .collect()
+}
diff --git a/src/dynamic/mod.rs b/src/dynamic/mod.rs
new file mode 100644
index 0000000..f7c9857
--- /dev/null
+++ b/src/dynamic/mod.rs
@@ -0,0 +1,7 @@
+//! Complete commands within shells
+
+mod completer;
+
+pub mod shells;
+
+pub use completer::*;
diff --git a/src/dynamic/shells/bash.rs b/src/dynamic/shells/bash.rs
new file mode 100644
index 0000000..43c128e
--- /dev/null
+++ b/src/dynamic/shells/bash.rs
@@ -0,0 +1,121 @@
+use unicode_xid::UnicodeXID as _;
+
+/// Bash completions
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Bash;
+
+impl crate::dynamic::Completer for Bash {
+    fn file_name(&self, name: &str) -> String {
+        format!("{name}.bash")
+    }
+    fn write_registration(
+        &self,
+        name: &str,
+        bin: &str,
+        completer: &str,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error> {
+        let escaped_name = name.replace('-', "_");
+        debug_assert!(
+            escaped_name.chars().all(|c| c.is_xid_continue()),
+            "`name` must be an identifier, got `{escaped_name}`"
+        );
+        let mut upper_name = escaped_name.clone();
+        upper_name.make_ascii_uppercase();
+
+        let completer = shlex::quote(completer);
+
+        let script = r#"
+_clap_complete_NAME() {
+    export IFS=$'\013'
+    export _CLAP_COMPLETE_INDEX=${COMP_CWORD}
+    export _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
+    if compopt +o nospace 2> /dev/null; then
+        export _CLAP_COMPLETE_SPACE=false
+    else
+        export _CLAP_COMPLETE_SPACE=true
+    fi
+    COMPREPLY=( $("COMPLETER" complete --shell bash -- "${COMP_WORDS[@]}") )
+    if [[ $? != 0 ]]; then
+        unset COMPREPLY
+    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
+        compopt -o nospace
+    fi
+}
+complete -o nospace -o bashdefault -F _clap_complete_NAME BIN
+"#
+        .replace("NAME", &escaped_name)
+        .replace("BIN", bin)
+        .replace("COMPLETER", &completer)
+        .replace("UPPER", &upper_name);
+
+        writeln!(buf, "{script}")?;
+        Ok(())
+    }
+    fn write_complete(
+        &self,
+        cmd: &mut clap::Command,
+        args: Vec<std::ffi::OsString>,
+        current_dir: Option<&std::path::Path>,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error> {
+        let index: usize = std::env::var("_CLAP_COMPLETE_INDEX")
+            .ok()
+            .and_then(|i| i.parse().ok())
+            .unwrap_or_default();
+        let _comp_type: CompType = std::env::var("_CLAP_COMPLETE_COMP_TYPE")
+            .ok()
+            .and_then(|i| i.parse().ok())
+            .unwrap_or_default();
+        let _space: Option<bool> = std::env::var("_CLAP_COMPLETE_SPACE")
+            .ok()
+            .and_then(|i| i.parse().ok());
+        let ifs: Option<String> = std::env::var("IFS").ok().and_then(|i| i.parse().ok());
+        let completions = crate::dynamic::complete(cmd, args, index, current_dir)?;
+
+        for (i, (completion, _)) in completions.iter().enumerate() {
+            if i != 0 {
+                write!(buf, "{}", ifs.as_deref().unwrap_or("\n"))?;
+            }
+            write!(buf, "{}", completion.to_string_lossy())?;
+        }
+        Ok(())
+    }
+}
+
+/// Type of completion attempted that caused a completion function to be called
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+enum CompType {
+    /// Normal completion
+    Normal,
+    /// List completions after successive tabs
+    Successive,
+    /// List alternatives on partial word completion
+    Alternatives,
+    /// List completions if the word is not unmodified
+    Unmodified,
+    /// Menu completion
+    Menu,
+}
+
+impl std::str::FromStr for CompType {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "9" => Ok(Self::Normal),
+            "63" => Ok(Self::Successive),
+            "33" => Ok(Self::Alternatives),
+            "64" => Ok(Self::Unmodified),
+            "37" => Ok(Self::Menu),
+            _ => Err(format!("unsupported COMP_TYPE `{}`", s)),
+        }
+    }
+}
+
+impl Default for CompType {
+    fn default() -> Self {
+        Self::Normal
+    }
+}
diff --git a/src/dynamic/shells/fish.rs b/src/dynamic/shells/fish.rs
new file mode 100644
index 0000000..9d7e8c6
--- /dev/null
+++ b/src/dynamic/shells/fish.rs
@@ -0,0 +1,46 @@
+/// Fish completions
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Fish;
+
+impl crate::dynamic::Completer for Fish {
+    fn file_name(&self, name: &str) -> String {
+        format!("{name}.fish")
+    }
+    fn write_registration(
+        &self,
+        _name: &str,
+        bin: &str,
+        completer: &str,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error> {
+        let bin = shlex::quote(bin);
+        let completer = shlex::quote(completer);
+        writeln!(
+            buf,
+            r#"complete -x -c {bin} -a "("'{completer}'" complete --shell fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""#
+        )
+    }
+    fn write_complete(
+        &self,
+        cmd: &mut clap::Command,
+        args: Vec<std::ffi::OsString>,
+        current_dir: Option<&std::path::Path>,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error> {
+        let index = args.len() - 1;
+        let completions = crate::dynamic::complete(cmd, args, index, current_dir)?;
+
+        for (completion, help) in completions {
+            write!(buf, "{}", completion.to_string_lossy())?;
+            if let Some(help) = help {
+                write!(
+                    buf,
+                    "\t{}",
+                    help.to_string().lines().next().unwrap_or_default()
+                )?;
+            }
+            writeln!(buf)?;
+        }
+        Ok(())
+    }
+}
diff --git a/src/dynamic/shells/mod.rs b/src/dynamic/shells/mod.rs
new file mode 100644
index 0000000..54d23a3
--- /dev/null
+++ b/src/dynamic/shells/mod.rs
@@ -0,0 +1,82 @@
+//! Shell support
+
+mod bash;
+mod fish;
+mod shell;
+
+pub use bash::*;
+pub use fish::*;
+pub use shell::*;
+
+use std::ffi::OsString;
+use std::io::Write as _;
+
+use crate::dynamic::Completer as _;
+
+#[derive(clap::Subcommand)]
+#[allow(missing_docs)]
+#[derive(Clone, Debug)]
+pub enum CompleteCommand {
+    /// Register shell completions for this program
+    #[command(hide = true)]
+    Complete(CompleteArgs),
+}
+
+#[derive(clap::Args)]
+#[command(arg_required_else_help = true)]
+#[command(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
+#[allow(missing_docs)]
+#[derive(Clone, Debug)]
+pub struct CompleteArgs {
+    /// Specify shell to complete for
+    #[arg(long)]
+    shell: Shell,
+
+    /// Path to write completion-registration to
+    #[arg(long, required = true)]
+    register: Option<std::path::PathBuf>,
+
+    #[arg(raw = true, hide_short_help = true, group = "complete")]
+    comp_words: Vec<OsString>,
+}
+
+impl CompleteCommand {
+    /// Process the completion request
+    pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
+        self.try_complete(cmd).unwrap_or_else(|e| e.exit());
+        std::process::exit(0)
+    }
+
+    /// Process the completion request
+    pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::error::Result<()> {
+        debug!("CompleteCommand::try_complete: {self:?}");
+        let CompleteCommand::Complete(args) = self;
+        if let Some(out_path) = args.register.as_deref() {
+            let mut buf = Vec::new();
+            let name = cmd.get_name();
+            let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
+            args.shell.write_registration(name, bin, bin, &mut buf)?;
+            if out_path == std::path::Path::new("-") {
+                std::io::stdout().write_all(&buf)?;
+            } else if out_path.is_dir() {
+                let out_path = out_path.join(args.shell.file_name(name));
+                std::fs::write(out_path, buf)?;
+            } else {
+                std::fs::write(out_path, buf)?;
+            }
+        } else {
+            let current_dir = std::env::current_dir().ok();
+
+            let mut buf = Vec::new();
+            args.shell.write_complete(
+                cmd,
+                args.comp_words.clone(),
+                current_dir.as_deref(),
+                &mut buf,
+            )?;
+            std::io::stdout().write_all(&buf)?;
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/dynamic/shells/shell.rs b/src/dynamic/shells/shell.rs
new file mode 100644
index 0000000..a9f48ce
--- /dev/null
+++ b/src/dynamic/shells/shell.rs
@@ -0,0 +1,85 @@
+use std::fmt::Display;
+use std::str::FromStr;
+
+use clap::builder::PossibleValue;
+use clap::ValueEnum;
+
+/// Shell with auto-generated completion script available.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum Shell {
+    /// Bourne Again SHell (bash)
+    Bash,
+    /// Friendly Interactive SHell (fish)
+    Fish,
+}
+
+impl Display for Shell {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.to_possible_value()
+            .expect("no values are skipped")
+            .get_name()
+            .fmt(f)
+    }
+}
+
+impl FromStr for Shell {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        for variant in Self::value_variants() {
+            if variant.to_possible_value().unwrap().matches(s, false) {
+                return Ok(*variant);
+            }
+        }
+        Err(format!("invalid variant: {s}"))
+    }
+}
+
+// Hand-rolled so it can work even when `derive` feature is disabled
+impl ValueEnum for Shell {
+    fn value_variants<'a>() -> &'a [Self] {
+        &[Shell::Bash, Shell::Fish]
+    }
+
+    fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
+        Some(match self {
+            Shell::Bash => PossibleValue::new("bash"),
+            Shell::Fish => PossibleValue::new("fish"),
+        })
+    }
+}
+
+impl Shell {
+    fn completer(&self) -> &dyn crate::dynamic::Completer {
+        match self {
+            Self::Bash => &super::Bash,
+            Self::Fish => &super::Fish,
+        }
+    }
+}
+
+impl crate::dynamic::Completer for Shell {
+    fn file_name(&self, name: &str) -> String {
+        self.completer().file_name(name)
+    }
+    fn write_registration(
+        &self,
+        name: &str,
+        bin: &str,
+        completer: &str,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error> {
+        self.completer()
+            .write_registration(name, bin, completer, buf)
+    }
+    fn write_complete(
+        &self,
+        cmd: &mut clap::Command,
+        args: Vec<std::ffi::OsString>,
+        current_dir: Option<&std::path::Path>,
+        buf: &mut dyn std::io::Write,
+    ) -> Result<(), std::io::Error> {
+        self.completer().write_complete(cmd, args, current_dir, buf)
+    }
+}
diff --git a/src/generator/mod.rs b/src/generator/mod.rs
new file mode 100644
index 0000000..a371f68
--- /dev/null
+++ b/src/generator/mod.rs
@@ -0,0 +1,261 @@
+//! Shell completion machinery
+
+pub mod utils;
+
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::Error;
+use std::io::Write;
+use std::path::PathBuf;
+
+use clap::Command;
+
+/// Generator trait which can be used to write generators
+pub trait Generator {
+    /// Returns the file name that is created when this generator is called during compile time.
+    ///
+    /// # Panics
+    ///
+    /// May panic when called outside of the context of [`generate`] or [`generate_to`]
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use std::io::Write;
+    /// # use clap::Command;
+    /// use clap_complete::Generator;
+    ///
+    /// pub struct Fish;
+    ///
+    /// impl Generator for Fish {
+    ///     fn file_name(&self, name: &str) -> String {
+    ///         format!("{name}.fish")
+    ///     }
+    /// #   fn generate(&self, cmd: &Command, buf: &mut dyn Write) {}
+    /// }
+    /// ```
+    fn file_name(&self, name: &str) -> String;
+
+    /// Generates output out of [`clap::Command`](Command).
+    ///
+    /// # Panics
+    ///
+    /// May panic when called outside of the context of [`generate`] or [`generate_to`]
+    ///
+    /// # Examples
+    ///
+    /// The following example generator displays the [`clap::Command`](Command)
+    /// as if it is printed using [`std::println`].
+    ///
+    /// ```
+    /// use std::{io::Write, fmt::write};
+    /// use clap::Command;
+    /// use clap_complete::Generator;
+    ///
+    /// pub struct ClapDebug;
+    ///
+    /// impl Generator for ClapDebug {
+    /// #   fn file_name(&self, name: &str) -> String {
+    /// #       name.into()
+    /// #   }
+    ///     fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
+    ///         write!(buf, "{cmd}").unwrap();
+    ///     }
+    /// }
+    /// ```
+    fn generate(&self, cmd: &Command, buf: &mut dyn Write);
+}
+
+/// Generate a completions file for a specified shell at compile-time.
+///
+/// **NOTE:** to generate the file at compile time you must use a `build.rs` "Build Script" or a
+/// [`cargo-xtask`](https://github.com/matklad/cargo-xtask)
+///
+/// # Examples
+///
+/// The following example generates a bash completion script via a `build.rs` script. In this
+/// simple example, we'll demo a very small application with only a single subcommand and two
+/// args. Real applications could be many multiple levels deep in subcommands, and have tens or
+/// potentially hundreds of arguments.
+///
+/// First, it helps if we separate out our `Command` definition into a separate file. Whether you
+/// do this as a function, or bare Command definition is a matter of personal preference.
+///
+/// ```
+/// // src/cli.rs
+/// # use clap::{Command, Arg, ArgAction};
+/// pub fn build_cli() -> Command {
+///     Command::new("compl")
+///         .about("Tests completions")
+///         .arg(Arg::new("file")
+///             .help("some input file"))
+///         .subcommand(Command::new("test")
+///             .about("tests things")
+///             .arg(Arg::new("case")
+///                 .long("case")
+///                 .action(ArgAction::Set)
+///                 .help("the case to test")))
+/// }
+/// ```
+///
+/// In our regular code, we can simply call this `build_cli()` function, then call
+/// `get_matches()`, or any of the other normal methods directly after. For example:
+///
+/// ```ignore
+/// // src/main.rs
+///
+/// mod cli;
+///
+/// fn main() {
+///     let _m = cli::build_cli().get_matches();
+///
+///     // normal logic continues...
+/// }
+/// ```
+///
+/// Next, we set up our `Cargo.toml` to use a `build.rs` build script.
+///
+/// ```toml
+/// # Cargo.toml
+/// build = "build.rs"
+///
+/// [dependencies]
+/// clap = "*"
+///
+/// [build-dependencies]
+/// clap = "*"
+/// clap_complete = "*"
+/// ```
+///
+/// Next, we place a `build.rs` in our project root.
+///
+/// ```ignore
+/// use clap_complete::{generate_to, shells::Bash};
+/// use std::env;
+/// use std::io::Error;
+///
+/// include!("src/cli.rs");
+///
+/// fn main() -> Result<(), Error> {
+///     let outdir = match env::var_os("OUT_DIR") {
+///         None => return Ok(()),
+///         Some(outdir) => outdir,
+///     };
+///
+///     let mut cmd = build_cli();
+///     let path = generate_to(
+///         Bash,
+///         &mut cmd, // We need to specify what generator to use
+///         "myapp",  // We need to specify the bin name manually
+///         outdir,   // We need to specify where to write to
+///     )?;
+///
+///     println!("cargo:warning=completion file is generated: {path:?}");
+///
+///     Ok(())
+/// }
+/// ```
+///
+/// Now, once we compile there will be a `{bin_name}.bash` file in the directory.
+/// Assuming we compiled with debug mode, it would be somewhere similar to
+/// `<project>/target/debug/build/myapp-<hash>/out/myapp.bash`.
+///
+/// **NOTE:** Please look at the individual [shells][crate::shells]
+/// to see the name of the files generated.
+///
+/// Using [`ValueEnum::value_variants()`][clap::ValueEnum::value_variants] you can easily loop over
+/// all the supported shell variants to generate all the completions at once too.
+///
+/// ```ignore
+/// use clap::ValueEnum;
+/// use clap_complete::{generate_to, Shell};
+/// use std::env;
+/// use std::io::Error;
+///
+/// include!("src/cli.rs");
+///
+/// fn main() -> Result<(), Error> {
+///     let outdir = match env::var_os("OUT_DIR") {
+///         None => return Ok(()),
+///         Some(outdir) => outdir,
+///     };
+///
+///     let mut cmd = build_cli();
+///     for &shell in Shell::value_variants() {
+///         generate_to(shell, &mut cmd, "myapp", outdir)?;
+///     }
+///
+///     Ok(())
+/// }
+/// ```
+pub fn generate_to<G, S, T>(
+    gen: G,
+    cmd: &mut Command,
+    bin_name: S,
+    out_dir: T,
+) -> Result<PathBuf, Error>
+where
+    G: Generator,
+    S: Into<String>,
+    T: Into<OsString>,
+{
+    cmd.set_bin_name(bin_name);
+
+    let out_dir = PathBuf::from(out_dir.into());
+    let file_name = gen.file_name(cmd.get_bin_name().unwrap());
+
+    let path = out_dir.join(file_name);
+    let mut file = File::create(&path)?;
+
+    _generate::<G>(gen, cmd, &mut file);
+    Ok(path)
+}
+
+/// Generate a completions file for a specified shell at runtime.
+///
+/// Until `cargo install` can install extra files like a completion script, this may be
+/// used e.g. in a command that outputs the contents of the completion script, to be
+/// redirected into a file by the user.
+///
+/// # Examples
+///
+/// Assuming a separate `cli.rs` like the [`generate_to` example](generate_to()),
+/// we can let users generate a completion script using a command:
+///
+/// ```ignore
+/// // src/main.rs
+///
+/// mod cli;
+/// use std::io;
+/// use clap_complete::{generate, shells::Bash};
+///
+/// fn main() {
+///     let matches = cli::build_cli().get_matches();
+///
+///     if matches.is_present("generate-bash-completions") {
+///         generate(Bash, &mut cli::build_cli(), "myapp", &mut io::stdout());
+///     }
+///
+///     // normal logic continues...
+/// }
+///
+/// ```
+///
+/// Usage:
+///
+/// ```console
+/// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash
+/// ```
+pub fn generate<G, S>(gen: G, cmd: &mut Command, bin_name: S, buf: &mut dyn Write)
+where
+    G: Generator,
+    S: Into<String>,
+{
+    cmd.set_bin_name(bin_name);
+    _generate::<G>(gen, cmd, buf)
+}
+
+fn _generate<G: Generator>(gen: G, cmd: &mut Command, buf: &mut dyn Write) {
+    cmd.build();
+    gen.generate(cmd, buf)
+}
diff --git a/src/generator/utils.rs b/src/generator/utils.rs
new file mode 100644
index 0000000..ca76d18
--- /dev/null
+++ b/src/generator/utils.rs
@@ -0,0 +1,278 @@
+//! Helpers for writing generators
+
+use clap::{Arg, Command};
+
+/// Gets all subcommands including child subcommands in the form of `("name", "bin_name")`.
+///
+/// Subcommand `rustup toolchain install` would be converted to
+/// `("install", "rustup toolchain install")`.
+pub fn all_subcommands(cmd: &Command) -> Vec<(String, String)> {
+    let mut subcmds: Vec<_> = subcommands(cmd);
+
+    for sc_v in cmd.get_subcommands().map(all_subcommands) {
+        subcmds.extend(sc_v);
+    }
+
+    subcmds
+}
+
+/// Finds the subcommand [`clap::Command`] from the given [`clap::Command`] with the given path.
+///
+/// **NOTE:** `path` should not contain the root `bin_name`.
+pub fn find_subcommand_with_path<'cmd>(p: &'cmd Command, path: Vec<&str>) -> &'cmd Command {
+    let mut cmd = p;
+
+    for sc in path {
+        cmd = cmd.find_subcommand(sc).unwrap();
+    }
+
+    cmd
+}
+
+/// Gets subcommands of [`clap::Command`] in the form of `("name", "bin_name")`.
+///
+/// Subcommand `rustup toolchain install` would be converted to
+/// `("install", "rustup toolchain install")`.
+pub fn subcommands(p: &Command) -> Vec<(String, String)> {
+    debug!("subcommands: name={}", p.get_name());
+    debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
+
+    let mut subcmds = vec![];
+
+    for sc in p.get_subcommands() {
+        let sc_bin_name = sc.get_bin_name().unwrap();
+
+        debug!(
+            "subcommands:iter: name={}, bin_name={}",
+            sc.get_name(),
+            sc_bin_name
+        );
+
+        subcmds.push((sc.get_name().to_string(), sc_bin_name.to_string()));
+    }
+
+    subcmds
+}
+
+/// Gets all the short options, their visible aliases and flags of a [`clap::Command`].
+/// Includes `h` and `V` depending on the [`clap::Command`] settings.
+pub fn shorts_and_visible_aliases(p: &Command) -> Vec<char> {
+    debug!("shorts: name={}", p.get_name());
+
+    p.get_arguments()
+        .filter_map(|a| {
+            if !a.is_positional() {
+                if a.get_visible_short_aliases().is_some() && a.get_short().is_some() {
+                    let mut shorts_and_visible_aliases = a.get_visible_short_aliases().unwrap();
+                    shorts_and_visible_aliases.push(a.get_short().unwrap());
+                    Some(shorts_and_visible_aliases)
+                } else if a.get_visible_short_aliases().is_none() && a.get_short().is_some() {
+                    Some(vec![a.get_short().unwrap()])
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        })
+        .flatten()
+        .collect()
+}
+
+/// Gets all the long options, their visible aliases and flags of a [`clap::Command`].
+/// Includes `help` and `version` depending on the [`clap::Command`] settings.
+pub fn longs_and_visible_aliases(p: &Command) -> Vec<String> {
+    debug!("longs: name={}", p.get_name());
+
+    p.get_arguments()
+        .filter_map(|a| {
+            if !a.is_positional() {
+                if a.get_visible_aliases().is_some() && a.get_long().is_some() {
+                    let mut visible_aliases: Vec<_> = a
+                        .get_visible_aliases()
+                        .unwrap()
+                        .into_iter()
+                        .map(|s| s.to_string())
+                        .collect();
+                    visible_aliases.push(a.get_long().unwrap().to_string());
+                    Some(visible_aliases)
+                } else if a.get_visible_aliases().is_none() && a.get_long().is_some() {
+                    Some(vec![a.get_long().unwrap().to_string()])
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        })
+        .flatten()
+        .collect()
+}
+
+/// Gets all the flags of a [`clap::Command`](Command).
+/// Includes `help` and `version` depending on the [`clap::Command`] settings.
+pub fn flags(p: &Command) -> Vec<Arg> {
+    debug!("flags: name={}", p.get_name());
+    p.get_arguments()
+        .filter(|a| !a.get_num_args().expect("built").takes_values() && !a.is_positional())
+        .cloned()
+        .collect()
+}
+
+/// Get the possible values for completion
+pub fn possible_values(a: &Arg) -> Option<Vec<clap::builder::PossibleValue>> {
+    if !a.get_num_args().expect("built").takes_values() {
+        None
+    } else {
+        a.get_value_parser()
+            .possible_values()
+            .map(|pvs| pvs.collect())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use clap::Arg;
+    use clap::ArgAction;
+
+    fn common_app() -> Command {
+        Command::new("myapp")
+            .subcommand(
+                Command::new("test").subcommand(Command::new("config")).arg(
+                    Arg::new("file")
+                        .short('f')
+                        .short_alias('c')
+                        .visible_short_alias('p')
+                        .long("file")
+                        .action(ArgAction::SetTrue)
+                        .visible_alias("path"),
+                ),
+            )
+            .subcommand(Command::new("hello"))
+            .bin_name("my-cmd")
+    }
+
+    fn built() -> Command {
+        let mut cmd = common_app();
+
+        cmd.build();
+        cmd
+    }
+
+    fn built_with_version() -> Command {
+        let mut cmd = common_app().version("3.0");
+
+        cmd.build();
+        cmd
+    }
+
+    #[test]
+    fn test_subcommands() {
+        let cmd = built_with_version();
+
+        assert_eq!(
+            subcommands(&cmd),
+            vec![
+                ("test".to_string(), "my-cmd test".to_string()),
+                ("hello".to_string(), "my-cmd hello".to_string()),
+                ("help".to_string(), "my-cmd help".to_string()),
+            ]
+        );
+    }
+
+    #[test]
+    fn test_all_subcommands() {
+        let cmd = built_with_version();
+
+        assert_eq!(
+            all_subcommands(&cmd),
+            vec![
+                ("test".to_string(), "my-cmd test".to_string()),
+                ("hello".to_string(), "my-cmd hello".to_string()),
+                ("help".to_string(), "my-cmd help".to_string()),
+                ("config".to_string(), "my-cmd test config".to_string()),
+                ("help".to_string(), "my-cmd test help".to_string()),
+                ("config".to_string(), "my-cmd test help config".to_string()),
+                ("help".to_string(), "my-cmd test help help".to_string()),
+                ("test".to_string(), "my-cmd help test".to_string()),
+                ("hello".to_string(), "my-cmd help hello".to_string()),
+                ("help".to_string(), "my-cmd help help".to_string()),
+                ("config".to_string(), "my-cmd help test config".to_string()),
+            ]
+        );
+    }
+
+    #[test]
+    fn test_find_subcommand_with_path() {
+        let cmd = built_with_version();
+        let sc_app = find_subcommand_with_path(&cmd, "test config".split(' ').collect());
+
+        assert_eq!(sc_app.get_name(), "config");
+    }
+
+    #[test]
+    fn test_flags() {
+        let cmd = built_with_version();
+        let actual_flags = flags(&cmd);
+
+        assert_eq!(actual_flags.len(), 2);
+        assert_eq!(actual_flags[0].get_long(), Some("help"));
+        assert_eq!(actual_flags[1].get_long(), Some("version"));
+
+        let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"]));
+
+        assert_eq!(sc_flags.len(), 2);
+        assert_eq!(sc_flags[0].get_long(), Some("file"));
+        assert_eq!(sc_flags[1].get_long(), Some("help"));
+    }
+
+    #[test]
+    fn test_flag_subcommand() {
+        let cmd = built();
+        let actual_flags = flags(&cmd);
+
+        assert_eq!(actual_flags.len(), 1);
+        assert_eq!(actual_flags[0].get_long(), Some("help"));
+
+        let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"]));
+
+        assert_eq!(sc_flags.len(), 2);
+        assert_eq!(sc_flags[0].get_long(), Some("file"));
+        assert_eq!(sc_flags[1].get_long(), Some("help"));
+    }
+
+    #[test]
+    fn test_shorts() {
+        let cmd = built_with_version();
+        let shorts = shorts_and_visible_aliases(&cmd);
+
+        assert_eq!(shorts.len(), 2);
+        assert_eq!(shorts[0], 'h');
+        assert_eq!(shorts[1], 'V');
+
+        let sc_shorts = shorts_and_visible_aliases(find_subcommand_with_path(&cmd, vec!["test"]));
+
+        assert_eq!(sc_shorts.len(), 3);
+        assert_eq!(sc_shorts[0], 'p');
+        assert_eq!(sc_shorts[1], 'f');
+        assert_eq!(sc_shorts[2], 'h');
+    }
+
+    #[test]
+    fn test_longs() {
+        let cmd = built_with_version();
+        let longs = longs_and_visible_aliases(&cmd);
+
+        assert_eq!(longs.len(), 2);
+        assert_eq!(longs[0], "help");
+        assert_eq!(longs[1], "version");
+
+        let sc_longs = longs_and_visible_aliases(find_subcommand_with_path(&cmd, vec!["test"]));
+
+        assert_eq!(sc_longs.len(), 3);
+        assert_eq!(sc_longs[0], "path");
+        assert_eq!(sc_longs[1], "file");
+        assert_eq!(sc_longs[2], "help");
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..a442882
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,74 @@
+// Copyright â“’ 2015-2018 Kevin B. Knapp
+//
+// `clap_complete` is distributed under the terms of both the MIT license and the Apache License
+// (Version 2.0).
+// See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files in this repository
+// for more information.
+
+#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")]
+#![doc = include_str!("../README.md")]
+#![warn(missing_docs, trivial_casts, unused_allocation, trivial_numeric_casts)]
+#![forbid(unsafe_code)]
+#![allow(clippy::needless_doctest_main)]
+
+//! ## Quick Start
+//!
+//! - For generating at compile-time, see [`generate_to`]
+//! - For generating at runtime, see [`generate`]
+//!
+//! [`Shell`] is a convenience `enum` for an argument value type that implements `Generator`
+//! for each natively-supported shell type.
+//!
+//! ## Example
+//!
+//! ```rust,no_run
+//! use clap::{Command, Arg, ValueHint, value_parser, ArgAction};
+//! use clap_complete::{generate, Generator, Shell};
+//! use std::io;
+//!
+//! fn build_cli() -> Command {
+//!     Command::new("example")
+//!          .arg(Arg::new("file")
+//!              .help("some input file")
+//!                 .value_hint(ValueHint::AnyPath),
+//!         )
+//!        .arg(
+//!            Arg::new("generator")
+//!                .long("generate")
+//!                .action(ArgAction::Set)
+//!                .value_parser(value_parser!(Shell)),
+//!        )
+//! }
+//!
+//! fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
+//!     generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+//! }
+//!
+//! fn main() {
+//!     let matches = build_cli().get_matches();
+//!
+//!     if let Some(generator) = matches.get_one::<Shell>("generator").copied() {
+//!         let mut cmd = build_cli();
+//!         eprintln!("Generating completion file for {generator}...");
+//!         print_completions(generator, &mut cmd);
+//!     }
+//! }
+//! ```
+
+const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
+                                  report at https://github.com/clap-rs/clap/issues";
+
+#[macro_use]
+#[allow(missing_docs)]
+mod macros;
+
+pub mod generator;
+pub mod shells;
+
+pub use generator::generate;
+pub use generator::generate_to;
+pub use generator::Generator;
+pub use shells::Shell;
+
+#[cfg(feature = "unstable-dynamic")]
+pub mod dynamic;
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..bc69794
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,21 @@
+macro_rules! w {
+    ($buf:expr, $to_w:expr) => {
+        match $buf.write_all($to_w) {
+            Ok(..) => (),
+            Err(..) => panic!("Failed to write to generated file"),
+        }
+    };
+}
+
+#[cfg(feature = "debug")]
+macro_rules! debug {
+    ($($arg:tt)*) => {
+        eprint!("[{:>w$}] \t", module_path!(), w = 28);
+        eprintln!($($arg)*)
+    }
+}
+
+#[cfg(not(feature = "debug"))]
+macro_rules! debug {
+    ($($arg:tt)*) => {};
+}
diff --git a/src/shells/bash.rs b/src/shells/bash.rs
new file mode 100644
index 0000000..2a97e1d
--- /dev/null
+++ b/src/shells/bash.rs
@@ -0,0 +1,243 @@
+use std::{fmt::Write as _, io::Write};
+
+use clap::*;
+
+use crate::generator::{utils, Generator};
+
+/// Generate bash completion file
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Bash;
+
+impl Generator for Bash {
+    fn file_name(&self, name: &str) -> String {
+        format!("{name}.bash")
+    }
+
+    fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
+        let bin_name = cmd
+            .get_bin_name()
+            .expect("crate::generate should have set the bin_name");
+
+        w!(
+            buf,
+            format!(
+                "_{name}() {{
+    local i cur prev opts cmd
+    COMPREPLY=()
+    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
+    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
+    cmd=\"\"
+    opts=\"\"
+
+    for i in ${{COMP_WORDS[@]}}
+    do
+        case \"${{cmd}},${{i}}\" in
+            \",$1\")
+                cmd=\"{cmd}\"
+                ;;{subcmds}
+            *)
+                ;;
+        esac
+    done
+
+    case \"${{cmd}}\" in
+        {cmd})
+            opts=\"{name_opts}\"
+            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
+                COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
+                return 0
+            fi
+            case \"${{prev}}\" in{name_opts_details}
+                *)
+                    COMPREPLY=()
+                    ;;
+            esac
+            COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
+            return 0
+            ;;{subcmd_details}
+    esac
+}}
+
+complete -F _{name} -o nosort -o bashdefault -o default {name}
+",
+                name = bin_name,
+                cmd = bin_name.replace('-', "__"),
+                name_opts = all_options_for_path(cmd, bin_name),
+                name_opts_details = option_details_for_path(cmd, bin_name),
+                subcmds = all_subcommands(cmd),
+                subcmd_details = subcommand_details(cmd)
+            )
+            .as_bytes()
+        );
+    }
+}
+
+fn all_subcommands(cmd: &Command) -> String {
+    debug!("all_subcommands");
+
+    fn add_command(
+        parent_fn_name: &str,
+        cmd: &Command,
+        subcmds: &mut Vec<(String, String, String)>,
+    ) {
+        let fn_name = format!(
+            "{parent_fn_name}__{cmd_name}",
+            parent_fn_name = parent_fn_name,
+            cmd_name = cmd.get_name().to_string().replace('-', "__")
+        );
+        subcmds.push((
+            parent_fn_name.to_string(),
+            cmd.get_name().to_string(),
+            fn_name.clone(),
+        ));
+        for alias in cmd.get_visible_aliases() {
+            subcmds.push((
+                parent_fn_name.to_string(),
+                alias.to_string(),
+                fn_name.clone(),
+            ));
+        }
+        for subcmd in cmd.get_subcommands() {
+            add_command(&fn_name, subcmd, subcmds);
+        }
+    }
+    let mut subcmds = vec![];
+    let fn_name = cmd.get_name().replace('-', "__");
+    for subcmd in cmd.get_subcommands() {
+        add_command(&fn_name, subcmd, &mut subcmds);
+    }
+    subcmds.sort();
+
+    let mut cases = vec![String::new()];
+    for (parent_fn_name, name, fn_name) in subcmds {
+        cases.push(format!(
+            "{parent_fn_name},{name})
+                cmd=\"{fn_name}\"
+                ;;",
+        ));
+    }
+
+    cases.join("\n            ")
+}
+
+fn subcommand_details(cmd: &Command) -> String {
+    debug!("subcommand_details");
+
+    let mut subcmd_dets = vec![String::new()];
+    let mut scs = utils::all_subcommands(cmd)
+        .iter()
+        .map(|x| x.1.replace(' ', "__"))
+        .collect::<Vec<_>>();
+
+    scs.sort();
+
+    subcmd_dets.extend(scs.iter().map(|sc| {
+        format!(
+            "{subcmd})
+            opts=\"{sc_opts}\"
+            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
+                COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
+                return 0
+            fi
+            case \"${{prev}}\" in{opts_details}
+                *)
+                    COMPREPLY=()
+                    ;;
+            esac
+            COMPREPLY=( $(compgen -W \"${{opts}}\" -- \"${{cur}}\") )
+            return 0
+            ;;",
+            subcmd = sc.replace('-', "__"),
+            sc_opts = all_options_for_path(cmd, sc),
+            level = sc.split("__").map(|_| 1).sum::<u64>(),
+            opts_details = option_details_for_path(cmd, sc)
+        )
+    }));
+
+    subcmd_dets.join("\n        ")
+}
+
+fn option_details_for_path(cmd: &Command, path: &str) -> String {
+    debug!("option_details_for_path: path={path}");
+
+    let p = utils::find_subcommand_with_path(cmd, path.split("__").skip(1).collect());
+    let mut opts = vec![String::new()];
+
+    for o in p.get_opts() {
+        if let Some(longs) = o.get_long_and_visible_aliases() {
+            opts.extend(longs.iter().map(|long| {
+                format!(
+                    "--{})
+                    COMPREPLY=({})
+                    return 0
+                    ;;",
+                    long,
+                    vals_for(o)
+                )
+            }));
+        }
+
+        if let Some(shorts) = o.get_short_and_visible_aliases() {
+            opts.extend(shorts.iter().map(|short| {
+                format!(
+                    "-{})
+                    COMPREPLY=({})
+                    return 0
+                    ;;",
+                    short,
+                    vals_for(o)
+                )
+            }));
+        }
+    }
+
+    opts.join("\n                ")
+}
+
+fn vals_for(o: &Arg) -> String {
+    debug!("vals_for: o={}", o.get_id());
+
+    if let Some(vals) = crate::generator::utils::possible_values(o) {
+        format!(
+            "$(compgen -W \"{}\" -- \"${{cur}}\")",
+            vals.iter()
+                .filter(|pv| !pv.is_hide_set())
+                .map(|n| n.get_name())
+                .collect::<Vec<_>>()
+                .join(" ")
+        )
+    } else if o.get_value_hint() == ValueHint::Other {
+        String::from("\"${cur}\"")
+    } else {
+        String::from("$(compgen -f \"${cur}\")")
+    }
+}
+
+fn all_options_for_path(cmd: &Command, path: &str) -> String {
+    debug!("all_options_for_path: path={path}");
+
+    let p = utils::find_subcommand_with_path(cmd, path.split("__").skip(1).collect());
+
+    let mut opts = String::new();
+    for short in utils::shorts_and_visible_aliases(p) {
+        write!(&mut opts, "-{short} ").unwrap();
+    }
+    for long in utils::longs_and_visible_aliases(p) {
+        write!(&mut opts, "--{long} ").unwrap();
+    }
+    for pos in p.get_positionals() {
+        if let Some(vals) = utils::possible_values(pos) {
+            for value in vals {
+                write!(&mut opts, "{} ", value.get_name()).unwrap();
+            }
+        } else {
+            write!(&mut opts, "{pos} ").unwrap();
+        }
+    }
+    for (sc, _) in utils::subcommands(p) {
+        write!(&mut opts, "{sc} ").unwrap();
+    }
+    opts.pop();
+
+    opts
+}
diff --git a/src/shells/elvish.rs b/src/shells/elvish.rs
new file mode 100644
index 0000000..48a0f85
--- /dev/null
+++ b/src/shells/elvish.rs
@@ -0,0 +1,136 @@
+use std::io::Write;
+
+use clap::builder::StyledStr;
+use clap::*;
+
+use crate::generator::{utils, Generator};
+use crate::INTERNAL_ERROR_MSG;
+
+/// Generate elvish completion file
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Elvish;
+
+impl Generator for Elvish {
+    fn file_name(&self, name: &str) -> String {
+        format!("{name}.elv")
+    }
+
+    fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
+        let bin_name = cmd
+            .get_bin_name()
+            .expect("crate::generate should have set the bin_name");
+
+        let subcommands_cases = generate_inner(cmd, "");
+
+        let result = format!(
+            r#"
+use builtin;
+use str;
+
+set edit:completion:arg-completer[{bin_name}] = {{|@words|
+    fn spaces {{|n|
+        builtin:repeat $n ' ' | str:join ''
+    }}
+    fn cand {{|text desc|
+        edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
+    }}
+    var command = '{bin_name}'
+    for word $words[1..-1] {{
+        if (str:has-prefix $word '-') {{
+            break
+        }}
+        set command = $command';'$word
+    }}
+    var completions = [{subcommands_cases}
+    ]
+    $completions[$command]
+}}
+"#,
+        );
+
+        w!(buf, result.as_bytes());
+    }
+}
+
+// Escape string inside single quotes
+fn escape_string(string: &str) -> String {
+    string.replace('\'', "''")
+}
+
+fn get_tooltip<T: ToString>(help: Option<&StyledStr>, data: T) -> String {
+    match help {
+        Some(help) => escape_string(&help.to_string()),
+        _ => data.to_string(),
+    }
+}
+
+fn generate_inner(p: &Command, previous_command_name: &str) -> String {
+    debug!("generate_inner");
+
+    let command_name = if previous_command_name.is_empty() {
+        p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()
+    } else {
+        format!("{};{}", previous_command_name, &p.get_name())
+    };
+
+    let mut completions = String::new();
+    let preamble = String::from("\n            cand ");
+
+    for option in p.get_opts() {
+        if let Some(shorts) = option.get_short_and_visible_aliases() {
+            let tooltip = get_tooltip(option.get_help(), shorts[0]);
+            for short in shorts {
+                completions.push_str(&preamble);
+                completions.push_str(format!("-{short} '{tooltip}'").as_str());
+            }
+        }
+
+        if let Some(longs) = option.get_long_and_visible_aliases() {
+            let tooltip = get_tooltip(option.get_help(), longs[0]);
+            for long in longs {
+                completions.push_str(&preamble);
+                completions.push_str(format!("--{long} '{tooltip}'").as_str());
+            }
+        }
+    }
+
+    for flag in utils::flags(p) {
+        if let Some(shorts) = flag.get_short_and_visible_aliases() {
+            let tooltip = get_tooltip(flag.get_help(), shorts[0]);
+            for short in shorts {
+                completions.push_str(&preamble);
+                completions.push_str(format!("-{short} '{tooltip}'").as_str());
+            }
+        }
+
+        if let Some(longs) = flag.get_long_and_visible_aliases() {
+            let tooltip = get_tooltip(flag.get_help(), longs[0]);
+            for long in longs {
+                completions.push_str(&preamble);
+                completions.push_str(format!("--{long} '{tooltip}'").as_str());
+            }
+        }
+    }
+
+    for subcommand in p.get_subcommands() {
+        let data = &subcommand.get_name();
+        let tooltip = get_tooltip(subcommand.get_about(), data);
+
+        completions.push_str(&preamble);
+        completions.push_str(format!("{data} '{tooltip}'").as_str());
+    }
+
+    let mut subcommands_cases = format!(
+        r"
+        &'{}'= {{{}
+        }}",
+        &command_name, completions
+    );
+
+    for subcommand in p.get_subcommands() {
+        let subcommand_subcommands_cases = generate_inner(subcommand, &command_name);
+        subcommands_cases.push_str(&subcommand_subcommands_cases);
+    }
+
+    subcommands_cases
+}
diff --git a/src/shells/fish.rs b/src/shells/fish.rs
new file mode 100644
index 0000000..7dae5b6
--- /dev/null
+++ b/src/shells/fish.rs
@@ -0,0 +1,201 @@
+use std::io::Write;
+
+use clap::*;
+
+use crate::generator::{utils, Generator};
+
+/// Generate fish completion file
+///
+/// Note: The fish generator currently only supports named options (-o/--option), not positional arguments.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Fish;
+
+impl Generator for Fish {
+    fn file_name(&self, name: &str) -> String {
+        format!("{name}.fish")
+    }
+
+    fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
+        let bin_name = cmd
+            .get_bin_name()
+            .expect("crate::generate should have set the bin_name");
+
+        let mut buffer = String::new();
+        gen_fish_inner(bin_name, &[], cmd, &mut buffer);
+        w!(buf, buffer.as_bytes());
+    }
+}
+
+// Escape string inside single quotes
+fn escape_string(string: &str, escape_comma: bool) -> String {
+    let string = string.replace('\\', "\\\\").replace('\'', "\\'");
+    if escape_comma {
+        string.replace(',', "\\,")
+    } else {
+        string
+    }
+}
+
+fn gen_fish_inner(
+    root_command: &str,
+    parent_commands: &[&str],
+    cmd: &Command,
+    buffer: &mut String,
+) {
+    debug!("gen_fish_inner");
+    // example :
+    //
+    // complete
+    //      -c {command}
+    //      -d "{description}"
+    //      -s {short}
+    //      -l {long}
+    //      -a "{possible_arguments}"
+    //      -r # if require parameter
+    //      -f # don't use file completion
+    //      -n "__fish_use_subcommand"               # complete for command "myprog"
+    //      -n "__fish_seen_subcommand_from subcmd1" # complete for command "myprog subcmd1"
+
+    let mut basic_template = format!("complete -c {root_command}");
+
+    if parent_commands.is_empty() {
+        if cmd.has_subcommands() {
+            basic_template.push_str(" -n \"__fish_use_subcommand\"");
+        }
+    } else {
+        basic_template.push_str(
+            format!(
+                " -n \"{}\"",
+                parent_commands
+                    .iter()
+                    .map(|command| format!("__fish_seen_subcommand_from {command}"))
+                    .chain(
+                        cmd.get_subcommands()
+                            .map(|command| format!("not __fish_seen_subcommand_from {command}"))
+                    )
+                    .collect::<Vec<_>>()
+                    .join("; and ")
+            )
+            .as_str(),
+        );
+    }
+
+    debug!("gen_fish_inner: parent_commands={parent_commands:?}");
+
+    for option in cmd.get_opts() {
+        let mut template = basic_template.clone();
+
+        if let Some(shorts) = option.get_short_and_visible_aliases() {
+            for short in shorts {
+                template.push_str(format!(" -s {short}").as_str());
+            }
+        }
+
+        if let Some(longs) = option.get_long_and_visible_aliases() {
+            for long in longs {
+                template.push_str(format!(" -l {}", escape_string(long, false)).as_str());
+            }
+        }
+
+        if let Some(data) = option.get_help() {
+            template
+                .push_str(format!(" -d '{}'", escape_string(&data.to_string(), false)).as_str());
+        }
+
+        template.push_str(value_completion(option).as_str());
+
+        buffer.push_str(template.as_str());
+        buffer.push('\n');
+    }
+
+    for flag in utils::flags(cmd) {
+        let mut template = basic_template.clone();
+
+        if let Some(shorts) = flag.get_short_and_visible_aliases() {
+            for short in shorts {
+                template.push_str(format!(" -s {short}").as_str());
+            }
+        }
+
+        if let Some(longs) = flag.get_long_and_visible_aliases() {
+            for long in longs {
+                template.push_str(format!(" -l {}", escape_string(long, false)).as_str());
+            }
+        }
+
+        if let Some(data) = flag.get_help() {
+            template
+                .push_str(format!(" -d '{}'", escape_string(&data.to_string(), false)).as_str());
+        }
+
+        buffer.push_str(template.as_str());
+        buffer.push('\n');
+    }
+
+    for subcommand in cmd.get_subcommands() {
+        let mut template = basic_template.clone();
+
+        template.push_str(" -f");
+        template.push_str(format!(" -a \"{}\"", &subcommand.get_name()).as_str());
+
+        if let Some(data) = subcommand.get_about() {
+            template.push_str(format!(" -d '{}'", escape_string(&data.to_string(), false)).as_str())
+        }
+
+        buffer.push_str(template.as_str());
+        buffer.push('\n');
+    }
+
+    // generate options of subcommands
+    for subcommand in cmd.get_subcommands() {
+        let mut parent_commands: Vec<_> = parent_commands.into();
+        parent_commands.push(subcommand.get_name());
+        gen_fish_inner(root_command, &parent_commands, subcommand, buffer);
+    }
+}
+
+fn value_completion(option: &Arg) -> String {
+    if !option.get_num_args().expect("built").takes_values() {
+        return "".to_string();
+    }
+
+    if let Some(data) = crate::generator::utils::possible_values(option) {
+        // We return the possible values with their own empty description e.g. {a\t,b\t}
+        // this makes sure that a and b don't get the description of the option or argument
+        format!(
+            " -r -f -a \"{{{}}}\"",
+            data.iter()
+                .filter_map(|value| if value.is_hide_set() {
+                    None
+                } else {
+                    // The help text after \t is wrapped in '' to make sure that the it is taken literally
+                    // and there is no command substitution or variable expansion resulting in unexpected errors
+                    Some(format!(
+                        "{}\t'{}'",
+                        escape_string(value.get_name(), true).as_str(),
+                        escape_string(&value.get_help().unwrap_or_default().to_string(), false)
+                    ))
+                })
+                .collect::<Vec<_>>()
+                .join(",")
+        )
+    } else {
+        // NB! If you change this, please also update the table in `ValueHint` documentation.
+        match option.get_value_hint() {
+            ValueHint::Unknown => " -r",
+            // fish has no built-in support to distinguish these
+            ValueHint::AnyPath | ValueHint::FilePath | ValueHint::ExecutablePath => " -r -F",
+            ValueHint::DirPath => " -r -f -a \"(__fish_complete_directories)\"",
+            // It seems fish has no built-in support for completing command + arguments as
+            // single string (CommandString). Complete just the command name.
+            ValueHint::CommandString | ValueHint::CommandName => {
+                " -r -f -a \"(__fish_complete_command)\""
+            }
+            ValueHint::Username => " -r -f -a \"(__fish_complete_users)\"",
+            ValueHint::Hostname => " -r -f -a \"(__fish_print_hostnames)\"",
+            // Disable completion for others
+            _ => " -r -f",
+        }
+        .to_string()
+    }
+}
diff --git a/src/shells/mod.rs b/src/shells/mod.rs
new file mode 100644
index 0000000..a08aa87
--- /dev/null
+++ b/src/shells/mod.rs
@@ -0,0 +1,15 @@
+//! Shell-specific generators
+
+mod bash;
+mod elvish;
+mod fish;
+mod powershell;
+mod shell;
+mod zsh;
+
+pub use bash::Bash;
+pub use elvish::Elvish;
+pub use fish::Fish;
+pub use powershell::PowerShell;
+pub use shell::Shell;
+pub use zsh::Zsh;
diff --git a/src/shells/powershell.rs b/src/shells/powershell.rs
new file mode 100644
index 0000000..6b09b2e
--- /dev/null
+++ b/src/shells/powershell.rs
@@ -0,0 +1,142 @@
+use std::io::Write;
+
+use clap::builder::StyledStr;
+use clap::*;
+
+use crate::generator::{utils, Generator};
+use crate::INTERNAL_ERROR_MSG;
+
+/// Generate powershell completion file
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct PowerShell;
+
+impl Generator for PowerShell {
+    fn file_name(&self, name: &str) -> String {
+        format!("_{name}.ps1")
+    }
+
+    fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
+        let bin_name = cmd
+            .get_bin_name()
+            .expect("crate::generate should have set the bin_name");
+
+        let subcommands_cases = generate_inner(cmd, "");
+
+        let result = format!(
+            r#"
+using namespace System.Management.Automation
+using namespace System.Management.Automation.Language
+
+Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
+    param($wordToComplete, $commandAst, $cursorPosition)
+
+    $commandElements = $commandAst.CommandElements
+    $command = @(
+        '{bin_name}'
+        for ($i = 1; $i -lt $commandElements.Count; $i++) {{
+            $element = $commandElements[$i]
+            if ($element -isnot [StringConstantExpressionAst] -or
+                $element.StringConstantType -ne [StringConstantType]::BareWord -or
+                $element.Value.StartsWith('-') -or
+                $element.Value -eq $wordToComplete) {{
+                break
+        }}
+        $element.Value
+    }}) -join ';'
+
+    $completions = @(switch ($command) {{{subcommands_cases}
+    }})
+
+    $completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} |
+        Sort-Object -Property ListItemText
+}}
+"#
+        );
+
+        w!(buf, result.as_bytes());
+    }
+}
+
+// Escape string inside single quotes
+fn escape_string(string: &str) -> String {
+    string.replace('\'', "''")
+}
+
+fn get_tooltip<T: ToString>(help: Option<&StyledStr>, data: T) -> String {
+    match help {
+        Some(help) => escape_string(&help.to_string()),
+        _ => data.to_string(),
+    }
+}
+
+fn generate_inner(p: &Command, previous_command_name: &str) -> String {
+    debug!("generate_inner");
+
+    let command_name = if previous_command_name.is_empty() {
+        p.get_bin_name().expect(INTERNAL_ERROR_MSG).to_string()
+    } else {
+        format!("{};{}", previous_command_name, &p.get_name())
+    };
+
+    let mut completions = String::new();
+    let preamble = String::from("\n            [CompletionResult]::new(");
+
+    for option in p.get_opts() {
+        generate_aliases(&mut completions, &preamble, option);
+    }
+
+    for flag in utils::flags(p) {
+        generate_aliases(&mut completions, &preamble, &flag);
+    }
+
+    for subcommand in p.get_subcommands() {
+        let data = &subcommand.get_name();
+        let tooltip = get_tooltip(subcommand.get_about(), data);
+
+        completions.push_str(&preamble);
+        completions.push_str(
+            format!("'{data}', '{data}', [CompletionResultType]::ParameterValue, '{tooltip}')")
+                .as_str(),
+        );
+    }
+
+    let mut subcommands_cases = format!(
+        r"
+        '{}' {{{}
+            break
+        }}",
+        &command_name, completions
+    );
+
+    for subcommand in p.get_subcommands() {
+        let subcommand_subcommands_cases = generate_inner(subcommand, &command_name);
+        subcommands_cases.push_str(&subcommand_subcommands_cases);
+    }
+
+    subcommands_cases
+}
+
+fn generate_aliases(completions: &mut String, preamble: &String, arg: &Arg) {
+    use std::fmt::Write as _;
+
+    if let Some(aliases) = arg.get_short_and_visible_aliases() {
+        let tooltip = get_tooltip(arg.get_help(), aliases[0]);
+        for alias in aliases {
+            let _ = write!(
+                completions,
+                "{preamble}'-{alias}', '{alias}{}', [CompletionResultType]::ParameterName, '{tooltip}')",
+                // make PowerShell realize there is a difference between `-s` and `-S`
+                if alias.is_uppercase() { " " } else { "" },
+            );
+        }
+    }
+    if let Some(aliases) = arg.get_long_and_visible_aliases() {
+        let tooltip = get_tooltip(arg.get_help(), aliases[0]);
+        for alias in aliases {
+            let _ = write!(
+                completions,
+                "{preamble}'--{alias}', '{alias}', [CompletionResultType]::ParameterName, '{tooltip}')"
+            );
+        }
+    }
+}
diff --git a/src/shells/shell.rs b/src/shells/shell.rs
new file mode 100644
index 0000000..52cb2e9
--- /dev/null
+++ b/src/shells/shell.rs
@@ -0,0 +1,155 @@
+use std::fmt::Display;
+use std::path::Path;
+use std::str::FromStr;
+
+use clap::builder::PossibleValue;
+use clap::ValueEnum;
+
+use crate::shells;
+use crate::Generator;
+
+/// Shell with auto-generated completion script available.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum Shell {
+    /// Bourne Again SHell (bash)
+    Bash,
+    /// Elvish shell
+    Elvish,
+    /// Friendly Interactive SHell (fish)
+    Fish,
+    /// PowerShell
+    PowerShell,
+    /// Z SHell (zsh)
+    Zsh,
+}
+
+impl Display for Shell {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.to_possible_value()
+            .expect("no values are skipped")
+            .get_name()
+            .fmt(f)
+    }
+}
+
+impl FromStr for Shell {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        for variant in Self::value_variants() {
+            if variant.to_possible_value().unwrap().matches(s, false) {
+                return Ok(*variant);
+            }
+        }
+        Err(format!("invalid variant: {s}"))
+    }
+}
+
+// Hand-rolled so it can work even when `derive` feature is disabled
+impl ValueEnum for Shell {
+    fn value_variants<'a>() -> &'a [Self] {
+        &[
+            Shell::Bash,
+            Shell::Elvish,
+            Shell::Fish,
+            Shell::PowerShell,
+            Shell::Zsh,
+        ]
+    }
+
+    fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
+        Some(match self {
+            Shell::Bash => PossibleValue::new("bash"),
+            Shell::Elvish => PossibleValue::new("elvish"),
+            Shell::Fish => PossibleValue::new("fish"),
+            Shell::PowerShell => PossibleValue::new("powershell"),
+            Shell::Zsh => PossibleValue::new("zsh"),
+        })
+    }
+}
+
+impl Generator for Shell {
+    fn file_name(&self, name: &str) -> String {
+        match self {
+            Shell::Bash => shells::Bash.file_name(name),
+            Shell::Elvish => shells::Elvish.file_name(name),
+            Shell::Fish => shells::Fish.file_name(name),
+            Shell::PowerShell => shells::PowerShell.file_name(name),
+            Shell::Zsh => shells::Zsh.file_name(name),
+        }
+    }
+
+    fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
+        match self {
+            Shell::Bash => shells::Bash.generate(cmd, buf),
+            Shell::Elvish => shells::Elvish.generate(cmd, buf),
+            Shell::Fish => shells::Fish.generate(cmd, buf),
+            Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
+            Shell::Zsh => shells::Zsh.generate(cmd, buf),
+        }
+    }
+}
+
+impl Shell {
+    /// Parse a shell from a path to the executable for the shell
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use clap_complete::shells::Shell;
+    ///
+    /// assert_eq!(Shell::from_shell_path("/bin/bash"), Some(Shell::Bash));
+    /// assert_eq!(Shell::from_shell_path("/usr/bin/zsh"), Some(Shell::Zsh));
+    /// assert_eq!(Shell::from_shell_path("/opt/my_custom_shell"), None);
+    /// ```
+    pub fn from_shell_path<P: AsRef<Path>>(path: P) -> Option<Shell> {
+        parse_shell_from_path(path.as_ref())
+    }
+
+    /// Determine the user's current shell from the environment
+    ///
+    /// This will read the SHELL environment variable and try to determine which shell is in use
+    /// from that.
+    ///
+    /// If SHELL is not set, then on windows, it will default to powershell, and on
+    /// other OSes it will return `None`.
+    ///
+    /// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell
+    /// types, then return `None`.
+    ///
+    /// # Example:
+    ///
+    /// ```no_run
+    /// # use clap::Command;
+    /// use clap_complete::{generate, shells::Shell};
+    /// # fn build_cli() -> Command {
+    /// #     Command::new("compl")
+    /// # }
+    /// let mut cmd = build_cli();
+    /// generate(Shell::from_env().unwrap_or(Shell::Bash), &mut cmd, "myapp", &mut std::io::stdout());
+    /// ```
+    pub fn from_env() -> Option<Shell> {
+        if let Some(env_shell) = std::env::var_os("SHELL") {
+            Shell::from_shell_path(env_shell)
+        } else if cfg!(windows) {
+            Some(Shell::PowerShell)
+        } else {
+            None
+        }
+    }
+}
+
+// use a separate function to avoid having to monomorphize the entire function due
+// to from_shell_path being generic
+fn parse_shell_from_path(path: &Path) -> Option<Shell> {
+    let name = path.file_stem()?.to_str()?;
+    match name {
+        "bash" => Some(Shell::Bash),
+        "zsh" => Some(Shell::Zsh),
+        "fish" => Some(Shell::Fish),
+        "elvish" => Some(Shell::Elvish),
+        "powershell" | "powershell_ise" => Some(Shell::PowerShell),
+        _ => None,
+    }
+}
diff --git a/src/shells/zsh.rs b/src/shells/zsh.rs
new file mode 100644
index 0000000..65d7af6
--- /dev/null
+++ b/src/shells/zsh.rs
@@ -0,0 +1,691 @@
+use std::io::Write;
+
+use clap::*;
+
+use crate::generator::{utils, Generator};
+use crate::INTERNAL_ERROR_MSG;
+
+/// Generate zsh completion file
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Zsh;
+
+impl Generator for Zsh {
+    fn file_name(&self, name: &str) -> String {
+        format!("_{name}")
+    }
+
+    fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
+        let bin_name = cmd
+            .get_bin_name()
+            .expect("crate::generate should have set the bin_name");
+
+        w!(
+            buf,
+            format!(
+                "#compdef {name}
+
+autoload -U is-at-least
+
+_{name}() {{
+    typeset -A opt_args
+    typeset -a _arguments_options
+    local ret=1
+
+    if is-at-least 5.2; then
+        _arguments_options=(-s -S -C)
+    else
+        _arguments_options=(-s -C)
+    fi
+
+    local context curcontext=\"$curcontext\" state line
+    {initial_args}{subcommands}
+}}
+
+{subcommand_details}
+
+if [ \"$funcstack[1]\" = \"_{name}\" ]; then
+    _{name} \"$@\"
+else
+    compdef _{name} {name}
+fi
+",
+                name = bin_name,
+                initial_args = get_args_of(cmd, None),
+                subcommands = get_subcommands_of(cmd),
+                subcommand_details = subcommand_details(cmd)
+            )
+            .as_bytes()
+        );
+    }
+}
+
+// Displays the commands of a subcommand
+// (( $+functions[_[bin_name_underscore]_commands] )) ||
+// _[bin_name_underscore]_commands() {
+//     local commands; commands=(
+//         '[arg_name]:[arg_help]'
+//     )
+//     _describe -t commands '[bin_name] commands' commands "$@"
+//
+// Where the following variables are present:
+//    [bin_name_underscore]: The full space delineated bin_name, where spaces have been replaced by
+//                           underscore characters
+//    [arg_name]: The name of the subcommand
+//    [arg_help]: The help message of the subcommand
+//    [bin_name]: The full space delineated bin_name
+//
+// Here's a snippet from rustup:
+//
+// (( $+functions[_rustup_commands] )) ||
+// _rustup_commands() {
+//     local commands; commands=(
+//      'show:Show the active and installed toolchains'
+//      'update:Update Rust toolchains'
+//      # ... snip for brevity
+//      'help:Print this message or the help of the given subcommand(s)'
+//     )
+//     _describe -t commands 'rustup commands' commands "$@"
+//
+fn subcommand_details(p: &Command) -> String {
+    debug!("subcommand_details");
+
+    let bin_name = p
+        .get_bin_name()
+        .expect("crate::generate should have set the bin_name");
+
+    let mut ret = vec![];
+
+    // First we do ourself
+    let parent_text = format!(
+        "\
+(( $+functions[_{bin_name_underscore}_commands] )) ||
+_{bin_name_underscore}_commands() {{
+    local commands; commands=({subcommands_and_args})
+    _describe -t commands '{bin_name} commands' commands \"$@\"
+}}",
+        bin_name_underscore = bin_name.replace(' ', "__"),
+        bin_name = bin_name,
+        subcommands_and_args = subcommands_of(p)
+    );
+    ret.push(parent_text);
+
+    // Next we start looping through all the children, grandchildren, etc.
+    let mut all_subcommands = utils::all_subcommands(p);
+
+    all_subcommands.sort();
+    all_subcommands.dedup();
+
+    for (_, ref bin_name) in &all_subcommands {
+        debug!("subcommand_details:iter: bin_name={bin_name}");
+
+        ret.push(format!(
+            "\
+(( $+functions[_{bin_name_underscore}_commands] )) ||
+_{bin_name_underscore}_commands() {{
+    local commands; commands=({subcommands_and_args})
+    _describe -t commands '{bin_name} commands' commands \"$@\"
+}}",
+            bin_name_underscore = bin_name.replace(' ', "__"),
+            bin_name = bin_name,
+            subcommands_and_args =
+                subcommands_of(parser_of(p, bin_name).expect(INTERNAL_ERROR_MSG))
+        ));
+    }
+
+    ret.join("\n")
+}
+
+// Generates subcommand completions in form of
+//
+//         '[arg_name]:[arg_help]'
+//
+// Where:
+//    [arg_name]: the subcommand's name
+//    [arg_help]: the help message of the subcommand
+//
+// A snippet from rustup:
+//         'show:Show the active and installed toolchains'
+//      'update:Update Rust toolchains'
+fn subcommands_of(p: &Command) -> String {
+    debug!("subcommands_of");
+
+    let mut segments = vec![];
+
+    fn add_subcommands(subcommand: &Command, name: &str, ret: &mut Vec<String>) {
+        debug!("add_subcommands");
+
+        let text = format!(
+            "'{name}:{help}' \\",
+            name = name,
+            help = escape_help(&subcommand.get_about().unwrap_or_default().to_string())
+        );
+
+        ret.push(text);
+    }
+
+    // The subcommands
+    for command in p.get_subcommands() {
+        debug!("subcommands_of:iter: subcommand={}", command.get_name());
+
+        add_subcommands(command, command.get_name(), &mut segments);
+
+        for alias in command.get_visible_aliases() {
+            add_subcommands(command, alias, &mut segments);
+        }
+    }
+
+    // Surround the text with newlines for proper formatting.
+    // We need this to prevent weirdly formatted `command=(\n        \n)` sections.
+    // When there are no (sub-)commands.
+    if !segments.is_empty() {
+        segments.insert(0, "".to_string());
+        segments.push("    ".to_string());
+    }
+
+    segments.join("\n")
+}
+
+// Get's the subcommand section of a completion file
+// This looks roughly like:
+//
+// case $state in
+// ([bin_name]_args)
+//     curcontext=\"${curcontext%:*:*}:[name_hyphen]-command-$words[1]:\"
+//     case $line[1] in
+//
+//         ([name])
+//         _arguments -C -s -S \
+//             [subcommand_args]
+//         && ret=0
+//
+//         [RECURSIVE_CALLS]
+//
+//         ;;",
+//
+//         [repeat]
+//
+//     esac
+// ;;
+// esac",
+//
+// Where the following variables are present:
+//    [name] = The subcommand name in the form of "install" for "rustup toolchain install"
+//    [bin_name] = The full space delineated bin_name such as "rustup toolchain install"
+//    [name_hyphen] = The full space delineated bin_name, but replace spaces with hyphens
+//    [repeat] = From the same recursive calls, but for all subcommands
+//    [subcommand_args] = The same as zsh::get_args_of
+fn get_subcommands_of(parent: &Command) -> String {
+    debug!(
+        "get_subcommands_of: Has subcommands...{:?}",
+        parent.has_subcommands()
+    );
+
+    if !parent.has_subcommands() {
+        return String::new();
+    }
+
+    let subcommand_names = utils::subcommands(parent);
+    let mut all_subcommands = vec![];
+
+    for (ref name, ref bin_name) in &subcommand_names {
+        debug!(
+            "get_subcommands_of:iter: parent={}, name={name}, bin_name={bin_name}",
+            parent.get_name(),
+        );
+        let mut segments = vec![format!("({name})")];
+        let subcommand_args = get_args_of(
+            parser_of(parent, bin_name).expect(INTERNAL_ERROR_MSG),
+            Some(parent),
+        );
+
+        if !subcommand_args.is_empty() {
+            segments.push(subcommand_args);
+        }
+
+        // Get the help text of all child subcommands.
+        let children = get_subcommands_of(parser_of(parent, bin_name).expect(INTERNAL_ERROR_MSG));
+
+        if !children.is_empty() {
+            segments.push(children);
+        }
+
+        segments.push(String::from(";;"));
+        all_subcommands.push(segments.join("\n"));
+    }
+
+    let parent_bin_name = parent
+        .get_bin_name()
+        .expect("crate::generate should have set the bin_name");
+
+    format!(
+        "
+    case $state in
+    ({name})
+        words=($line[{pos}] \"${{words[@]}}\")
+        (( CURRENT += 1 ))
+        curcontext=\"${{curcontext%:*:*}}:{name_hyphen}-command-$line[{pos}]:\"
+        case $line[{pos}] in
+            {subcommands}
+        esac
+    ;;
+esac",
+        name = parent.get_name(),
+        name_hyphen = parent_bin_name.replace(' ', "-"),
+        subcommands = all_subcommands.join("\n"),
+        pos = parent.get_positionals().count() + 1
+    )
+}
+
+// Get the Command for a given subcommand tree.
+//
+// Given the bin_name "a b c" and the Command for "a" this returns the "c" Command.
+// Given the bin_name "a b c" and the Command for "b" this returns the "c" Command.
+fn parser_of<'cmd>(parent: &'cmd Command, bin_name: &str) -> Option<&'cmd Command> {
+    debug!("parser_of: p={}, bin_name={}", parent.get_name(), bin_name);
+
+    if bin_name == parent.get_bin_name().unwrap_or_default() {
+        return Some(parent);
+    }
+
+    for subcommand in parent.get_subcommands() {
+        if let Some(ret) = parser_of(subcommand, bin_name) {
+            return Some(ret);
+        }
+    }
+
+    None
+}
+
+// Writes out the args section, which ends up being the flags, opts and positionals, and a jump to
+// another ZSH function if there are subcommands.
+// The structure works like this:
+//    ([conflicting_args]) [multiple] arg [takes_value] [[help]] [: :(possible_values)]
+//       ^-- list '-v -h'    ^--'*'          ^--'+'                   ^-- list 'one two three'
+//
+// An example from the rustup command:
+//
+// _arguments -C -s -S \
+//         '(-h --help --verbose)-v[Enable verbose output]' \
+//         '(-V -v --version --verbose --help)-h[Print help information]' \
+//      # ... snip for brevity
+//         ':: :_rustup_commands' \    # <-- displays subcommands
+//         '*::: :->rustup' \          # <-- displays subcommand args and child subcommands
+//     && ret=0
+//
+// The args used for _arguments are as follows:
+//    -C: modify the $context internal variable
+//    -s: Allow stacking of short args (i.e. -a -b -c => -abc)
+//    -S: Do not complete anything after '--' and treat those as argument values
+fn get_args_of(parent: &Command, p_global: Option<&Command>) -> String {
+    debug!("get_args_of");
+
+    let mut segments = vec![String::from("_arguments \"${_arguments_options[@]}\" \\")];
+    let opts = write_opts_of(parent, p_global);
+    let flags = write_flags_of(parent, p_global);
+    let positionals = write_positionals_of(parent);
+
+    if !opts.is_empty() {
+        segments.push(opts);
+    }
+
+    if !flags.is_empty() {
+        segments.push(flags);
+    }
+
+    if !positionals.is_empty() {
+        segments.push(positionals);
+    }
+
+    if parent.has_subcommands() {
+        let parent_bin_name = parent
+            .get_bin_name()
+            .expect("crate::generate should have set the bin_name");
+        let subcommand_bin_name = format!(
+            "\":: :_{name}_commands\" \\",
+            name = parent_bin_name.replace(' ', "__")
+        );
+        segments.push(subcommand_bin_name);
+
+        let subcommand_text = format!("\"*::: :->{name}\" \\", name = parent.get_name());
+        segments.push(subcommand_text);
+    };
+
+    segments.push(String::from("&& ret=0"));
+    segments.join("\n")
+}
+
+// Uses either `possible_vals` or `value_hint` to give hints about possible argument values
+fn value_completion(arg: &Arg) -> Option<String> {
+    if let Some(values) = crate::generator::utils::possible_values(arg) {
+        if values
+            .iter()
+            .any(|value| !value.is_hide_set() && value.get_help().is_some())
+        {
+            Some(format!(
+                "(({}))",
+                values
+                    .iter()
+                    .filter_map(|value| {
+                        if value.is_hide_set() {
+                            None
+                        } else {
+                            Some(format!(
+                                r#"{name}\:"{tooltip}""#,
+                                name = escape_value(value.get_name()),
+                                tooltip =
+                                    escape_help(&value.get_help().unwrap_or_default().to_string()),
+                            ))
+                        }
+                    })
+                    .collect::<Vec<_>>()
+                    .join("\n")
+            ))
+        } else {
+            Some(format!(
+                "({})",
+                values
+                    .iter()
+                    .filter(|pv| !pv.is_hide_set())
+                    .map(|n| n.get_name())
+                    .collect::<Vec<_>>()
+                    .join(" ")
+            ))
+        }
+    } else {
+        // NB! If you change this, please also update the table in `ValueHint` documentation.
+        Some(
+            match arg.get_value_hint() {
+                ValueHint::Unknown => {
+                    return None;
+                }
+                ValueHint::Other => "( )",
+                ValueHint::AnyPath => "_files",
+                ValueHint::FilePath => "_files",
+                ValueHint::DirPath => "_files -/",
+                ValueHint::ExecutablePath => "_absolute_command_paths",
+                ValueHint::CommandName => "_command_names -e",
+                ValueHint::CommandString => "_cmdstring",
+                ValueHint::CommandWithArguments => "_cmdambivalent",
+                ValueHint::Username => "_users",
+                ValueHint::Hostname => "_hosts",
+                ValueHint::Url => "_urls",
+                ValueHint::EmailAddress => "_email_addresses",
+                _ => {
+                    return None;
+                }
+            }
+            .to_string(),
+        )
+    }
+}
+
+/// Escape help string inside single quotes and brackets
+fn escape_help(string: &str) -> String {
+    string
+        .replace('\\', "\\\\")
+        .replace('\'', "'\\''")
+        .replace('[', "\\[")
+        .replace(']', "\\]")
+        .replace(':', "\\:")
+        .replace('$', "\\$")
+        .replace('`', "\\`")
+}
+
+/// Escape value string inside single quotes and parentheses
+fn escape_value(string: &str) -> String {
+    string
+        .replace('\\', "\\\\")
+        .replace('\'', "'\\''")
+        .replace('[', "\\[")
+        .replace(']', "\\]")
+        .replace(':', "\\:")
+        .replace('$', "\\$")
+        .replace('`', "\\`")
+        .replace('(', "\\(")
+        .replace(')', "\\)")
+        .replace(' ', "\\ ")
+}
+
+fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String {
+    debug!("write_opts_of");
+
+    let mut ret = vec![];
+
+    for o in p.get_opts() {
+        debug!("write_opts_of:iter: o={}", o.get_id());
+
+        let help = escape_help(&o.get_help().unwrap_or_default().to_string());
+        let conflicts = arg_conflicts(p, o, p_global);
+
+        let multiple = if let ArgAction::Count | ArgAction::Append = o.get_action() {
+            "*"
+        } else {
+            ""
+        };
+
+        let vn = match o.get_value_names() {
+            None => " ".to_string(),
+            Some(val) => val[0].to_string(),
+        };
+        let vc = match value_completion(o) {
+            Some(val) => format!(":{vn}:{val}"),
+            None => format!(":{vn}: "),
+        };
+        let vc = vc.repeat(o.get_num_args().expect("built").min_values());
+
+        if let Some(shorts) = o.get_short_and_visible_aliases() {
+            for short in shorts {
+                let s = format!("'{conflicts}{multiple}-{short}+[{help}]{vc}' \\");
+
+                debug!("write_opts_of:iter: Wrote...{}", &*s);
+                ret.push(s);
+            }
+        }
+        if let Some(longs) = o.get_long_and_visible_aliases() {
+            for long in longs {
+                let l = format!("'{conflicts}{multiple}--{long}=[{help}]{vc}' \\");
+
+                debug!("write_opts_of:iter: Wrote...{}", &*l);
+                ret.push(l);
+            }
+        }
+    }
+
+    ret.join("\n")
+}
+
+fn arg_conflicts(cmd: &Command, arg: &Arg, app_global: Option<&Command>) -> String {
+    fn push_conflicts(conflicts: &[&Arg], res: &mut Vec<String>) {
+        for conflict in conflicts {
+            if let Some(s) = conflict.get_short() {
+                res.push(format!("-{s}"));
+            }
+
+            if let Some(l) = conflict.get_long() {
+                res.push(format!("--{l}"));
+            }
+        }
+    }
+
+    let mut res = vec![];
+    match (app_global, arg.is_global_set()) {
+        (Some(x), true) => {
+            let conflicts = x.get_arg_conflicts_with(arg);
+
+            if conflicts.is_empty() {
+                return String::new();
+            }
+
+            push_conflicts(&conflicts, &mut res);
+        }
+        (_, _) => {
+            let conflicts = cmd.get_arg_conflicts_with(arg);
+
+            if conflicts.is_empty() {
+                return String::new();
+            }
+
+            push_conflicts(&conflicts, &mut res);
+        }
+    };
+
+    format!("({})", res.join(" "))
+}
+
+fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String {
+    debug!("write_flags_of;");
+
+    let mut ret = vec![];
+
+    for f in utils::flags(p) {
+        debug!("write_flags_of:iter: f={}", f.get_id());
+
+        let help = escape_help(&f.get_help().unwrap_or_default().to_string());
+        let conflicts = arg_conflicts(p, &f, p_global);
+
+        let multiple = if let ArgAction::Count | ArgAction::Append = f.get_action() {
+            "*"
+        } else {
+            ""
+        };
+
+        if let Some(short) = f.get_short() {
+            let s = format!("'{conflicts}{multiple}-{short}[{help}]' \\");
+
+            debug!("write_flags_of:iter: Wrote...{}", &*s);
+
+            ret.push(s);
+
+            if let Some(short_aliases) = f.get_visible_short_aliases() {
+                for alias in short_aliases {
+                    let s = format!("'{conflicts}{multiple}-{alias}[{help}]' \\",);
+
+                    debug!("write_flags_of:iter: Wrote...{}", &*s);
+
+                    ret.push(s);
+                }
+            }
+        }
+
+        if let Some(long) = f.get_long() {
+            let l = format!("'{conflicts}{multiple}--{long}[{help}]' \\");
+
+            debug!("write_flags_of:iter: Wrote...{}", &*l);
+
+            ret.push(l);
+
+            if let Some(aliases) = f.get_visible_aliases() {
+                for alias in aliases {
+                    let l = format!("'{conflicts}{multiple}--{alias}[{help}]' \\");
+
+                    debug!("write_flags_of:iter: Wrote...{}", &*l);
+
+                    ret.push(l);
+                }
+            }
+        }
+    }
+
+    ret.join("\n")
+}
+
+fn write_positionals_of(p: &Command) -> String {
+    debug!("write_positionals_of;");
+
+    let mut ret = vec![];
+
+    // Completions for commands that end with two Vec arguments require special care.
+    // - You can have two Vec args separated with a custom value terminator.
+    // - You can have two Vec args with the second one set to last (raw sets last)
+    //   which will require a '--' separator to be used before the second argument
+    //   on the command-line.
+    //
+    // We use the '-S' _arguments option to disable completion after '--'. Thus, the
+    // completion for the second argument in scenario (B) does not need to be emitted
+    // because it is implicitly handled by the '-S' option.
+    // We only need to emit the first catch-all.
+    //
+    // Have we already emitted a catch-all multi-valued positional argument
+    // without a custom value terminator?
+    let mut catch_all_emitted = false;
+
+    for arg in p.get_positionals() {
+        debug!("write_positionals_of:iter: arg={}", arg.get_id());
+
+        let num_args = arg.get_num_args().expect("built");
+        let is_multi_valued = num_args.max_values() > 1;
+
+        if catch_all_emitted && (arg.is_last_set() || is_multi_valued) {
+            // This is the final argument and it also takes multiple arguments.
+            // We've already emitted a catch-all positional argument so we don't need
+            // to emit anything for this argument because it is implicitly handled by
+            // the use of the '-S' _arguments option.
+            continue;
+        }
+
+        let cardinality_value;
+        // If we have any subcommands, we'll emit a catch-all argument, so we shouldn't
+        // emit one here.
+        let cardinality = if is_multi_valued && !p.has_subcommands() {
+            match arg.get_value_terminator() {
+                Some(terminator) => {
+                    cardinality_value = format!("*{}:", escape_value(terminator));
+                    cardinality_value.as_str()
+                }
+                None => {
+                    catch_all_emitted = true;
+                    "*:"
+                }
+            }
+        } else if !arg.is_required_set() {
+            ":"
+        } else {
+            ""
+        };
+
+        let a = format!(
+            "'{cardinality}:{name}{help}:{value_completion}' \\",
+            cardinality = cardinality,
+            name = arg.get_id(),
+            help = arg
+                .get_help()
+                .map(|s| s.to_string())
+                .map(|v| " -- ".to_owned() + &v)
+                .unwrap_or_else(|| "".to_owned())
+                .replace('[', "\\[")
+                .replace(']', "\\]")
+                .replace('\'', "'\\''")
+                .replace(':', "\\:"),
+            value_completion = value_completion(arg).unwrap_or_default()
+        );
+
+        debug!("write_positionals_of:iter: Wrote...{a}");
+
+        ret.push(a);
+    }
+
+    ret.join("\n")
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::shells::zsh::{escape_help, escape_value};
+
+    #[test]
+    fn test_escape_value() {
+        let raw_string = "\\ [foo]() `bar https://$PATH";
+        assert_eq!(
+            escape_value(raw_string),
+            "\\\\\\ \\[foo\\]\\(\\)\\ \\`bar\\ https\\://\\$PATH"
+        )
+    }
+
+    #[test]
+    fn test_escape_help() {
+        let raw_string = "\\ [foo]() `bar https://$PATH";
+        assert_eq!(
+            escape_help(raw_string),
+            "\\\\ \\[foo\\]() \\`bar https\\://\\$PATH"
+        )
+    }
+}