Allow an even number of backslashes before control operators and use variables for shell metacharacters
This commit is contained in:
parent
31ad7d7c28
commit
b2639827d7
1 changed files with 26 additions and 14 deletions
40
bash-abbr
40
bash-abbr
|
|
@ -111,15 +111,21 @@
|
||||||
# Therefore, it might interfere with other plugins/custom bindings.
|
# Therefore, it might interfere with other plugins/custom bindings.
|
||||||
# Some bindings may be changed (e.g. \e\C-a...) while others should not (e.g. \C-m)
|
# Some bindings may be changed (e.g. \e\C-a...) while others should not (e.g. \C-m)
|
||||||
|
|
||||||
# Regexes for heuristically determining whether we're abbreviating the first word.
|
# Setting IFS to null so we can use "${var[*]}" syntax to make a string out of the meta char array
|
||||||
|
_bash_base_meta_chars=('|' '&' ';' '(' ')')
|
||||||
|
_bash_meta_chars_without_left_right_angle_brackets=("${_bash_base_meta_chars[@]}" ' ' $'\t' $'\n')
|
||||||
|
_bash_meta_chars_without_tab_and_nl=("${_bash_base_meta_chars[@]}" '<' '>' ' ')
|
||||||
|
_bash_meta_chars=("${_bash_base_meta_chars[@]}" '<' '>' ' ' $'\t' $'\n')
|
||||||
|
|
||||||
|
# Regexes for heuristically determining whether we're doing expansion on the first word.
|
||||||
# Construction put into functions to prevent global namespace pollution
|
# Construction put into functions to prevent global namespace pollution
|
||||||
# and to increase readability.
|
# and to increase readability.
|
||||||
_control_op () {
|
_control_op () {
|
||||||
declare -r _newline=$'\n' \
|
declare -r _newline=$'\n' \
|
||||||
_bg_proc_or_and_and='[^&;]&&?' \
|
_bg_proc_or_and_and='[^\\](\\\\)*&&?' \
|
||||||
_pipe_or_or_or='[^&|;]\|\|?' \
|
_pipe_or_or_or='[^\\](\\\\)*\|\|?' \
|
||||||
_semicolon_or_case_operators='[^;|];;?&?' \
|
_semicolon_or_case_operators='[^\\](\\\\)*;;?&?' \
|
||||||
_opening_paren='(^|[^[:alnum:]])\('
|
_opening_paren='(^|[^[:alnum:]\\])(\\\\)*\('
|
||||||
declare _control_op="^"
|
declare _control_op="^"
|
||||||
_control_op+="|$_newline"
|
_control_op+="|$_newline"
|
||||||
_control_op+="|$_bg_proc_or_and_and"
|
_control_op+="|$_bg_proc_or_and_and"
|
||||||
|
|
@ -190,11 +196,14 @@ _try_abbrev_internal () {
|
||||||
# set a variable defined in the calling context. This level is just appended to the identifier to make it unique
|
# set a variable defined in the calling context. This level is just appended to the identifier to make it unique
|
||||||
# across all the recursive calls.
|
# across all the recursive calls.
|
||||||
|
|
||||||
|
# IFS must be null so "${var[*]}" concatenates without space
|
||||||
|
declare -r IFS=
|
||||||
|
|
||||||
# If the self-insertion character is null (i.e. when pressing enter/accepting a line),
|
# If the self-insertion character is null (i.e. when pressing enter/accepting a line),
|
||||||
# then we also want to expand aliases if the cursor/point is *on* the word instead of just *after* the word.
|
# then we also want to expand aliases if the cursor/point is *on* the word instead of just *after* the word.
|
||||||
if [[ -z $self_insertion ]]; then
|
if [[ -z $self_insertion ]]; then
|
||||||
line_from_point=${line:point}
|
line_from_point=${line:point}
|
||||||
trailing_part_of_word=${line_from_point%%[<>&;()|[:space:]]*}
|
trailing_part_of_word=${line_from_point%%["${_bash_meta_chars[*]}"]*}
|
||||||
# Move point forward to right after the word. Keep mark where it is.
|
# Move point forward to right after the word. Keep mark where it is.
|
||||||
point=$((point+${#trailing_part_of_word}))
|
point=$((point+${#trailing_part_of_word}))
|
||||||
fi
|
fi
|
||||||
|
|
@ -202,13 +211,13 @@ _try_abbrev_internal () {
|
||||||
|
|
||||||
if
|
if
|
||||||
# Check if the character at point is a shell metacharacter or EOL
|
# Check if the character at point is a shell metacharacter or EOL
|
||||||
# [[ $point -eq ${#line} || "${line:point:1}" = [\<\>\&\;\(\)\|[:space:]] ]] &&
|
# [[ $point -eq ${#line} || "${line:point:1}" = ["${_bash_meta_chars[*]}"] ]] &&
|
||||||
# NOTE: Above line commented out, it's actually not that useful.
|
# NOTE: Above line commented out, it's actually not that useful.
|
||||||
|
|
||||||
# Determine word to complete
|
# Determine word to complete
|
||||||
# Uses the same word separators as bash (| & ; ( ) space tab newline) (aka "shell metacharacters")
|
# Uses the same word separators as bash (| & ; ( ) space tab newline) (aka "shell metacharacters")
|
||||||
# But < and > are ignored, since they always expect a file/delimiter/string afterwards and not a command
|
# But < and > are ignored, since they always expect a file/delimiter/string afterwards and not a command
|
||||||
declare -r word_at_point=${line_until_point##*[&;()|[:space:]]} &&
|
declare -r word_at_point=${line_until_point##*["${_bash_meta_chars_without_left_right_angle_brackets[*]}"]} &&
|
||||||
|
|
||||||
# Ensure that word does not contain either < or >
|
# Ensure that word does not contain either < or >
|
||||||
[[ ! $word_at_point =~ [\<\>] ]] &&
|
[[ ! $word_at_point =~ [\<\>] ]] &&
|
||||||
|
|
@ -260,7 +269,7 @@ _try_abbrev_internal () {
|
||||||
# Recursively expand replacement if we haven't expanded it already
|
# Recursively expand replacement if we haven't expanded it already
|
||||||
declare tmp=" $replacement" # Prepend space, makes the logic easier
|
declare tmp=" $replacement" # Prepend space, makes the logic easier
|
||||||
[[ $tmp =~ ^([[:blank:]]*$is_first_word_prefix) ]] && tmp=${tmp#"${BASH_REMATCH[1]}"} # Strip matching prefix
|
[[ $tmp =~ ^([[:blank:]]*$is_first_word_prefix) ]] && tmp=${tmp#"${BASH_REMATCH[1]}"} # Strip matching prefix
|
||||||
declare -r word_to_expand=${tmp%%[<>&;()|[:space:]]*}
|
declare -r word_to_expand=${tmp%%["${_bash_meta_chars[*]}"]*}
|
||||||
if [[ -z ${_expanded_aliases[$word_to_expand]-} ]]; then
|
if [[ -z ${_expanded_aliases[$word_to_expand]-} ]]; then
|
||||||
declare -r point_for_replacement=$((${#BASH_REMATCH[1]}+${#word_to_expand}))
|
declare -r point_for_replacement=$((${#BASH_REMATCH[1]}+${#word_to_expand}))
|
||||||
declare -g "ret_$level"
|
declare -g "ret_$level"
|
||||||
|
|
@ -307,20 +316,23 @@ _try_abbrev () {
|
||||||
_try_abbrev_internal "$READLINE_LINE" "$READLINE_POINT" "$READLINE_MARK" READLINE_LINE READLINE_POINT READLINE_MARK "$@"
|
_try_abbrev_internal "$READLINE_LINE" "$READLINE_POINT" "$READLINE_MARK" READLINE_LINE READLINE_POINT READLINE_MARK "$@"
|
||||||
|
|
||||||
# Don't pollute global namespace
|
# Don't pollute global namespace
|
||||||
unset -v _expanded_aliases
|
declare -gA _expanded_aliases=()
|
||||||
}
|
}
|
||||||
|
|
||||||
bash_abbr () {
|
bash_abbr () {
|
||||||
|
# IFS must be null so "${var[*]}" concatenates without space
|
||||||
|
declare -r IFS=
|
||||||
|
|
||||||
# From the bash man page:
|
# From the bash man page:
|
||||||
# metacharacter
|
# metacharacter
|
||||||
# A character that, when unquoted, separates words. One of the following:
|
# A character that, when unquoted, separates words. One of the following:
|
||||||
# | & ; ( ) < > space tab newline
|
# | & ; ( ) < > space tab newline
|
||||||
# Note: double quote should be escaped, if added to the list of metacharacters
|
# Note: double quote should be escaped, if added to the list of metacharacters
|
||||||
declare ch
|
declare ch
|
||||||
for ch in ' ' '|' '&' ';' '(' ')' '<' '>'; do
|
for ch in "${_bash_meta_chars_without_tab_and_nl[@]}"; do
|
||||||
bind -x '"\e\C-a'"$ch"'": _try_abbrev "'"$ch"'"'
|
bind_if -x '"\e\C-a'"$ch"'": _try_abbrev "'"$ch"'"'
|
||||||
bind '"'"$ch"'": "\e\C-a'"$ch"'"'
|
bind_if '"'"$ch"'": "\e\C-a'"$ch"'"'
|
||||||
# bind '"'"$ch"'": "\e\C-a'"$ch"'\e^"' # TODO fix. Moves point weirdly atm.
|
# bind_if '"'"$ch"'": "\e\C-a'"$ch"'\e^"' # TODO fix. Moves point weirdly atm.
|
||||||
done
|
done
|
||||||
|
|
||||||
# TODO To get it fully working in all edge cases (as far as we can parse),
|
# TODO To get it fully working in all edge cases (as far as we can parse),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue