Search code examples
zshzsh-completion

Only suggest executables and not directories when typing ./ (dot-slash) in zsh


Here's an example snippet from my current zsh session:

llama@llama:...Code/cpp/KingOfTheCode$ make
clang++ -std=c++11 -Iinclude src/*.cpp kothsrc/*.cpp -pthread -o KingOfTheCode
llama@llama:...Code/cpp/KingOfTheCode$ ./
Completing executable file or directory
include/        KingOfTheCode*  kothsrc/        src/

(I typed a ., a /, and then a Tab character for autocompletion.)

Why does zsh suggest directories when I type ./<tab>? I clearly want to execute a file, and if I wanted to execute something in a subdirectory the ./ part would be useless.

How can I prevent this annoying behavior from occurring? Just to be clear, my desired behavior is that autocompletion for ./<...> excludes directories and only looks for executable files.


Solution

  • I assume that there is compinit somewhere in the ~/.zshrc.

    You could limit the completion results by zstylelike this:

    zstyle -e ':completion::complete:-command-::executables' ignored-patterns '
      [[ "$PREFIX" == ./* ]] && { reply=(./*(/)) }
    '
    

    This adds the ignored-patterns zstyle entries when typing ./<...> then hitting the Tab. Abobe ./*(/) part indicates directories in the current directory. As a result, any subdirectories will be removed from completion results when completing the “executable file or directory”.


    If you want to reinstate completion results without ignored-patterns effects if there are no matching completions, you could add the _ignored completer:

    # this is same as the zsh default behavior though.
    zstyle ':completion:*' completer _complete _ignored
    

    It should complete the ignored subdirectories in this case with typing ./koTab./koshsrc/.


    Update: Mon, 29 Dec 2014 21:30:37 +0900 (Thank you for your suggestion, @Francisco)
    Teach to skip directories of which namestring has some spaces in it.

    zstyle -e ':completion::complete:-command-::executables' ignored-patterns '
      [[ "$PREFIX" == ./* ]] && {
        local -a tmp; set -A tmp ./*(/)
        : ${(A)tmp::=${tmp// /\\ }}
        reply=(${(j:|:)tmp})
      }
    '