<%
# To make changes to the completions:
#
# - For changes to a command under `COMMANDS` or `DEVELOPER COMMANDS` sections):
#   - Find the source file in `Library/Homebrew/[dev-]cmd/<command>.{rb,sh}`.
#   - For `.rb` files, edit the `cmd_args` block. For subcommand metadata,
#     edit `Library/Homebrew/<command>/subcommand.rb` and
#     `Library/Homebrew/<command>/subcommand/*.rb` if they exist.
#   - For `.sh` files, edit the top comment, being sure to use the line prefix
#     `#:` for the comments to be recognized as documentation. If in doubt,
#     compare with already documented commands.
# - For other changes: Edit this file.
#
# When done, regenerate the completions by running `brew generate-man-completions`.
%>
# Fish shell completions for Homebrew
# This file is automatically generated by running `brew generate-man-completions`.
# See Library/Homebrew/completions/fish.erb for editing instructions.

# A note about aliases:
#
# * When defining completions for the (sub)commands themselves, only the full names are used, as they
#   are more descriptive and worth completing. Aliases are usually shorter than the full names, and
#   exist exactly to save time for users who already know what they want and are going to type the
#   command anyway (i.e. without completion).
# * Nevertheless, it's important to support aliases in the completions for their arguments/options.

##########################
## COMMAND LINE PARSING ##
##########################

function __fish_brew_args -d "Returns a list of all arguments given to brew"

    set -l tokens (commandline -opc)
    set -e tokens[1] # remove 'brew'
    for t in $tokens
        echo $t
    end
end

function __fish_brew_expand_alias -a cmd -d "Expands a Homebrew command alias"
    switch $cmd
<%= aliases.join("\n") + "\n" %>
        case '*'
            echo $cmd
    end
end

function __fish_brew_opts -d "Only arguments starting with a dash (options)"
    string match --all -- '-*' (__fish_brew_args)
end

# This can be used either to get the first argument or to match it against a given list of commands
#
# Usage examples (for `completion -n '...'`):
# * `__fish_brew_command` returns the command (first arg of brew) or exits with 1
# * `not __fish_brew_command` returns true when brew doesn't have a command yet
# * `__fish_brew_command list ls` returns true when brew command is _either_ `list` _or_ `ls`
#
function __fish_brew_command -d "Helps matching the first argument of brew"
    set args (__fish_brew_args)
    set -q args[1]; or return 1
    set -l cmd (__fish_brew_expand_alias $args[1])

    if count $argv
        contains -- $cmd $argv
    else
        echo $cmd
    end
end

function __fish_brew_subcommand -a cmd -d "Helps matching the second argument of brew"
    set args (__fish_brew_args)

    __fish_brew_command $cmd
    and set -q args[2]
    and set -l sub $args[2]
    or return 1

    set -e argv[1]
    if count $argv
        contains -- $sub $argv
    else
        echo $sub
    end
end

# This can be used to match any given option against the given list of arguments:
# * to add condition on interdependent options
# * to add condition on mutually exclusive options
#
# Usage examples (for `completion -n '...'`):
# * `__fish_brew_opt -s --long` returns true if _either_ `-s` _or_ `--long` is present
# * `not __fish_brew_opt --foo --bar` will work only if _neither_ `--foo` _nor_ `--bar` are present
#
function __fish_brew_opt -d "Helps matching brew options against the given list"

    not count $argv
    or contains -- $argv[1] (__fish_brew_opts)
    or begin
        set -q argv[2]
        and __fish_brew_opt $argv[2..-1]
    end
end


######################
## SUGGESTION LISTS ##
######################
# These functions return lists of suggestions for arguments completion

function __fish_brew_suggest_formulae_all -d 'Lists all available formulae with their descriptions'
    HOMEBREW_COMPLETION=1 brew formulae
end

function __fish_brew_suggest_formulae_installed
    command ls -1 (brew --cellar)
