{"id":3779,"date":"2025-11-26T12:44:12","date_gmt":"2025-11-26T11:44:12","guid":{"rendered":"https:\/\/www.albertopasca.it\/whiletrue\/?p=3779"},"modified":"2025-11-26T12:44:23","modified_gmt":"2025-11-26T11:44:23","slug":"building-a-custom-autocomplete-function-in-zsh","status":"publish","type":"post","link":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/","title":{"rendered":"Building a Custom Autocomplete Function in ZSH"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>C<\/strong>ustom command-line tooling is one of the most effective ways to <strong>improve your workflow<\/strong>. Whether you&#8217;re automating repetitive tasks, organizing project scripts, or creating a small internal CLI, adding&nbsp;<strong>autocomplete<\/strong>&nbsp;makes your tool feel polished, fast, and user-friendly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this tutorial, we\u2019ll walk step-by-step through how to build a&nbsp;<strong>custom Zsh autocomplete function<\/strong>, using a simple <strong>file-lookup command<\/strong> as the foundation. While the example maps logical keys to file paths, the technique is fully generic and can be applied to anything\u2014API commands, Kubernetes actions, internal scripts, or even entire CLI frameworks.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"222\" src=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png?resize=500%2C222&#038;ssl=1\" alt=\"\" class=\"wp-image-3781\" srcset=\"https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png?w=500&amp;ssl=1 500w, https:\/\/i0.wp.com\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png?resize=300%2C133&amp;ssl=1 300w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/figure>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">&#x2049;&#xfe0f;Why Create a Custom Autocomplete?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Autocomplete dramatically improves usability:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Reduces typing effort<\/li>\n\n\n\n<li>Avoids memorizing long or cryptic options<\/li>\n\n\n\n<li>Prevents mistakes (typos, invalid arguments)<\/li>\n\n\n\n<li>Makes simple shell functions feel like full CLI tools<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">With a small amount of Zsh scripting, you can provide intelligent suggestions for your custom commands.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-vivid-red-color has-alpha-channel-opacity has-vivid-red-background-color has-background\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. The Base Command<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s start with a simple shell function that accepts a keyword as argument and performs an action. In our example, each keyword corresponds to a file path stored in an associative array.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>mytool() {\n    local -A FILES=(\n      &#91;config&#93;=\"\/path\/to\/project\/config.yaml\"\n      &#91;build&#93;=\"\/path\/to\/scripts\/build.sh\"\n      &#91;deploy&#93;=\"\/path\/to\/scripts\/deploy.sh\"\n      &#91;test&#93;=\"\/path\/to\/scripts\/test-suite.sh\"\n    )\n\n    if [&#91; -z \"$1\" &#93;]; then\n        echo \"&#x2757; You must specify a parameter. Available keys:\"\n        for key in \"${(@k)FILES}\"; do\n            echo \" - $key \u2192 ${FILES&#91;$key&#93;}\"\n        done\n        return 1\n    fi\n\n    local KEY=\"$1\"\n\n    if [[ -z \"${FILES&#91;$KEY&#93;}\" ]]; then\n        echo \"&#x2757; Unknown parameter: $KEY\"\n        return 1\n    fi\n\n    local FILE=\"${FILES&#91;$KEY&#93;}\"\n\n    if [&#91; ! -f \"$FILE\" &#93;]; then\n        echo \"&#x2757; The file '${FILE}' does not exist!\"\n        return 1\n    fi\n\n    pbcopy &lt; \"$FILE\"\n    echo \"&#x2705; Success!\"\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">mytool<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">local<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-A<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">FILES=<\/span><span style=\"color: #D8DEE9FF\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">      <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">config<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\">=<\/span><span style=\"color: #88C0D0\">&quot;\/path\/to\/project\/config.yaml&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">      <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">build<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\">=<\/span><span style=\"color: #88C0D0\">&quot;\/path\/to\/scripts\/build.sh&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">      <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">deploy<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\">=<\/span><span style=\"color: #88C0D0\">&quot;\/path\/to\/scripts\/deploy.sh&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">      <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9FF\">test<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\">=<\/span><span style=\"color: #88C0D0\">&quot;\/path\/to\/scripts\/test-suite.sh&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    )<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[&#91;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-z<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9\">$1<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#93;]<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">then<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #88C0D0\">echo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">&#x2757; You must specify a parameter. Available keys:<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">for<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">key<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #81A1C1\">${<\/span><span style=\"color: #A3BE8C\">(<\/span><span style=\"color: #81A1C1\">@<\/span><span style=\"color: #D8DEE9\">k<\/span><span style=\"color: #A3BE8C\">)<\/span><span style=\"color: #D8DEE9\">FILES<\/span><span style=\"color: #81A1C1\">}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">do<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #88C0D0\">echo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\"> - <\/span><span style=\"color: #D8DEE9\">$key<\/span><span style=\"color: #A3BE8C\"> \u2192 <\/span><span style=\"color: #81A1C1\">${<\/span><span style=\"color: #D8DEE9\">FILES<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #A3BE8C\">$key<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #81A1C1\">}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">done<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">fi<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">local<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">KEY<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9\">$1<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[[<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-z<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #81A1C1\">${<\/span><span style=\"color: #D8DEE9\">FILES<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #A3BE8C\">$KEY<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #81A1C1\">}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">]]<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">then<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #88C0D0\">echo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">&#x2757; Unknown parameter: <\/span><span style=\"color: #D8DEE9\">$KEY<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">fi<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">local<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">FILE<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #81A1C1\">${<\/span><span style=\"color: #D8DEE9\">FILES<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #A3BE8C\">$KEY<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #81A1C1\">}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">[&#91;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-f<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9\">$FILE<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#93;]<\/span><span style=\"color: #81A1C1\">;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">then<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #88C0D0\">echo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">&#x2757; The file &#39;<\/span><span style=\"color: #81A1C1\">${<\/span><span style=\"color: #D8DEE9\">FILE<\/span><span style=\"color: #81A1C1\">}<\/span><span style=\"color: #A3BE8C\">&#39; does not exist!<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">fi<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #88C0D0\">pbcopy<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9\">$FILE<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #88C0D0\">echo<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">&#x2705; Success!<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">What this function does:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Defines a mapping of keys \u2192 paths<\/li>\n\n\n\n<li>Validates the user input<\/li>\n\n\n\n<li>Performs a final action (<code><strong>pbcopy<\/strong><\/code>&nbsp;is just an example)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The missing piece is autocomplete.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Creating the Autocomplete Function<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To enable autocomplete, Zsh expects a helper function following a specific naming pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">_&lt;commandname&gt;()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So if your command is&nbsp;<code>mytool<\/code>, the autocomplete handler should be:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>_mytool() {\n    local -a keys\n    keys=(config build deploy test)\n    _arguments -C '1:select an action:('\"${keys&#91;*&#93;}\"')'\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #88C0D0\">_mytool<\/span><span style=\"color: #ECEFF4\">()<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">local<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-a<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">keys<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">keys<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #A3BE8C\">config<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">build<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">deploy<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">test<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #88C0D0\">_arguments<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #A3BE8C\">-C<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">1:select an action:(<\/span><span style=\"color: #ECEFF4\">&#39;&quot;<\/span><span style=\"color: #81A1C1\">${<\/span><span style=\"color: #D8DEE9\">keys<\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #A3BE8C\">*<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #81A1C1\">}<\/span><span style=\"color: #ECEFF4\">&quot;&#39;<\/span><span style=\"color: #A3BE8C\">)<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Key parts explained:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\"><code>local -a keys<\/code><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Declares an array of valid suggestions.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\"><code>keys=(...)<\/code><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Populates the array with valid autocomplete values.<br><strong>These must match<\/strong>&nbsp;the keys defined inside&nbsp;<code>mytool()<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\"><code>_arguments -C '1:...:()'<\/code><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tells Zsh that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>This autocomplete applies to the first argument (<code>1:<\/code>)<\/li>\n\n\n\n<li>The description text appears in tab-completion menus<\/li>\n\n\n\n<li>The list inside parentheses defines the allowed values<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">3. Registering the Autocomplete<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, tell Zsh that autocomplete for&nbsp;<code>mytool<\/code>&nbsp;should be handled by&nbsp;<code>_mytool<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">compdef _mytool mytool<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once this line is sourced, pressing&nbsp;<strong>TAB<\/strong>&nbsp;after typing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">mytool &lt;TAB&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">will display the list of keys.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Installing the Script<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Save the entire script in a file, for example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">~\/.zsh\/mytool-autocomplete.zsh<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then source it from your&nbsp;<code>.zshrc<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">source ~\/.zsh\/mytool-autocomplete.zsh<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Refresh your shell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">source ~\/.zshrc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">&#x1f389; <em>Now your autocomplete is live!<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-vivid-red-color has-alpha-channel-opacity has-vivid-red-background-color has-background\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Making It Generic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This autocomplete pattern can be adapted to any workflow:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">&#x2705; Script runners<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Map keywords to CI scripts, build scripts, or test scripts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">&#x2705; Kubernetes helpers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Autocomplete namespace names, deployments, or contexts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">&#x2705; API clients<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Offer subcommands like&nbsp;<code>list<\/code>,&nbsp;<code>create<\/code>,&nbsp;<code>update<\/code>, etc.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">&#x2705; File shortcuts<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Provide quick access to templates, configuration files, or documentation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can even generate the list dynamically\u2014reading from directories, Git branches, or JSON files.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. Tips for Improving Your CLI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here are some enhancements you might want to add:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Dynamic key generation<\/strong>&nbsp;using filesystem scanning<\/li>\n\n\n\n<li><strong>Multi-argument autocomplete<\/strong><\/li>\n\n\n\n<li><strong>Option flags<\/strong>&nbsp;(<code>--force<\/code>,&nbsp;<code>--verbose<\/code>, &#8230;)<\/li>\n\n\n\n<li><strong>Command help<\/strong>&nbsp;using&nbsp;<code>--help<\/code><\/li>\n\n\n\n<li><strong>Colorized output<\/strong>&nbsp;for clarity<\/li>\n\n\n\n<li><strong>Logging<\/strong>&nbsp;for debugging<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Zsh\u2019s completion system is extremely powerful, and this tutorial only scratches the surface.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-vivid-red-color has-alpha-channel-opacity has-vivid-red-background-color has-background\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">By combining a simple shell function with a custom Zsh completion handler, you can create lightweight but highly ergonomic CLI utilities. These tools help streamline your workflow, reduce errors, and feel much more professional.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Adding autocomplete is not only easy\u2014it&#8217;s one of the highest-impact improvements you can make for any internal script or tooling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re building your own developer utilities, this is a great feature to start with.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Custom command-line tooling is one of the most effective ways to improve your workflow. Whether you&#8217;re automating repetitive tasks, organizing project scripts, or creating a small internal CLI, adding&nbsp;autocomplete&nbsp;makes your tool feel polished, fast, and user-friendly. In this tutorial, we\u2019ll walk step-by-step through how to build a&nbsp;custom Zsh autocomplete function, using a simple file-lookup command <a class=\"read-more\" href=\"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-3779","post","type-post","status-publish","format-standard","hentry","category-generic"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Building a Custom Autocomplete Function in ZSH - while (true) {\u00a0}<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building a Custom Autocomplete Function in ZSH - while (true) {\u00a0}\" \/>\n<meta property=\"og:description\" content=\"Custom command-line tooling is one of the most effective ways to improve your workflow. Whether you&#8217;re automating repetitive tasks, organizing project scripts, or creating a small internal CLI, adding&nbsp;autocomplete&nbsp;makes your tool feel polished, fast, and user-friendly. In this tutorial, we\u2019ll walk step-by-step through how to build a&nbsp;custom Zsh autocomplete function, using a simple file-lookup command Read More\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/\" \/>\n<meta property=\"og:site_name\" content=\"while (true) {\u00a0}\" \/>\n<meta property=\"article:published_time\" content=\"2025-11-26T11:44:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-26T11:44:23+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png\" \/>\n<meta name=\"author\" content=\"Alberto Pasca\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@lupasca\" \/>\n<meta name=\"twitter:site\" content=\"@lupasca\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alberto Pasca\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/\"},\"author\":{\"name\":\"Alberto Pasca\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\"},\"headline\":\"Building a Custom Autocomplete Function in ZSH\",\"datePublished\":\"2025-11-26T11:44:12+00:00\",\"dateModified\":\"2025-11-26T11:44:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/\"},\"wordCount\":557,\"publisher\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\"},\"image\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/Screenshot-2025-11-26-alle-12.08.00.png\",\"articleSection\":[\"generic\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/\",\"url\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/\",\"name\":\"Building a Custom Autocomplete Function in ZSH - while (true) {\u00a0}\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/Screenshot-2025-11-26-alle-12.08.00.png\",\"datePublished\":\"2025-11-26T11:44:12+00:00\",\"dateModified\":\"2025-11-26T11:44:23+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/Screenshot-2025-11-26-alle-12.08.00.png\",\"contentUrl\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/wp-content\\\/uploads\\\/2025\\\/11\\\/Screenshot-2025-11-26-alle-12.08.00.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/building-a-custom-autocomplete-function-in-zsh\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building a Custom Autocomplete Function in ZSH\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#website\",\"url\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/\",\"name\":\"while (true) {\u00a0}\",\"description\":\"notes from a developer, tutorials, how-to, ideas\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.albertopasca.it\\\/whiletrue\\\/#\\\/schema\\\/person\\\/d3cf0df774eb4aac797f246a795e9fac\",\"name\":\"Alberto Pasca\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\",\"caption\":\"Alberto Pasca\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g\"},\"description\":\"Software engineer @ Pirelli &amp; C. S.p.A. with a strong passion for mobile \uf8ff development, security, and connected things.\",\"sameAs\":[\"https:\\\/\\\/www.albertopasca.it\",\"albertopasca\",\"https:\\\/\\\/x.com\\\/lupasca\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Building a Custom Autocomplete Function in ZSH - while (true) {\u00a0}","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/","og_locale":"en_US","og_type":"article","og_title":"Building a Custom Autocomplete Function in ZSH - while (true) {\u00a0}","og_description":"Custom command-line tooling is one of the most effective ways to improve your workflow. Whether you&#8217;re automating repetitive tasks, organizing project scripts, or creating a small internal CLI, adding&nbsp;autocomplete&nbsp;makes your tool feel polished, fast, and user-friendly. In this tutorial, we\u2019ll walk step-by-step through how to build a&nbsp;custom Zsh autocomplete function, using a simple file-lookup command Read More","og_url":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/","og_site_name":"while (true) {\u00a0}","article_published_time":"2025-11-26T11:44:12+00:00","article_modified_time":"2025-11-26T11:44:23+00:00","og_image":[{"url":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png","type":"","width":"","height":""}],"author":"Alberto Pasca","twitter_card":"summary_large_image","twitter_creator":"@lupasca","twitter_site":"@lupasca","twitter_misc":{"Written by":"Alberto Pasca","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#article","isPartOf":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/"},"author":{"name":"Alberto Pasca","@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac"},"headline":"Building a Custom Autocomplete Function in ZSH","datePublished":"2025-11-26T11:44:12+00:00","dateModified":"2025-11-26T11:44:23+00:00","mainEntityOfPage":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/"},"wordCount":557,"publisher":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac"},"image":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#primaryimage"},"thumbnailUrl":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png","articleSection":["generic"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/","url":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/","name":"Building a Custom Autocomplete Function in ZSH - while (true) {\u00a0}","isPartOf":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#primaryimage"},"image":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#primaryimage"},"thumbnailUrl":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png","datePublished":"2025-11-26T11:44:12+00:00","dateModified":"2025-11-26T11:44:23+00:00","breadcrumb":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#primaryimage","url":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png","contentUrl":"https:\/\/www.albertopasca.it\/whiletrue\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-26-alle-12.08.00.png"},{"@type":"BreadcrumbList","@id":"https:\/\/www.albertopasca.it\/whiletrue\/building-a-custom-autocomplete-function-in-zsh\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.albertopasca.it\/whiletrue\/"},{"@type":"ListItem","position":2,"name":"Building a Custom Autocomplete Function in ZSH"}]},{"@type":"WebSite","@id":"https:\/\/www.albertopasca.it\/whiletrue\/#website","url":"https:\/\/www.albertopasca.it\/whiletrue\/","name":"while (true) {\u00a0}","description":"notes from a developer, tutorials, how-to, ideas","publisher":{"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.albertopasca.it\/whiletrue\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.albertopasca.it\/whiletrue\/#\/schema\/person\/d3cf0df774eb4aac797f246a795e9fac","name":"Alberto Pasca","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g","caption":"Alberto Pasca"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/4d8fe43c412d0bb34a71c41f60dce53eff3afc027853cbfd20bee6ddc7e93f35?s=96&d=retro&r=g"},"description":"Software engineer @ Pirelli &amp; C. S.p.A. with a strong passion for mobile \uf8ff development, security, and connected things.","sameAs":["https:\/\/www.albertopasca.it","albertopasca","https:\/\/x.com\/lupasca"]}]}},"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts\/3779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/comments?post=3779"}],"version-history":[{"count":8,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts\/3779\/revisions"}],"predecessor-version":[{"id":3790,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/posts\/3779\/revisions\/3790"}],"wp:attachment":[{"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/media?parent=3779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/categories?post=3779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.albertopasca.it\/whiletrue\/wp-json\/wp\/v2\/tags?post=3779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}