1#compdef fd
2
3##
4# zsh completion function for fd
5#
6# Based on ripgrep completion function.
7# Originally based on code from the zsh-users project — see copyright notice
8# below.
9
10autoload -U is-at-least
11
12_fd() {
13  local curcontext="$curcontext" no='!' ret=1
14  local -a context line state state_descr _arguments_options fd_types fd_args
15  local -A opt_args
16
17  if is-at-least 5.2; then
18    _arguments_options=( -s -S )
19  else
20    _arguments_options=( -s )
21  fi
22
23  fd_types=(
24    {f,file}'\:"regular files"'
25    {d,directory}'\:"directories"'
26    {l,symlink}'\:"symbolic links"'
27    {e,empty}'\:"empty files or directories"'
28    {x,executable}'\:"executable (files)"'
29    {s,socket}'\:"sockets"'
30    {p,pipe}'\:"named pipes (FIFOs)"'
31  )
32
33  # Do not complete rare options unless either the current prefix
34  # matches one of those options or the user has the `complete-all`
35  # style set. Note that this prefix check has to be updated manually to account
36  # for all of the potential negation options listed below!
37  if
38    # (--[bpsu]* => match all options marked with '$no')
39    [[ $PREFIX$SUFFIX == --[bopsu]* ]] ||
40    zstyle -t ":complete:$curcontext:*" complete-all
41  then
42    no=
43  fi
44
45  # We make heavy use of argument groups here to prevent the option specs from
46  # growing unwieldy. These aren't supported in zsh <5.4, though, so we'll strip
47  # them out below if necessary. This makes the exclusions inaccurate on those
48  # older versions, but oh well — it's not that big a deal
49  fd_args=(
50    + '(hidden)' # hidden files
51    {-H,--hidden}'[search hidden files/directories]'
52
53    + '(no-ignore-full)' # all ignore files
54    '(no-ignore-partial)'{-I,--no-ignore}"[don't respect .(git|fd)ignore and global ignore files]"
55    $no'(no-ignore-partial)*'{-u,--unrestricted}'[alias for --no-ignore, when repeated also alias for --hidden]'
56
57    + no-ignore-partial # some ignore files
58    "(no-ignore-full --no-ignore-vcs)--no-ignore-vcs[don't respect .gitignore files]"
59    "!(no-ignore-full --no-global-ignore-file)--no-global-ignore-file[don't respect the global ignore file]"
60
61    + '(case)' # case-sensitivity
62    {-s,--case-sensitive}'[perform a case-sensitive search]'
63    {-i,--ignore-case}'[perform a case-insensitive search]'
64
65    + '(regex-pattern)' # regex-based search pattern
66    '(no-regex-pattern)--regex[perform a regex-based search (default)]'
67
68    + '(no-regex-pattern)' # non-regex-based search pattern
69    {-g,--glob}'[perform a glob-based search]'
70    {-F,--fixed-strings}'[treat pattern as literal string instead of a regex]'
71
72    + '(match-full)' # match against full path
73    {-p,--full-path}'[match the pattern against the full path instead of the basename]'
74
75    + '(follow)' # follow symlinks
76    {-L,--follow}'[follow symbolic links to directories]'
77
78    + '(abs-path)' # show absolute paths
79    '(long-listing)'{-a,--absolute-path}'[show absolute paths instead of relative paths]'
80
81    + '(null-sep)' # use null separator for output
82    '(long-listing)'{-0,--print0}'[separate search results by the null character]'
83
84    + '(long-listing)' # long-listing output
85    '(abs-path null-sep max-results exec-cmds)'{-l,--list-details}'[use a long listing format with file metadata]'
86
87    + '(max-results)' # max number of results
88    '(long-listing exec-cmds)--max-results=[limit number of search results to given count and quit]:count'
89    '(long-listing exec-cmds)-1[limit to a single search result and quit]'
90
91    + '(fs-errors)' # file-system errors
92    $no'--show-errors[enable the display of filesystem errors]'
93
94    + '(fs-traversal)' # file-system traversal
95    $no"--one-file-system[don't descend into directories on other file systems]"
96    '!--mount'
97    '!--xdev'
98
99    + dir-depth # directory depth
100    '(--exact-depth -d --max-depth)'{-d+,--max-depth=}'[set max directory depth to descend when searching]:depth'
101    '!(--exact-depth -d --max-depth)--maxdepth:depth'
102    '(--exact-depth --min-depth)--min-depth=[set directory depth to descend before start searching]:depth'
103    '(--exact-depth -d --max-depth --maxdepth --min-depth)--exact-depth=[only search at the exact given directory depth]:depth'
104
105    + prune # pruning
106    "--prune[don't traverse into matching directories]"
107
108    + filter-misc # filter search
109    '*'{-t+,--type=}"[filter search by type]:type:(($fd_types))"
110    '*'{-e+,--extension=}'[filter search by file extension]:extension'
111    '*'{-E+,--exclude=}'[exclude files/directories that match the given glob pattern]:glob pattern'
112    '*'{-S+,--size=}'[limit search by file size]:size limit:->size'
113    '(-o --owner)'{-o+,--owner=}'[filter by owning user and/or group]:owner and/or group:->owner'
114
115    + ignore-file # extra ignore files
116    '*--ignore-file=[add a custom, low-precedence ignore-file with .gitignore format]: :_files'
117
118    + '(filter-mtime-newer)' # filter by files modified after than
119    '--changed-within=[limit search to files/directories modified within the given date/duration]:date or duration'
120    '!--change-newer-than=:date/duration'
121    '!--newer=:date/duration'
122
123    + '(filter-mtime-older)' # filter by files modified before than
124    '--changed-before=[limit search to files/directories modified before the given date/duration]:date or duration'
125    '!--change-older-than=:date/duration'
126    '!--older=:date/duration'
127
128    + '(color)' # colorize output
129    {-c+,--color=}'[declare when to colorize search results]:when to colorize:((
130      auto\:"show colors if the output goes to an interactive console (default)"
131      never\:"do not use colorized output"
132      always\:"always use colorized output"
133    ))'
134
135    + '(threads)'
136    {-j+,--threads=}'[set the number of threads for searching and executing]:number of threads'
137
138    + '(exec-cmds)' # execute command
139    '(long-listing max-results)'{-x+,--exec=}'[execute command for each search result]:command: _command_names -e:*\;::program arguments: _normal'
140    '(long-listing max-results)'{-X+,--exec-batch=}'[execute command for all search results at once]:command: _command_names -e:*\;::program arguments: _normal'
141    '(long-listing max-results)--batch-size=[max number of args for each -X call]:size'
142
143    + other
144    '!(--max-buffer-time)--max-buffer-time=[set amount of time to buffer before showing output]:time (ms)'
145
146    + '(about)' # about flags
147    '(: * -)'{-h,--help}'[display help message]'
148    '(: * -)'{-v,--version}'[display version information]'
149
150    + path-sep # set path separator for output
151    $no'(--path-separator)--path-separator=[set the path separator to use when printing file paths]:path separator'
152
153    + search-path
154    $no'(--base-directory)--base-directory=[change the current working directory to the given path]:directory:_files -/'
155    $no'(*)*--search-path=[set search path (instead of positional <path> arguments)]:directory:_files -/'
156
157    + args # positional arguments
158    '1: :_guard "^-*" pattern'
159    '(--search-path)*:directory:_files -/'
160  )
161
162  # Strip out argument groups where unsupported (see above)
163  is-at-least 5.4 ||
164  fd_args=( ${(@)args:#(#i)(+|[a-z0-9][a-z0-9_-]#|\([a-z0-9][a-z0-9_-]#\))} )
165
166  _arguments $_arguments_options : $fd_args && ret=0
167
168  case ${state} in
169    owner)
170      compset -P '(\\|)\!'
171      if compset -P '*:'; then
172        _groups && ret=0
173      else
174        if
175          compset -S ':*' ||
176          # Do not add the colon suffix when completing "!user<TAB>
177          # (with a starting double-quote) otherwise pressing tab again
178          # after the inserted colon "!user:<TAB> will complete history modifiers
179          [[ $IPREFIX == (\\|\!)*  && ($QIPREFIX == \"* && -z $QISUFFIX) ]]
180        then
181          _users && ret=0
182        else
183          local q
184          # Since quotes are needed when using the negation prefix !,
185          # automatically remove the colon suffix also when closing the quote
186          if [[ $QIPREFIX == [\'\"]* ]]; then
187            q=${QIPREFIX:0:1}
188          fi
189          _users -r ": \t\n\-$q" -S : && ret=0
190        fi
191      fi
192      ;;
193
194    size)
195      if compset -P '[-+][0-9]##'; then
196        local -a suff=(
197          'B:bytes'
198          'K:kilobytes  (10^3  = 1000   bytes)'
199          'M:megabytes  (10^6  = 1000^2 bytes)'
200          'G:gigabytes  (10^9  = 1000^3 bytes)'
201          'T:terabytes  (10^12 = 1000^4 bytes)'
202          'Ki:kibibytes  ( 2^10 = 1024   bytes)'
203          'Mi:mebibytes  ( 2^20 = 1024^2 bytes)'
204          'Gi:gigibytes  ( 2^30 = 1024^3 bytes)'
205          'Ti:tebibytes  ( 2^40 = 1024^4 bytes)'
206        )
207        _describe -t units 'size limit units' suff -V 'units'
208      elif compset -P '[-+]'; then
209        _message -e 'size limit number (full format: <+-><number><unit>)'
210      else
211        _values 'size limit prefix (full format: <prefix><number><unit>)' \
212          '\+[file size must be greater or equal to]'\
213          '-[file size must be less than or equal to]' && ret=0
214      fi
215      ;;
216  esac
217
218  return ret
219}
220
221_fd "$@"
222
223# ------------------------------------------------------------------------------
224# Copyright (c) 2011 GitHub zsh-users - http://github.com/zsh-users
225# All rights reserved.
226#
227# Redistribution and use in source and binary forms, with or without
228# modification, are permitted provided that the following conditions are met:
229#     * Redistributions of source code must retain the above copyright
230#       notice, this list of conditions and the following disclaimer.
231#     * Redistributions in binary form must reproduce the above copyright
232#       notice, this list of conditions and the following disclaimer in the
233#       documentation and/or other materials provided with the distribution.
234#     * Neither the name of the zsh-users nor the
235#       names of its contributors may be used to endorse or promote products
236#       derived from this software without specific prior written permission.
237#
238# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
239# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
240# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
241# DISCLAIMED. IN NO EVENT SHALL ZSH-USERS BE LIABLE FOR ANY
242# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
243# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
244# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
245# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
246# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
247# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248# ------------------------------------------------------------------------------
249# Description
250# -----------
251#
252#  Completion script for fd
253#
254# ------------------------------------------------------------------------------
255# Authors
256# -------
257#
258#  * smancill (https://github.com/smancill)
259#
260# ------------------------------------------------------------------------------
261
262# Local Variables:
263# mode: shell-script
264# coding: utf-8-unix
265# indent-tabs-mode: nil
266# sh-indentation: 2
267# sh-basic-offset: 2
268# End:
269# vim: ft=zsh sw=2 ts=2 et
270