end


function __fish_brew_suggest_formulae_outdated -d "List of outdated formulae with the information about potential upgrade"
    HOMEBREW_NO_AUTO_UPDATE=1 brew outdated --formula --verbose 2>/dev/null \
        # replace first space with tab to make the following a description in the completions list:
        | string replace -r '\s' '\t'
end

function __fish_brew_suggest_formula_options -a formula -d "List installation options for a given formula"
    function list_pairs
        set -q argv[2]; or return 0
        echo $argv[1]\t$argv[2]
        set -e argv[1..2]
        list_pairs $argv
    end

    # brew options lists options name and its description on different lines
    list_pairs (brew options $formula | string trim)
end

function __fish_brew_suggest_casks_all -d "Lists locally available casks"
    HOMEBREW_COMPLETION=1 brew casks
end

function __fish_brew_suggest_casks_installed -d "Lists installed casks"
    command ls -1 (brew --caskroom) 2>/dev/null
end

function __fish_brew_suggest_casks_outdated -d "Lists outdated casks with the information about potential upgrade"
    HOMEBREW_NO_AUTO_UPDATE=1 brew outdated --cask --verbose 2>/dev/null \
        # replace first space with tab to make the following a description in the completions list:
        | string replace -r '\s' '\t'
end

function __fish_brew_suggest_taps_installed -d "List all available taps"
    command find (brew --repo)/Library/Taps -mindepth 2 -maxdepth 2 -type d \
    | string replace homebrew- "" \
    | string replace (brew --repo)/Library/Taps/ ""
end

function __fish_brew_suggest_aliases -d "Lists user-defined aliases"
    set -l aliases_dir "$HOME/.config/brew-aliases"
    test -d $aliases_dir; or set aliases_dir "$HOME/.brew-aliases"
    test -d $aliases_dir; or return

    for file in $aliases_dir/*
        test -f $file; and not string match -q '*~' -- $file; or continue
        set -l alias_name (string replace -r '^.*/' '' -- $file)
        begin
            read -l line
            if read -l line
                set -l match (string match -rg 'alias: brew ([^[:space:]]+)' -- $line)
                test -n "$match"; and set alias_name $match[1]
            end
        end < $file
        echo $alias_name
    end
end

function __fish_brew_suggest_commands -d "Lists all command names"
    set -l commands
    if test -f (brew --cache)/all_commands_list.txt
        set commands (cat (brew --cache)/all_commands_list.txt)
    else
        set commands (cat (brew --repo)/completions/internal_commands_list.txt)
    end
    for command in $commands
        set -l expanded_command (__fish_brew_expand_alias $command)
        test "$expanded_command" = "$command"; and echo $command
    end
    __fish_brew_suggest_aliases
end

function __fish_brew_suggest_diagnostic_checks -d "List available diagnostic checks"
    brew doctor --list-checks
end

function __fish_brew_suggest_services -d "Lists available services"
    command find (brew --cellar) -mindepth 3 -maxdepth 3 -name '*.service' \
      | awk -F'homebrew.|.service' '{print $3}' \
      | sort -d
end


##########################
## COMPLETION SHORTCUTS ##
##########################

function __fish_brew_complete_cmd -a cmd -d "A shortcut for defining brew commands completions"
    set -e argv[1]
    complete -f -c brew -n 'not __fish_brew_command' -a $cmd -d $argv
end

function __fish_brew_complete_arg -a cond -d "A shortcut for defining arguments completion for brew commands"
    set -e argv[1]
    # NOTE: $cond can be just a name of a command (or several) or additionally any other condition
    complete -f -c brew -n "__fish_brew_command $cond" $argv
end

function __fish_brew_complete_sub_cmd -a cmd sub -d "A shortcut for defining brew subcommands completions"
    set -e argv[1..2]
    if count $argv > /dev/null
        __fish_brew_complete_arg "$cmd; and [ (count (__fish_brew_args)) = 1 ]" -a $sub -d $argv
    else
        __fish_brew_complete_arg "$cmd; and [ (count (__fish_brew_args)) = 1 ]" -a $sub
    end
