From 09b9d8231e8ec382c8c07dbce70cff3beb1c385c Mon Sep 17 00:00:00 2001 From: Logicer Date: Wed, 15 May 2024 22:07:35 +1000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=BA=20Add=20zsh=20completions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Homebrew/mas-tap.rb | 1 + Homebrew/mas.rb | 1 + contrib/completion/mas-completion.zsh | 164 ++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 contrib/completion/mas-completion.zsh diff --git a/Homebrew/mas-tap.rb b/Homebrew/mas-tap.rb index 664ca9d6..b26febc0 100644 --- a/Homebrew/mas-tap.rb +++ b/Homebrew/mas-tap.rb @@ -33,6 +33,7 @@ def install bash_completion.install "contrib/completion/mas-completion.bash" => "mas" fish_completion.install "contrib/completion/mas.fish" + zsh_completion.install "contrib/completion/mas-completion.zsh" => "_mas" end test do diff --git a/Homebrew/mas.rb b/Homebrew/mas.rb index 7836b2c8..ab8fb21e 100644 --- a/Homebrew/mas.rb +++ b/Homebrew/mas.rb @@ -28,6 +28,7 @@ def install bash_completion.install "contrib/completion/mas-completion.bash" => "mas" fish_completion.install "contrib/completion/mas.fish" + zsh_completion.install "contrib/completion/mas-completion.zsh" => "_mas" end test do diff --git a/contrib/completion/mas-completion.zsh b/contrib/completion/mas-completion.zsh new file mode 100644 index 00000000..d586835c --- /dev/null +++ b/contrib/completion/mas-completion.zsh @@ -0,0 +1,164 @@ +#compdef mas +_mas() { + local ret=1 + + _arguments -C \ + '1: :__mas_subcommands' \ + '*::arg:->args' && ret=0 + + case "$state" in + args) + case ${words[1]} in + help) + _arguments -C \ + '1:: :__mas_subcommands' \ + && ret=0 + ;; + home|info|open|vendor) + _arguments -C \ + '1:availableIDs:__mas_list_available_ids' \ + && ret=0 + ;; + install) + _arguments -C \ + '--force[Force reinstall]' \ + '*:availableIDs:__mas_list_available_ids' \ + && ret=0 + ;; + purchase) + _arguments -C \ + '*:availableIDs:__mas_list_available_ids' \ + && ret=0 + ;; + lucky) + _arguments -C \ + '--force[Force reinstall]' \ + '1:name:__mas_list_available' \ + && ret=0 + ;; + reset) + _arguments -C \ + '--debug[Enable debug mode]' \ + && ret=0 + ;; + search) + _arguments -C \ + '--price[Show price of found apps]' \ + '1:name:__mas_list_available' \ + && ret=0 + ;; + signin) + _arguments -C \ + '--dialog[Complete login with graphical dialog]' \ + '1:AppleID:' \ + '2::password:' \ + && ret=0 + ;; + uninstall) + _arguments -C \ + '--dry-run[Dry run]' \ + '1:installedIDs:__mas_list_installed_ids' \ + && ret=0 + ;; + upgrade) + _arguments -C \ + '*:app:__mas_list_outdated' \ + && ret=0 + ;; + account|list|outdated|signout|version) + ret=0 + ;; + *) + (( ret )) && _message 'No more arguments' + ;; + esac + ;; + esac + + return $ret +} + +__mas_subcommands() { + local -a commands=( + 'account:Prints the primary account Apple ID' + 'help:Display general or command-specific help' + 'home:Opens MAS Preview app page in a browser' + 'info:Display app information from the Mac App Store' + 'install:Install from the Mac App Store' + 'list:Lists apps from the Mac App Store which are currently installed' + 'lucky:Install the first result from the Mac App Store' + 'open:Opens app page in AppStore.app' + 'outdated:Lists pending updates from the Mac App Store' + 'purchase:Purchase and download free apps from the Mac App Store' + 'reset:Resets the Mac App Store' + 'search:Search for apps from the Mac App Store' + 'signin:Sign in to the Mac App Store' + 'signout:Sign out of the Mac App Store' + 'uninstall:Uninstall app installed from the Mac App Store' + 'upgrade:Upgrade outdated apps from the Mac App Store' + "vendor:Opens vendor's app page in a browser" + 'version:Print version number' + ) + + _describe -t commands 'command' commands "$@" +} + +__mas_list_available_ids() { + _alternative \ + 'searchedIDs:apps:__mas_search_ids' \ + 'installedIDs:apps:__mas_list_installed_ids' +} + +__mas_list_available() { + _alternative \ + 'ids:apps:__mas_list_available_ids' \ + 'searchedNames:apps:__mas_search_names' \ + 'installedNames:apps:__mas_list_installed_names' +} + +__mas_search_ids() { + [[ -z "$words[-1]" ]] && return + + local -a searchIDs=(${(f)"$(__mas_filter_ids "$(mas search $words[-1] 2>/dev/null)")"}) + _describe -t searchIDs 'search' searchIDs "$@" +} + +__mas_search_names() { + [[ -z "$words[-1]" ]] && return + + local -a searchNames=(${(f)"$(__mas_filter_names "$(mas search $words[-1] 2>/dev/null)")"}) + _describe -t searchNames 'search' searchNames "$@" +} + +__mas_list_outdated() { + local -a outdated=(${(f)"$(__mas_filter_ids "$(mas outdated 2>/dev/null)")"} ${(f)"$(__mas_filter_names "$(mas outdated 2>/dev/null)")"}) + _describe -t outdated 'outdated' outdated "$@" +} + +__mas_list_installed_ids() { + local -a installedIDs=(${(f)"$(__mas_filter_ids "$(mas list 2>/dev/null)")"}) + _describe -t installedIDs 'installed' installedIDs "$@" +} + +__mas_list_installed_names() { + local -a installedNames=(${(f)"$(__mas_filter_names "$(mas list 2>/dev/null)")"}) + _describe -t installedNames 'installed' installedNames "$@" +} + +__mas_filter_names() { + __mas_strip_price "$1" | sed -nEe 's/^[[:space:]]*[0-9\-]+[[:space:]]+//pg' +} + +__mas_filter_ids() { + __mas_strip_price "$1" | sed -nEe 's/^[[:space:]]*([0-9\-]+)[[:space:]]*(.*)/\1:\2/pg' +} + +__mas_strip_price() { + echo "$1" | sed -nEe 's/[[:space:]]+\(.*\).*$//pg' | sed 's/:/\\:/g' +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + _mas "$@" +else + compdef _mas 'mas' +fi From a82b472a80c257707ad60c1b026e8ce79b8ce324 Mon Sep 17 00:00:00 2001 From: Logicer Date: Tue, 24 Sep 2024 23:23:25 +1000 Subject: [PATCH 2/2] Improve zsh completions --- contrib/completion/mas-completion.zsh | 76 +++++++++++++++------------ 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/contrib/completion/mas-completion.zsh b/contrib/completion/mas-completion.zsh index d586835c..e2a22391 100644 --- a/contrib/completion/mas-completion.zsh +++ b/contrib/completion/mas-completion.zsh @@ -16,24 +16,24 @@ _mas() { ;; home|info|open|vendor) _arguments -C \ - '1:availableIDs:__mas_list_available_ids' \ + '1:availableIDs:__mas_list_installed_ids' \ && ret=0 ;; install) _arguments -C \ '--force[Force reinstall]' \ - '*:availableIDs:__mas_list_available_ids' \ + '*:availableIDs:' \ && ret=0 ;; purchase) _arguments -C \ - '*:availableIDs:__mas_list_available_ids' \ + '*:availableIDs:' \ && ret=0 ;; lucky) _arguments -C \ '--force[Force reinstall]' \ - '1:name:__mas_list_available' \ + '1:name:__mas_list_available_names' \ && ret=0 ;; reset) @@ -44,7 +44,7 @@ _mas() { search) _arguments -C \ '--price[Show price of found apps]' \ - '1:name:__mas_list_available' \ + '1:name:__mas_list_available_names' \ && ret=0 ;; signin) @@ -103,58 +103,66 @@ __mas_subcommands() { _describe -t commands 'command' commands "$@" } -__mas_list_available_ids() { +__mas_list_available_names() { _alternative \ - 'searchedIDs:apps:__mas_search_ids' \ - 'installedIDs:apps:__mas_list_installed_ids' -} - -__mas_list_available() { - _alternative \ - 'ids:apps:__mas_list_available_ids' \ 'searchedNames:apps:__mas_search_names' \ 'installedNames:apps:__mas_list_installed_names' } -__mas_search_ids() { - [[ -z "$words[-1]" ]] && return - - local -a searchIDs=(${(f)"$(__mas_filter_ids "$(mas search $words[-1] 2>/dev/null)")"}) - _describe -t searchIDs 'search' searchIDs "$@" -} - +# Autocomplete for the names returned by searching for the current word __mas_search_names() { - [[ -z "$words[-1]" ]] && return + # Don't search if no query has been entered + [[ -z "${(Q)words[-1]}" ]] && return - local -a searchNames=(${(f)"$(__mas_filter_names "$(mas search $words[-1] 2>/dev/null)")"}) + local -a searchNames=("${(f)"$(__mas_filter_names "$(mas search ${(Q)words[-1]} 2>/dev/null)")"}") _describe -t searchNames 'search' searchNames "$@" } -__mas_list_outdated() { - local -a outdated=(${(f)"$(__mas_filter_ids "$(mas outdated 2>/dev/null)")"} ${(f)"$(__mas_filter_names "$(mas outdated 2>/dev/null)")"}) - _describe -t outdated 'outdated' outdated "$@" +# Autocomplete for the names of installed apps +__mas_list_installed_names() { + local -a installedNames=("${(f)"$(__mas_filter_names "$(mas list 2>/dev/null)")"}") + _describe -t installedNames 'installed' installedNames "$@" } +# Autocomplete for the ids of installed apps __mas_list_installed_ids() { - local -a installedIDs=(${(f)"$(__mas_filter_ids "$(mas list 2>/dev/null)")"}) + local -A installedApps=("${(f)"$(__mas_filter_descriptive_ids "$(mas list 2>/dev/null)")"}") + local -a installedIDs=("${(f)"$(printf '%s:%s\n' "${(@kv)installedApps}")"}") _describe -t installedIDs 'installed' installedIDs "$@" } -__mas_list_installed_names() { - local -a installedNames=(${(f)"$(__mas_filter_names "$(mas list 2>/dev/null)")"}) - _describe -t installedNames 'installed' installedNames "$@" +# Autocomplete for the ids or names of installed apps +__mas_list_outdated() { + local -A unfilteredOutdated=( + "${(f)"$(__mas_filter_descriptive_ids "$(mas outdated 2>/dev/null)")"}" + ) + + # Exclude apps which have already been stated + local -a previousApps=(${(Q@)words:1}) + local -a outdated=() + + for id name in ${(kv)unfilteredOutdated}; do + local -a searchValues=( "$id" "$name" ) + [[ ${#previousApps:*searchValues} == 0 ]] && outdated+=( "$id:$name" "$name" ) + done + + _describe -t outdated 'outdated' outdated "$@" } +# Extract app names __mas_filter_names() { - __mas_strip_price "$1" | sed -nEe 's/^[[:space:]]*[0-9\-]+[[:space:]]+//pg' + local -A apps=("${(f)$(__mas_filter_descriptive_ids "$1")}") + printf "${(F)apps}" } -__mas_filter_ids() { - __mas_strip_price "$1" | sed -nEe 's/^[[:space:]]*([0-9\-]+)[[:space:]]*(.*)/\1:\2/pg' +# Extract app ids as an alternating, new-line seperated list of id and name, designed to be used to create an associative array. +__mas_filter_descriptive_ids() { + printf ${(QF)${(*fqqq)"$(__mas_isolate_id_and_name "$1")"}/ ##/\\n} } -__mas_strip_price() { - echo "$1" | sed -nEe 's/[[:space:]]+\(.*\).*$//pg' | sed 's/:/\\:/g' +# Remove leading spaces and the trailing version from the list of apps returned by mas +__mas_isolate_id_and_name() { + printf ${(*F)${${(*@)${(f)1}%% ##\([^(]##}//:/\\:}## ##} } if [[ $zsh_eval_context[-1] == loadautofunc ]]; then