summaryrefslogtreecommitdiff
path: root/Completion/Core/_approximate
diff options
context:
space:
mode:
Diffstat (limited to 'Completion/Core/_approximate')
-rw-r--r--Completion/Core/_approximate197
1 files changed, 197 insertions, 0 deletions
diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate
new file mode 100644
index 000000000..1b40f7cbf
--- /dev/null
+++ b/Completion/Core/_approximate
@@ -0,0 +1,197 @@
+#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