end

function __fish_brew_complete_sub_arg -a cmd sub -d "A shortcut for defining brew subcommand arguments completions"
    set -e argv[1..2]
    # NOTE: $sub can be just a name of a subcommand (or several) or additionally any other condition
    complete -f -c brew -n "__fish_brew_subcommand $cmd $sub" $argv
end


##############
## COMMANDS ##
##############


<%= completion_functions.join("\n\n") %>



################################
## OFFICIAL EXTERNAL COMMANDS ##
################################
# TODO: These commands are installed/tapped separately, so they should be completed only when present

##############
### BUNDLE ###

__fish_brew_complete_cmd 'bundle' "Install or upgrade all dependencies in a Brewfile"
__fish_brew_complete_arg 'bundle; and [ (count (__fish_brew_args)) = 1 ]' -s v -l verbose -d "Print more details"

# --file/--global option is available for bundle command and all its subcommands except exec
__fish_brew_complete_arg 'bundle;
        and not __fish_brew_subcommand bundle exec;
        and not __fish_brew_opt --file --global
    ' -l file -r -d "Specify Brewfile"
__fish_brew_complete_arg 'bundle;
        and not __fish_brew_subcommand bundle exec;
        and not __fish_brew_opt --file --global
    ' -l global  -d "Use \$HOME/.Brewfile"

__fish_brew_complete_sub_cmd 'bundle' 'install' "Install and upgrade (by default) all dependencies from the Brewfile."
__fish_brew_complete_sub_cmd 'bundle' 'dump'    "Write all installed casks/formulae/taps into a Brewfile"
__fish_brew_complete_sub_cmd 'bundle' 'cleanup' "Uninstall all dependencies not listed in a Brewfile"
__fish_brew_complete_sub_cmd 'bundle' 'check'   "Check if all dependencies are installed in a Brewfile"
__fish_brew_complete_sub_cmd 'bundle' 'exec'    "Run an external command in an isolated build environment"
__fish_brew_complete_sub_cmd 'bundle' 'list'    "List all dependencies present in the Brewfile."

# --force is available only for the dump/cleanup subcommands
__fish_brew_complete_sub_arg 'bundle' 'dump cleanup' -l force -d "Uninstall dependencies or overwrite an existing Brewfile"

# --no-upgrade is available for bundle command and its check subcommand
__fish_brew_complete_arg 'bundle; and [ (count (__fish_brew_args)) = 1 ];
        or __fish_brew_subcommand bundle check
    ' -l no-upgrade -d "Don't run brew upgrade for outdated dependencies"


################
### SERVICES ###

__fish_brew_complete_cmd 'services' "Integrates Homebrew formulae with macOS's launchctl manager"
__fish_brew_complete_arg 'services; and [ (count (__fish_brew_args)) = 1 ]' -s v -l verbose -d "Print more details"

__fish_brew_complete_sub_cmd 'services' 'list'    "List all running services for the current user"
__fish_brew_complete_sub_cmd 'services' 'run'     "Run service without starting at login/boot"
__fish_brew_complete_sub_cmd 'services' 'start'   "Start service immediately and register it to launch at login/boot"
__fish_brew_complete_sub_cmd 'services' 'stop'    "Stop service immediately and unregister it from launching at login/boot"
__fish_brew_complete_sub_cmd 'services' 'restart' "Stop and start service immediately and register it to launch at login/boot"
__fish_brew_complete_sub_cmd 'services' 'cleanup' "Remove all unused services"

__fish_brew_complete_sub_arg 'services' 'run start stop restart' -l all -d "Run all available services"
__fish_brew_complete_sub_arg 'services' 'run start stop restart' -a '(__fish_brew_suggest_services)'
