Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Completion script for the ctags program



Here’s the updated script. It's now is able to detect if it’s the new universal ctags, the older exuberant ctags, bsd ctags that comes with the Mac, or a symlink to etags:

#compdef ctags

local context state line expl
local -A opt_args

if [ -z "$_ctags_type" ]; then
  local output=`ctags --version 2>&1`
  if [[ "$output" = *Universal\ Ctags* ]]; then
    _ctags_type="universal"
  elif [[ "$output" = *Exuberant\ Ctags* ]]; then
    _ctags_type="exuberant"
  elif [[ "$output" = *usage:\ ctags* ]]; then
    _ctags_type="bsd"
  elif [[ "$output" = *Emacs* ]]; then
    _ctags_type="etags"
  else
    _ctags_type="universal"
  fi
fi

if [ "$_ctags_type" = "etags" ]; then
  _etags
  return $?
fi

local -a arguments

if [ "$_ctags_type" = "universal" ]; then
  arguments=(
    "-?[help text]"
    "-a[append to tags file]"
    "-B[use backward searching patterns (?...?)]"
    "-D[give definition for macro]:macro definition:"
    "-e[output tag file for use with emacs]"
    "-f[write tags to specified file. - is stdout]:file:_files"
    "-F[use forward searching patterns (/.../)]"
    "-G[equivalent to --guess-language-eagerly]"
    "-h[specify list of file extensions to be treated as include files]:"
    "-I[a list of tokens to be specifically handled is read from either the command line or the specified file]:"
    "-L[a list of input file names is read from the specified file. - is stdin]:file:_files"
    "-n[equivalent to --excmd=number]"
    "-N[equivalent to --excmd=pattern]"
    "-o[alternative to -f]:file:_files"
    "-R[equivalent to --recurse]"
    "-u[equivalent to --sort=no]"
    "-V[equivalent to --verbose]"
    "-x[print a tabular cross reference file to stdout]"
    "--alias-<lang>=[add a pattern detecting a name, can be used as an alt name for lang]:pattern"
    "--append=[should tags be appended to existing tag file]:bool:(yes no)"
    "--etags-include=[include reference to file in emacs style tag file]:file:_files"
    "--exclude=[exclude files and directories matching pattern]:pattern"
    "--exclude-exception=[don't exclude files and directories matching pattern even if they match the pattern specified with --exclude]:pattern"
    "--excmd=[uses the specified type of ex command to locate tags]:ex command type:(number pattern mix combine)"
    "--extras=[include extra tag entries for selected information (flags fFgpqrs)]:flags"
    "--extras-<lang>=[include <lang> own extra tag entries for selected information]:flags"
    "--fields=[include selected extension fields (flags aCeEfFikKlmnNpPrRsStxzZ)]:flags"
    "--fields-<lang>=[include selected <lang> own extension fields]:flags"
    "--filter=[behave as a filter, reading file names from stdin and writing tags to stdout]:bool:(yes no)"
    "--filter-terminator=[specify string to print to stdout following the tags for each file parsed when --filter is enabled]:string"
    "--format=[force output of specified tag file format]:level"
    "--guess-language-eagerly[guess the language of input file more eagerly]"
    "--help[help text]"
    "--help-full[help text with experimental features]"
    "--if0=[should code within #if 0 conditionals be parsed]:bool:(yes no)"
    "--input-encoding=[specify encoding of all input files]:encoding"
    "--input-encoding-<lang>=[specify encoding of the <lang> input files]:encoding"
    "--kinddef-<lang>=[define new kind for <lang>]:kind"
    "--kinds-<lang>=[enable/disable tag kinds for <lang>]:kind"
    "--langdef=[define a new language to be parsed with regular expressions]:name"
    "--langmap=[override default mapping of language to input file extension]:maps"
    "--language-force=[force all files to be interpreted using specified language]:language:->language"
    "--languages=[restrict files scanned to these comma-separated languages]:language:->languages"
    "--license[print details of software license]"
    "--line-directives=[should #line directives be processed]:bool:(yes no)"
    "--links=[indicate whether symlinks should be followed]:bool:(yes no)"
    "--list-aliases=[list of alias patterns]:language:->language"
    "--list-excludes[list of exclude patterns for files/dirs]"
    "--list-extras=[list of extra tag flags]:language:->language"
    "--list-features[list of compiled features]"
    "--list-fields=[list of fields]:language:->language"
    "--list-kinds=[list of all tag kinds for lang]:language:->language"
    "--list-kinds-full=[list details of all tag kinds for lang]:language:->language"
    "--list-languages[list of supported languages]"
    "--list-map-extensions=[list of language extensions in mapping]:language:->language"
    "--list-map-patterns=[list of language patterns in mapping]:language:->language"
    "--list-maps=[list of language mappings (both extensions and patterns)]:language:->language"
    "--list-mline-regex-flags[list of flags which can be used in a multiline regex parser definition]"
    "--list-params=[list of language parameters. works with --machinable]:language:->language"
    "--list-pseudo-tags[list of pseudo tags]"
    "--list-regex-flags[list of flags which can be used in a regex parser definition]"
    "--list-roles=[list of all roles of tag kinds specified for langs]:language:->language"
    "--list-subparsers=[list of subparsers for the base lang]:language:->language"
    "--machinable=[use tab separated representation in --list-* output]:bool:(yes no)"
    "--map-<lang>=[set, add(+), or remove(-) the map for <lang>]:pattern"
    "--maxdepth=[specify maximum recursion depth]:depth"
    "--mline-regex-<lang>=[define multiline regex for locating tags in <lang>]:pattern"
    "--options=[specify file (or dir) from which command line options should be read]:file:_files"
    "--options-maybe=[same as --options but doesn't error]:file:_files"
    "--optlib-dir=[add or set dir to optlib search path]:dir:_files -/"
    "--output-encoding=[the encoding to write the tag file in]:encoding"
    "--output-format=[specify the output format]:format:(u-ctags e-ctags etags xref)"
    "--param-<lang>=[set <lang> specific parameter]:argument"
    "--pattern-length-limit=[cutoff patterns of tag entries after N characters]:number"
    "--print-language[don't make tags file but just print the guessed lang name for input file]"
    "--pseudo-tags=[enable/disable emitting pseudo tag named ptag. if *, enable emitting all pseudo tags]:ptag"
    "--put-field-prefix[put UCTAGS as prefix for the name of fields newly introducted in universal ctags]"
    "--quiet=[don't print notice class messages]:bool:(yes no)"
    "--recurse=[recurse]:bool:(yes no)"
    "--regex-<lang>=[define regex for locating tags in specific lang]:pattern"
    "--roles-<lang>.<kind>=[enable/disable tag roles for kinds of <lang>]:role"
    "--sort=[should tags be sorted]:argument:(yes no foldcase)"
    "--tag-relative=[should paths be relative to location of tag file]:argument:(yes no always never)"
    "--totals=[print stats about input and tag files]:arguments:(yes no extra)"
    "--verbose=[enable verbose messages describing actions]:bool:(yes no)"
    "--version[print version]"
    "--with-list-header=[prepend the column descriptions in --list-* output]:bool:(yes no)"
    "*:file:_files"
  )
