Skip to content

Instantly share code, notes, and snippets.

@jhoff
Last active April 1, 2024 07:45
Show Gist options
  • Save jhoff/8fbe4116d74931751ecc9e8203dfb7c4 to your computer and use it in GitHub Desktop.
Save jhoff/8fbe4116d74931751ecc9e8203dfb7c4 to your computer and use it in GitHub Desktop.
Bash-only Laravel Artisan tab auto-complete

If you are an Oh-my-zsh user, see the Laravel 5 plugin

For the rest of us Bash users, all of the Laravel Artisan autocomplete solutions out there require installing a composer package to get a list of artisan commands. Turns out this isn't really necessary. Simply add the provided code in ~/.bash_profile ( or similarly sourced file ) and you'll get artisan command tab completes on any project on your system.

_artisan()
{
	COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
	COMMANDS=`php artisan --raw --no-ansi list | sed "s/[[:space:]].*//g"`
	COMPREPLY=(`compgen -W "$COMMANDS" -- "${COMP_WORDS[COMP_CWORD]}"`)
	return 0
}
complete -F _artisan art
complete -F _artisan artisan
@pdiveris
Copy link

pdiveris commented Mar 7, 2019

Nice one, thank you!

@bhuvidya
Copy link

bhuvidya commented May 29, 2020

Hey thanks for this. I really like the fact that it works completely in the shell without needing a package to be installed. I'm a fussy bastard and felt the delay on auto-complete was a bit too slow (understandable because each time you ask the shell to do an auto-complete it is firing up a new instance of artisan list). So, as an interim solution, I define an extra shell function to create a cache file with all the artisan commands, and then simply cat this file for auto-complete. It does require that extra step of calling the cache function, but my commands don't change that much. If the cache file doesn't exist, then I fallback to the artisan list approach.

export ARTISAN_CMDS_FILE=bootstrap/cache/artisan-cmds.txt

function _artisan() {
    COMP_WORDBREAKS=${COMP_WORDBREAKS//:}

    if [ -f "$ARTISAN_CMDS_FILE" ]; then
        COMMANDS=$(cat "$ARTISAN_CMDS_FILE")
    else
        COMMANDS=$(php artisan --raw --no-ansi list | awk '{print $1}')
    fi

    COMPREPLY=(`compgen -W "$COMMANDS" -- "${COMP_WORDS[COMP_CWORD]}"`)

    return 0
}

function art_cache() {
    if [[ "$1" == "clear" ]]; then
        echo -n "Removing commands cache file..."
        rm -f "$ARTISAN_CMDS_FILE"
        echo "done."
    else
        php artisan --raw --no-ansi list | awk '{print $1}' > "$ARTISAN_CMDS_FILE"
        echo $(wc -l "$ARTISAN_CMDS_FILE" | awk '{print $1}')" artisan commands cached."
    fi
}

complete -F _artisan art
complete -F _artisan artisan

So to create the command cache file:

$ art_cache

and to remove the cache file

$ art_cache clear

@surgiie
Copy link

surgiie commented May 29, 2020

Nice! However i noticed this breaks file system path autocompletes. :/

When you press tab as you are typing the [file] path argument it doesnt autocomplete

php artisan some-file:command [file]

Anyone know what i can do to work around that?

@surgiie
Copy link

surgiie commented May 29, 2020

Never mind to above ^

Adding -o default to complete command fixes this.

@bhuvidya
Copy link

bhuvidya commented May 31, 2020

@surgiie Wow thanks for that - I hadn't realised. Cheers for the solution.

@sfinktah
Copy link

sfinktah commented Oct 14, 2021

@bhuvidya I really like your caching idea, and props to everyone for their contributions. However it's just not autocomplete if it can't expand out the --many --forgettable --options.

You may apply your individual improves to this if you like:

# bash completion for artisan     [email protected]   -*- shell-script -*-

__get_first_word()
{
    while read -r first rest; do
        echo "$first"
    done
}

_artisan()
{
    COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
    local cur prev words cword split
    local debug=0
    _init_completion -s -n : || return


    (( debug )) && {
        echo ""
        echo "===================="
        echo "cur: '$cur'"
        echo "prev: '$prev'"
        echo "words: '${words[@]}'"
        echo "cword: '${cword}'"
        echo "split: '${split}'"
        echo "--------------------"
        declare -p | grep 'COMP'
        echo "===================="
        echo ""
    }

    case $prev in
        art*)
            COMMANDS=$( php artisan --raw list | __get_first_word )
            COMPREPLY=(`compgen -W "$COMMANDS" -- "$cur"`)
            return 0
            ;;
    esac

    case $cur in
        -*)
            COMMANDS=$( php artisan ${words[1]} --help | sed 's/[][]//g' | _parse_help - )
            COMPREPLY=(`compgen -W "$COMMANDS" -- "$cur"`)
            return
            ;;
        *)
            _filedir
            return
            ;;
    esac
} &&
complete -F _artisan -o nospace artisan

# ex: filetype=sh

note: it wouldn't be that difficult to add database table/column name completion, but it would involve having write the solution partially in PHP as a laravel cli.

@DRSDavidSoft
Copy link

DRSDavidSoft commented Dec 5, 2022

I wrote a small script so you can call artisan from sub directories of the project,
e.g: project/app/example $ artisan,
instead of only project $ artisan.

If interested, install it from here then change php artisan to artisan in your completion function.

https://github.com/DRSDavidSoft/artisan-root/

Demo:

@sfinktah
Copy link

sfinktah commented Jan 23, 2023

@DRSDavidSoft ahh yes, those is one of the two cases that annoy me...

  1. having to change back to the webroot to run stuff
  2. not having autocompletion when i prefix it with sail

But I'm a little more lazy that you, and opted for this fix to find the webroot, inspired by you.

.bashrc

artisan () {
    pushd . > /dev/null
    while test ! -e artisan; do
        [[ $PWD == '/' ]] && break
        cd .. > /dev/null
    done
    test -e artisan && php artisan "$@"
    popd > /dev/null
}

sail () { 
    pushd . > /dev/null;
    while test ! -e artisan; do
        [[ $PWD == '/' ]] && break;
        cd .. > /dev/null;
    done;
    test -e artisan && { 
        vendor/laravel/sail/bin/sail "$@";
    };
    popd > /dev/null
}

@DRSDavidSoft
Copy link

@sfinktah Awesome idea, even better to write it directly in bash instead of php! 👍

@zahardev
Copy link

zahardev commented Jan 6, 2024

@sfinktah Thank you, it should be in the Laravel/Sail docs!

@sfinktah
Copy link

sfinktah commented Apr 1, 2024

@zahardev
@taylorotwell

Thanks! But I can't even get permission to contribute to the Laravel Nova repo, and I'm a paid subscriber.

P.S. @matthiasl Euphoria BBS?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment