mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			197 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| #autoload
 | |
| 
 | |
| # This code will try to correct the string on the line based on the
 | |
| # strings generated for the context if `compconfig[correct]' is set.
 | |
| # These corrected strings will be shown in a list and one can
 | |
| # cycle through them as in a menucompletion or get the corrected prefix.
 | |
| #
 | |
| # Supported configuration keys:
 | |
| #
 | |
| #  approximate_accept
 | |
| #    This should be set to a number, specifying the maximum number
 | |
| #    of errors that should be accepted. If the string also contains
 | |
| #    a `n' or `N', the code will use the numeric argument as the
 | |
| #    maximum number of errors if a numeric argument was given. If no
 | |
| #    numeric argument was given, the number from the value of this
 | |
| #    key will be used. E.g. with `compconf approximate_accept=2n' two
 | |
| #    errors will be accepted, but if the user gives another number
 | |
| #    with the numeric argument, this will be prefered. Also, with
 | |
| #    `compconf approximate_accept=0n', normally no correction will be
 | |
| #    tried, but if a numeric argument is given, automatic correction
 | |
| #    will be used. On the other hand, if the string contains an `!'
 | |
| #    and a `n' or `N', correction is not attempted if a numeric
 | |
| #    argument is given. Once the number of errors to accept is
 | |
| #    determined, the code will repeatedly try to generate matches by
 | |
| #    allowing one error, two errors, and so on. Independent of the
 | |
| #    number of errors the user wants to accept, the code will allow
 | |
| #    only fewer errors than there are characters in the string from
 | |
| #    the line.
 | |
| #
 | |
| #  approximate_original
 | |
| #    This value is used to determine if the original string should
 | |
| #    be included in the list (and thus be presented to the user when
 | |
| #    cycling through the corrections). If it is set to any non-empty
 | |
| #    value, the original string will be offered. If it contains the
 | |
| #    sub-string `last', the original string will appear as the last
 | |
| #    string when cycling through the corrections, otherwise it will
 | |
| #    appear as the first one (so that the command line does not
 | |
| #    change immediately). Also, if the value contains the sub-string
 | |
| #    `always', the original string will always be included, whereas
 | |
| #    normally it is included only if more than one possible
 | |
| #    correction was generated.
 | |
| #
 | |
| #  approximate_prompt
 | |
| #    This can be set to a string that should be printed before the
 | |
| #    list of corrected strings when cycling through them. This string
 | |
| #    may contain the control sequences `%n', `%B', etc. known from
 | |
| #    the `-X' option of `compctl'. Also, the sequence `%e' will be
 | |
| #    replaced by the number of errors accepted to generate the
 | |
| #    corrected strings.
 | |
| #
 | |
| #  approximate_insert
 | |
| #    If this is set to a string starting with `unambig', the code
 | |
| #    will try to insert a usable unambiguous string in the command
 | |
| #    line instead of always cycling through the corrected strings.
 | |
| #    If such a unambiguous string could be found, the original
 | |
| #    string is not used, independent of the setting of
 | |
| #    `approximate_original'. If no sensible string could be found,
 | |
| #    one can cycle through the corrected strings as usual.
 | |
| #
 | |
| # If any of these keys is not set, but the the same key with the
 | |
| # prefix `correct' instead of `approximate' is set, that value will
 | |
| # be used.
 | |
| 
 | |
| local _comp_correct _correct_prompt comax
 | |
| local cfgacc cfgorig cfgps cfgins
 | |
| 
 | |
| # Only if all global matchers hav been tried.
 | |
| 
 | |
| [[ compstate[matcher] -ne compstate[total_matchers] ]] && return 1
 | |
| 
 | |
| # We don't try correction if the string is too short.
 | |
| 
 | |
| [[ "${#:-$PREFIX$SUFFIX}" -le 1 ]] && return 1
 | |
| 
 | |
| # Get the configuration values, using either the prefix `correct' or
 | |
| # `approximate'.
 | |
| 
 | |
| if [[ "$compstate[pattern_match]" = (|\**) ]]; then
 | |
|   cfgacc="${compconfig[approximate_accept]:-$compconfig[correct_accept]}"
 | |
|   cfgorig="${compconfig[approximate_original]:-$compconfig[correct_original]}"
 | |
|   cfgps="${compconfig[approximate_prompt]:-$compconfig[correct_prompt]}"
 | |
|   cfgins="${compconfig[approximate_insert]:-$compconfig[correct_insert]}"
 | |
| else
 | |
|   cfgacc="$compconfig[correct_accept]"
 | |
|   cfgorig="$compconfig[correct_original]"
 | |
|   cfgps="$compconfig[correct_prompt]"
 | |
|   cfgins="$compconfig[correct_insert]"
 | |
| fi
 | |
| 
 | |
| # Get the number of errors to accept.
 | |
| 
 | |
| if [[ "$cfgacc" = *[nN]* && NUMERIC -ne 1 ]]; then
 | |