elif [ "$_ctags_type" = "exuberant" ]; then
  arguments=(
    "-a[append to tags file]"
    "-B[use backward searching patterns (?...?)]"
    "-e[output tag file for use with emacs]"
    "-f[write tags to specified file. - is stdout]:file:_files"
    "-F[use forward searching patterns (/.../)]"
    "-h[specify list of file extensions to be treated as include files]:"
    "-I[a list of tokens to be specifically handled is read from either the command line or the specified file]:"
    "-L[a list of input file names is read from the specified file. - is stdin]:file:_files"
    "-n[equivalent to --excmd=number]"
    "-N[equivalent to --excmd=pattern]"
    "-o[alternative to -f]:file:_files"
    "-R[equivalent to --recurse]"
    "-u[equivalent to --sort=no]"
    "-V[equivalent to --verbose]"
    "-x[print a tabular cross reference file to stdout]"
    "--append=[should tags be appended to existing tag file]:bool:(yes no)"
    "--etags-include=[include reference to file in emacs style tag file]:file:_files"
    "--exclude=[exclude files and directories matching pattern]:pattern"
    "--excmd=[uses the specified type of ex command to locate tags]:ex command type:(number pattern mix)"
    "--extra=[include extra tag entries for selected information (flags fq)]:flags"
    "--fields=[include selected extension fields (flags afmikKlnsStz)]:flags"
    "--file-scope=[should tags scoped only for a single file be included in output]:bool:(yes no)"
    "--filter=[behave as a filter, reading file names from stdin and writing tags to stdout]:bool:(yes no)"
    "--filter-terminator=[specify string to print to stdout following the tags for each file parsed when --filter is enabled]:string"
    "--format=[force output of specified tag file format]:level"
    "--help[help text]"
    "--if0=[should code within #if 0 conditionals be parsed]:bool:(yes no)"
    "--<lang>-kinds=[enable/disable tag kinds for <lang>]:kind"
    "--langdef=[define a new language to be parsed with regular expressions]:name"
    "--langmap=[override default mapping of language to input file extension]:maps"
    "--language-force=[force all files to be interpreted using specified language]:language:->language"
    "--languages=[restrict files scanned to these comma-separated languages]:language:->languages"
    "--license[print details of software license]"
    "--line-directives=[should #line directives be processed]:bool:(yes no)"
    "--links=[indicate whether symlinks should be followed]:bool:(yes no)"
    "--list-kinds=[list of all tag kinds for lang]:language:->language"
    "--list-languages[list of supported languages]"
    "--list-maps=[list of language mappings (both extensions and patterns)]:language:->language"
    "--options=[specify file (or dir) from which command line options should be read]:file:_files"
    "--recurse=[recurse]:bool:(yes no)"
    "--regex-<lang>=[define regex for locating tags in specific lang]:pattern"
    "--sort=[should tags be sorted]:argument:(yes no foldcase)"
    "--tag-relative=[should paths be relative to location of tag file]:argument:(yes no)"
    "--totals=[print stats about input and tag files]:arguments:(yes no)"
    "--verbose=[enable verbose messages describing actions]:bool:(yes no)"
    "--version[print version]”
    "*:file:_files"
  )
