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.
|
||||
# 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
|
||||
# and to increase readability.
|
||||
_control_op () {
|
||||
declare -r _newline=$'\n' \
|
||||
_bg_proc_or_and_and='[^&;]&&?' \
|
||||
_pipe_or_or_or='[^&|;]\|\|?' \
|
||||
_semicolon_or_case_operators='[^;|];;?&?' \
|
||||
_opening_paren='(^|[^[:alnum:]])\('
|
||||
_bg_proc_or_and_and='[^\\](\\\\)*&&?' \
|
||||
_pipe_or_or_or='[^\\](\\\\)*\|\|?' \
|
||||
_semicolon_or_case_operators='[^\\](\\\\)*;;?&?' \
|
||||
_opening_paren='(^|[^[:alnum:]\\])(\\\\)*\('
|
||||
declare _control_op="^"
|
||||
_control_op+="|$_newline"
|
||||
_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
|
||||
# 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),
|
||||
# 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
|
||||
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.
|
||||
point=$((point+${#trailing_part_of_word}))
|
||||
fi
|
||||
|
|
@ -202,13 +211,13 @@ _try_abbrev_internal () {
|
|||
|
||||
if
|
||||
# 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.
|
||||
|
||||
# Determine word to complete
|
||||
# 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
|
||||
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 >
|
||||
[[ ! $word_at_point =~ [\<\>] ]] &&
|
||||
|
|
@ -260,7 +269,7 @@ _try_abbrev_internal () {
|
|||
# Recursively expand replacement if we haven't expanded it already
|
||||
declare tmp=" $replacement" # Prepend space, makes the logic easier
|
||||
[[ $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
|
||||
declare -r point_for_replacement=$((${#BASH_REMATCH[1]}+${#word_to_expand}))
|
||||
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 "$@"
|
||||
|
||||
# Don't pollute global namespace
|
||||
unset -v _expanded_aliases
|
||||
declare -gA _expanded_aliases=()
|
||||
}
|
||||
|
||||
bash_abbr () {
|
||||
# IFS must be null so "${var[*]}" concatenates without space
|
||||
declare -r IFS=
|
||||
|
||||
# From the bash man page:
|
||||
# metacharacter
|
||||
# A character that, when unquoted, separates words. One of the following:
|
||||
# | & ; ( ) < > space tab newline
|
||||
# Note: double quote should be escaped, if added to the list of metacharacters
|
||||
declare ch
|
||||
for ch in ' ' '|' '&' ';' '(' ')' '<' '>'; do
|
||||
bind -x '"\e\C-a'"$ch"'": _try_abbrev "'"$ch"'"'
|
||||
bind '"'"$ch"'": "\e\C-a'"$ch"'"'
|
||||
# bind '"'"$ch"'": "\e\C-a'"$ch"'\e^"' # TODO fix. Moves point weirdly atm.
|
||||
for ch in "${_bash_meta_chars_without_tab_and_nl[@]}"; do
|
||||
bind_if -x '"\e\C-a'"$ch"'": _try_abbrev "'"$ch"'"'
|
||||
bind_if '"'"$ch"'": "\e\C-a'"$ch"'"'
|
||||
# bind_if '"'"$ch"'": "\e\C-a'"$ch"'\e^"' # TODO fix. Moves point weirdly atm.
|
||||
done
|
||||
|
||||
# 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