just icon indicating copy to clipboard operation
just copied to clipboard

Add recipes to generated completion scripts

Open casey opened this issue 5 years ago • 15 comments

In #572, I'll be landing a --completions flag to make just output completion scripts for various shells. However, these completion scripts should also allow completing recipe names (like build or test). I'll need to add this functionality separately.

See #223 for completion scripts that others have written.

Add recipe completions to:

  • [x] Bash
  • [x] Zsh
  • [x] Fish
  • [x] PowerShell
  • [ ] Elvish

casey avatar Jan 15 '20 04:01 casey

@casey If the just.bash file (see https://github.com/casey/just/pull/572/files#diff-1dcc75cc227d1a56d11d839f20f6a50c) is generated with clap, how do you plan to maintain it if I fiddle to merge the recipe completion I have in https://github.com/gotcha/just-bash-completion ?

gotcha avatar Jan 15 '20 11:01 gotcha

@gotcha Good question!

I was thinking that I would first write the completion script to a string while generating it in config.rs, and then modifying it before printing it. (Either by appending more stuff to the end, or doing a search and replace if it's more involved than that.)

If you can show me what should be added to the completion script (either in a gist, or a PR), then I can make the changes in rust to add it when it's generated too.

casey avatar Jan 15 '20 11:01 casey

<ARGUMENTS>In case of bash, I do this dirty trick to replace <ARGUMENTS>... with (fine tuned) output of just --list.

source <(OPTS=$(just --list | tail -n +2 | sed 's/    / /g' | tr -d '\n'); just --completions bash | sed "s/  <ARGUMENTS>... /$OPTS/g")

Do you think it would make sense to include similar solution in default script generated by just --completions bash? Probably some dedicated variant of just --list would be applicable here.

gergelyk avatar Feb 12 '21 10:02 gergelyk

That's an awesome trick! I'd be worried about adding it into the default script, just because it adds dependencies on tail, sed, and tr. But if you can modify the bash completion script to include recipe names without using those additional commands, I can modify the scripts when they're generated, here: https://github.com/Insomniak47/just/blob/master/src/subcommand.rs

casey avatar Feb 12 '21 11:02 casey

I agree. tail, sed, tr is probably what we wand to avoid. As an alternative I would suggest extending functionality of --list. E.g. --list-inline would give the same output as --list but in a single line, without header line and space-separated. This way completion script could be modified so that instead of <ARGUMENTS>... we have $(just --list-inline).

gergelyk avatar Feb 12 '21 12:02 gergelyk

Ah, okay, I misunderstood. Actually, that's already available if you do just --summary, it'll print a space-separated list of recipe names on a single line.

casey avatar Feb 12 '21 12:02 casey

Great! I confirm, the following does the trick:

source <(just --completions bash | sed 's/ <ARGUMENTS>... /$(just --summary)/g')

We will appreciate making it default in just --completions bash.

gergelyk avatar Feb 12 '21 13:02 gergelyk

Actually, can you check to see if you're using the latest version of Just? I think recipe completions may have already been added to the latest version of the bash completions script:

https://github.com/casey/just/blob/c647efa200c71bf8b73511c47e88a1cca0f87c0e/completions/just.bash#L28

casey avatar Feb 12 '21 22:02 casey

You are correct. just of mine wasn't up to date. After upgrading to v0.8.4 I got it working. The only remark I can make is that it works only for the first task that I try to execute. For instance here only foo is completed and bar, baz aren't:

just foo bar baz

gergelyk avatar Feb 14 '21 18:02 gergelyk

After upgrading to v5.4 I got it working.

Ah, great. I don't use bash myself, so I forgot that it already had completions.

The only remark I can make is that it works only for the first task that I try to execute.

That's definitely an unfortunate limitation of the completion script. I opened #756 to track it.

casey avatar Feb 15 '21 00:02 casey

This solution does not support aliases which are sometimes made up of several characters

just --summary

To support aliases and other section we can use this command:

just --list --list-heading "" | awk '{print $1}'

You can also add checking for justfile to expand completions only when file exists:

if [ -f justfile ]; then
    options=$(just --list --list-heading "" | awk '{print $1}')
fi

Is it possible to add autocomplete that would match only sections and aliases from justfile? Without auto-completion for just? In case when we would like use only auto-completion for our project we could simplify bash completions to:

_just()
{
    COMPREPLY=()
    options=()

    if [ -f justfile ]; then
        options=$(just --list --list-heading "" | awk '{print $1}')
    fi

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

complete -F _just j

j is simple wrapper function for just:

function j()
{
    if [ $# -eq 0 ]
    then
        just --list
    else
        just "$@"
    fi
}

In this case I would like to request new command something like --project-only or something like that.

And it resolving limitation of the completion script so You can press 2 xTAB, use option, use again 2 x TAB, use next option etc etc. So it helps with auto-completion for:

just foo bar baz

Like:

obraz

It also works dynamically for each of project, so You will have other auto-completions for each of justfile (without changing anything). Like:

obraz

8tm avatar Feb 15 '21 07:02 8tm

This solution does not support aliases which are sometimes made up of several characters.

I wasn't sure whether completion scripts should complete aliases, as well as full recipe names. My thought was completions scripts should only complete full recipe names, since aliases are often made to be shorter to type, but might clutter completion results. I'm curious how others feel about this though.

You can also add checking for justfile to expand completions only when file exists:

if [ -f justfile ]; then
    options=$(just --list --list-heading "" | awk '{print $1}')
fi

Unfortunately this only works if the justfile is in the current directory, but not if it's in a parent directory.

Is it possible to add autocomplete that would match only sections and aliases from justfile? Without auto-completion for just?

Do you mean auto-complete that only completes recipe names and aliases? Is this because it's simpler, or because it's undesirable to complete flags and options?

casey avatar Feb 15 '21 09:02 casey

I wasn't sure whether completion scripts should complete aliases, as well as full recipe names.

I don't have sharp opinion, but probably yes, I would include aliases too.

gergelyk avatar Feb 15 '21 12:02 gergelyk

Solution for Bash with parent directories (fixed):

#!/usr/bin/env bash

_just()
{
    COMPREPLY=()
    options=""
    nearest_existing_justfile_path=""
    path=""
    DEFAULT_IFS="${IFS}"
    IFS="/";


    for current_folder_name in $(pwd);
    do
        IFS="${DEFAULT_IFS}"
        path="${path}/${current_folder_name}"

        if [ -f "${path}/justfile" ]; then
            nearest_existing_justfile_path="${path}/justfile"
        fi;

        IFS="/"
    done;

    IFS="${DEFAULT_IFS}"

    if [ -f "${nearest_existing_justfile_path}" ]; then
        options=$(just --list --list-heading "" --justfile "${nearest_existing_justfile_path}" | awk '{print $1}')
    fi

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

complete -F _just j

8tm avatar Feb 15 '21 14:02 8tm

I am using the following for fish:

# Fish completion for just
# Dynamically completes recipe names from justfile

function __fish_just_recipes
    set -l path (pwd)
    set -l nearest_justfile ""
    
    # Search for justfile in current and parent directories
    while test "$path" != "/"
        if test -f "$path/justfile"
            set nearest_justfile "$path/justfile"
            break
        else if test -f "$path/.justfile"
            set nearest_justfile "$path/.justfile"
            break
        end
        set path (dirname "$path")
    end
    
    # If justfile found, list recipes
    if test -n "$nearest_justfile"
        just --list --list-heading "" --justfile "$nearest_justfile" | awk '{print $1}'
    end
end

# Complete recipe names for just command
complete -c just -f -a '(__fish_just_recipes)'

# Complete recipe names for 'j' alias if you use it
complete -c j -f -a '(__fish_just_recipes)'

alisonjenkins avatar Nov 10 '25 21:11 alisonjenkins