elif [ "$_ctags_type" = "bsd" ]; then
  arguments=(
    "-a[append to tags file]"
    "-B[use backward searching patterns (?...?)]"
    "-d[create tags for #defines that don't take arguments]"
    "-F[use forward searching patterns (/.../)]"
    "-f[write tags to specified file]:file:_files"
    "-t[create tags for typedefs, structs, unions, and enums]"
    "-u[update the specified files in the tags file]"
    "-v[an index of the form expected by vgrind(1) is produced]"
    "-w[suppress warning diagnostics]"
    "-x[ctags produces a simple function index]"
    "*:file:_files"
  )
fi

_arguments $arguments

if [[ "$state" = language* ]]; then
  local -a languages
  languages=(`ctags --list-languages | cut -d" " -f1`)
  if [ "$state" = "language" ]; then
    _wanted languages expl language compadd $languages
  elif [ "$state" = "languages" ]; then
    _values -s , languages $languages
  fi
fi

return $(( compstate[nmatches] > 0 ? 0 : 1 ))







> On Feb 23, 2021, at 10:45 PM, Jacob Gelbman <gelbman@xxxxxxxxx> wrote:
> 
> Hey, thanks for looking at the script and adding it to the repo, although I think some of got pasted in wrong. There’s a lot to writing completion functions and I’m still not 100% sure how to do it right.
> 
>> On Feb 23, 2021, at 3:39 PM, Oliver Kiddle <opk@xxxxxxx> wrote:
>> 
>> Jacob Gelbman wrote:
>>> I wrote a completion script for the ctags program. Someone might be able to use it:
>> 
>> Which ctags!?
> 
> I have Universal Ctags 5.9.0
> 
>> This doesn't match what I have installed on any of my systems. There
>> are multiple implementations of ctags, with it often being just a link
>> to etags - for which there is a completion albeit not a well maintained
>> one. One of the main reasons, a completion doesn't already exist is
>> that it would ideally need to detect the variant and at least have sane
>> fallbacks for variants that aren't handled. It could be useful to check
>> what the existing _etags is handling - that might be the exhuberant or
>> emacs variant.
>> 
> 
> I located a few other ctags on my computers, I have BSD ctags that comes by default on the mac. Exuberant Ctags 5.8. and there’s etags that comes with emacs. I can probably add an if statement based on the output of ctags —version, and modify the function from that. If it’s etags, I’ll just:
> 
> _comps[ctags]=“_etags”; _etags
> 
> And exit.
> 
>> In general, please follow the conventions outlined in
>> Etc/completion-style-guide in the zsh source distribution. For example,
>> completion functions usually use just 2 spaces for indentation.
>> 
>>> #compdef ctags
>>> 
>>> local state
>> 
>> If you use states, you need to also handle the context which means
>> either passing -C to _arguments and setting up $curcontext or declaring
>> context local and passing it to later functions like _values.
> 
> The -C argument and the context/curcontext variables are confusing me, a lot.
> 
>> 
>>>   "--alias-<lang>=[add a pattern detecting a name, can be used as an alt name for lang]:pattern" \
>>>   "--input-encoding-<lang>=[specify encoding of the <lang> input files]:encoding" \
>>>   "--kinddef-<lang>=[define new kind for <lang>]:kind" \
>>>   "--kinds-<lang>=[enable/disable tag kinds for <lang>]:kind" \
>> 
>> These would not complete especially helpfully. I suspect that <lang> there is
>> supposed to be substituted.
> 
> They’d show up in the menu when you press tab, but if I filled in the actual values, the list would be too long.
> 
>> 
>>> if [ "$state" = "language" ]; then
>>>   compadd `ctags --list-languages | cut -d" " -f1`
>> 
>> It would be nicer to use a description by calling for example, _wanted
>> here.
> 
> I can do that.
> 
>> 
>>> elif [ "$state" = "languages" ]; then
>>>   _values -s , "languages" `ctags --list-languages | cut -d" " -f1`
>>> fi
>> 
>> I'd probably use _sequence here as it is smaller and simpler. But
>> _values is fine if none of the languages contain characters that need
>> quoting from it.
> 
> This too.
> 
>> 
>> The return status from this function will not be correct in all cases.
>> This can have effects like approximate completion being activated
>> despite matches having been added by earlier completers. Where states
>> are needed, you nearly always need to either save the status from
>> _arguments, typically via a ret variable or check $compstate[nmatches]
>> on exit.
>> 
>> Oliver
> 





Messages sorted by: Reverse Date, Date, Thread, Author