|   # Stop if we also have a `!'.
 | |
| 
 | |
|   [[ "$cfgacc" = *\!* ]] && return 1
 | |
| 
 | |
|   # Prefer the numeric argument if that has a sensible value.
 | |
| 
 | |
|   comax="$NUMERIC"
 | |
| else
 | |
|   comax="${cfgacc//[^0-9]}"
 | |
| fi
 | |
| 
 | |
| # If the number of errors to accept is too small, give up.
 | |
| 
 | |
| [[ "$comax" -lt 1 ]] && return 1
 | |
| 
 | |
| # Otherwise temporarily define functions to use instead of
 | |
| # the builtins that add matches. This is used to be able
 | |
| # to stick the `(#a...)' into the right place (after an
 | |
| # ignored prefix).
 | |
| 
 | |
| compadd() {
 | |
|   [[ "$*" != *-([a-zA-Z/]#|)U* &&
 | |
|      "${#:-$PREFIX$SUFFIX}" -le _comp_correct ]] && return
 | |
| 
 | |
|   if [[ "$PREFIX" = \~*/* ]]; then
 | |
|     PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
 | |
|   else
 | |
|     PREFIX="(#a${_comp_correct})$PREFIX"
 | |
|   fi
 | |
|   if [[ -n "$_correct_prompt" ]]; then
 | |
|     builtin compadd -X "$_correct_prompt" -J _correct "$@"
 | |
|   else
 | |
|     builtin compadd -J _correct "$@"
 | |
|   fi
 | |
| }
 | |
| 
 | |
| compgen() {
 | |
|   [[ "$*" != *-([a-zA-Z/]#|)U* &&
 | |
|      "${#:-$PREFIX$SUFFIX}" -le _comp_correct ]] && return
 | |
| 
 | |
|   if [[ "$PREFIX" = \~*/* ]]; then
 | |
|     PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
 | |
|   else
 | |
|     PREFIX="(#a${_comp_correct})$PREFIX"
 | |
|   fi
 | |
|   if [[ -n "$_correct_prompt" ]]; then
 | |
|     builtin compgen "$@" -X "$_correct_prompt" -J _correct
 | |
|   else
 | |
|     builtin compgen "$@" -J _correct
 | |
|   fi
 | |
| }
 | |
| 
 | |
| # Now initialise our counter. We also set `compstate[matcher]'
 | |
| # to `-1'. This allows completion functions to use the simple
 | |
| # `[[ compstate[matcher] -gt 1 ]] && return' to avoid being
 | |
| # called for multiple global match specs and still be called 
 | |
| # again when correction is done. Also, this makes it easy to
 | |
| # test if correction is attempted since `compstate[matcher]'
 | |
| # will never be set to a negative value by the completion code.
 | |
| 
 | |
| _comp_correct=1
 | |
| compstate[matcher]=-1
 | |
| 
 | |
| _correct_prompt="${cfgps//\%e/1}"
 | |
| 
 | |
| # We also need to set `extendedglob' and make the completion
 | |
| # code behave as if globcomplete were set.
 | |
| 
 | |
| setopt extendedglob
 | |
| 
 | |
| [[ -z "$compstate[pattern_match]" ]] && compstate[pattern_match]='*'
 | |
| 
 | |
| while [[ _comp_correct -le comax ]]; do
 | |
|   if _complete; then
 | |
|     if [[ "$cfgins" = unambig* &&
 | |
|           "${#compstate[unambiguous]}" -ge "${#:-$PREFIX$SUFFIX}" ]]; then
 | |
|       compstate[pattern_insert]=unambiguous
 | |
|     elif [[ compstate[nmatches] -gt 1 || "$cfgorig" = *always* ]]; then
 | |
|       if [[ "$cfgorig" = *last* ]]; then
 | |
|         builtin compadd -U -V _correct_original -nQ - "$PREFIX$SUFFIX"
 | |
|       elif [[ -n "$cfgorig" ]]; then
 | |
| 	builtin compadd -U -nQ - "$PREFIX$SUFFIX"
 | |
|       fi
 | |
| 
 | |
|       # If you always want to see the list of possible corrections,
 | |
|       # set `compstate[list]=list' here.
 | |
| 
 | |
|       compstate[force_list]=list
 | |
|     fi
 | |
|     compstate[matcher]="$compstate[total_matchers]"
 | |
|     unfunction compadd compgen
 | |
| 
 | |
|     return 0
 | |
|   fi
 | |
| 
 | |
|   [[ "${#:-$PREFIX$SUFFIX}" -le _comp_correct+1 ]] && break
 | |
|   (( _comp_correct++ ))
 | |
| 
 | |
|   _correct_prompt="${cfgps//\%e/$_comp_correct}"
 | |
| done
 | |
| 
 | |
| compstate[matcher]="$compstate[total_matchers]"
 | |
| unfunction compadd compgen
 | |
| 
 | |
| return 1
 |