#compdef frama-c frama-c-gui frama-c.byte frama-c-gui.byte ########################################################################## # # # This file is part of Frama-C. # # # # Copyright (C) 2007-2019 # # CEA (Commissariat à l'énergie atomique et aux énergies # # alternatives) # # # # you can redistribute it and/or modify it under the terms of the GNU # # Lesser General Public License as published by the Free Software # # Foundation, version 2.1. # # # # It is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU Lesser General Public License for more details. # # # # See the GNU Lesser General Public License version 2.1 # # for more details (enclosed in the file licenses/LGPLv2.1). # # # ########################################################################## # zsh completion for Frama-C # ========================== # # Installation # ============ # # This file must be placed in a directory listed in the $fpath variable. # You can add a directory to $fpath by adding a line like the following # to your ~/.zshrc file: # # fpath=(~/newdir $fpath) # # It also works with relative paths, such as 'bin/frama-c'. # # # The autocompletion can benefit from the caching system offered by zsh: # `zstyle ':completion:*' use-cache on` to enable caching for all commands # `zstyle ':completion:*:*:frama-c*:*' use-cache on` only for frama-c # # ----------------------------------------------------------------------------- # TODO: # - use _call_program to call frama-c instead of calling frama-c directly ? # - other ideas when to renew cache ? #local curcontext="$curcontext" state state_descr line # expl ret=1 ?? #typeset -A opt_args # filter_load takes a command line calling frama-c and # removes everything not a -load-module or -load-script # argument 1 is the variable name of the input # argument 2 is the variable name of the output function filter_load () { local next=0 local -a my_args my_args=(${(P)1[1]}) for w in ${(P)1}; do if [[ $next -eq 1 ]]; then my_args+=($w) next=0 else # very strange behaviour when ' is used instead of " around -load-* # actually not related to this if [[ $w = '-load-module' ]] || [[ $w = '-load-script' ]]; then my_args+=("$w") next=1 fi fi done eval "$2=($my_args)" } function _frama_c () { local ret=1 # the return value (1 if no autocompletion is done, 0 otherwise) local -a my_words my_words=($words) my_words[1]=${my_words[1]/-gui} # call frama-c instead of frama-c-gui # we do not waste our time on computation if we are not completing an option if [[ -prefix -* ]]; then # if the first character of the current word is a '-' # is the first word on the line executable ? if $my_words[1] 2>/dev/null; then local -a the_args local -a the_previous_args # we keep only parts of the command line relevant to -load-module/-load-script filter_load my_words the_args # we load the previous filtered command from cache if available _retrieve_cache frama-c_previous_command # can overwrite the_previous_args # some gymnastics because the name of the variable matters local -a tmp tmp=($the_previous_args) the_previous_args=($the_args) _store_cache frama-c_previous_command the_previous_args the_previous_args=($tmp) # if the time of the most recent modification in # `frama-c -print-plugin-path` is not the same as the one # in the cache, we deduce that it is not the same "frama-c" # as before and recompute the cache. # We put the new date in the cache and store # this information in $recompute local last_change _retrieve_cache frama-c_last_change zmodload -F zsh/stat b:zstat 2>/dev/null local current_last_change=$(zstat +mtime $($my_words[1] -print-plugin-path)/**/*(.om[1])) local recompute (( recompute = $current_last_change != ${last_change:-0} )) if (( $recompute )); then last_change=$current_last_change _store_cache frama-c_last_change last_change fi # if something in `frama-c -print-plugin-path` changed, # if the filtered current command is different from the remembered one or # if the cache is unavailable, recompute the list of options, # otherwise just load the cache if (( $recompute )) || [[ $the_args != $the_previous_args ]] || _cache_invalid frama-c_autocompletion || ! _retrieve_cache frama-c_autocompletion then local -a autocompletion local autocomp # call frama-c with all the -load-module ; if it fails, test without the load-modules ; # if it fails again, abort autocomp=$($the_args -autocomplete 2>/dev/null) || autocomp=$($my_words[1] -autocomplete 2>/dev/null) || unset autocomp (( $+autocomp )) && autocompletion=($(grep -o "\-[^ ]*" <<< $autocomp | sort)) (( $#autocompletion )) || _message "$my_words[1] exists, but no option was detected" _store_cache frama-c_autocompletion autocompletion fi _describe 'options' autocompletion && ret=0 else _message "$my_words[1] not found, dynamic autocompletion aborted" _files && ret=0 # defaults to _files fi else # if we complete a file (not sure if '_files' is the best default) _files && ret=0 fi return $ret } # call _frama_c when autocompletion is requested _frama_c "$@"