diff --git a/bash-abbr b/bash-abbr index a0c8b78..68dcb90 100644 --- a/bash-abbr +++ b/bash-abbr @@ -10,6 +10,8 @@ # WARNING 1: This plugin creates and overwrites various readline 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) +# WARNING 2: This plugin needs and enables extended globbing. +shopt -s extglob # Regexes for heuristically determining whether we're abbreviating the first word. # Construction put into functions to prevent global namespace pollution @@ -82,18 +84,28 @@ _try_abbrev_internal () { # Recursively perform abbrevation on the line defined by $1, taking $2 as the point and $3 as the mark. # Set the vars defined by $4, $5 and $6 to the updated values (line, point and mark). # $7 is the character/string to self-insert. - declare -r line=$1 point=$2 mark=$3 new_line=$4 new_point=$5 new_mark=$6 \ - level=${#FUNCNAME[@]} + declare -r line=$1 mark=$3 new_line=$4 new_point=$5 new_mark=$6 \ + level=${#FUNCNAME[@]} + declare point=$2 declare self_insertion=$7 # `level` is needed to keep track of the recursion, because we don't have a return value but rather # 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. - declare -r line_until_point=${line::point} + # 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} + line_from_point_without_partial_word=${line_from_point##*([^<>&;()|[:space:]])} # extglob must be enabled! + # Move point forward to right after the word. Keep mark where it is. + point=${#line}-${#line_from_point_without_partial_word} + fi + declare -r point line_until_point=${line::point} 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}" = [\<\>\&\;\(\)\|[:space:]] ]] && + # 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") @@ -141,7 +153,7 @@ _try_abbrev_internal () { declare tmp=" $replacement" [[ $tmp =~ ^([[:space:]]*$is_first_word_prefix) ]] && tmp=${tmp#"${BASH_REMATCH[1]}"} declare -r word_to_expand=${tmp%%[<>&;()|[:space:]]*} - if [[ ! ${_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 -g "ret_$level" declare -n recursively_expanded_replacement="ret_$level" @@ -149,7 +161,7 @@ _try_abbrev_internal () { replacement=${recursively_expanded_replacement# } fi # Eval if it has a trailing exclamation point and strip trailing \n and \r - [[ $eval_expansion = true ]] && replacement=$(eval "${replacement%!}") + "$eval_expansion" && replacement=$(eval "${replacement%!}") # Calculate point and mark offset. declare -ir offset=${#replacement}-${#word_at_point}+${#self_insertion} @@ -164,8 +176,8 @@ _try_abbrev_internal () { eval "$new_line"'=${line_until_point}${self_insertion}${line:point}' fi - # Advance mark if it's right of (or on) the point. - [[ $mark && $mark -ge "$point" ]] && eval "$new_mark"'=$((mark+offset))' + # Advance mark if it's set and right of (or on) the point. + [[ $mark && mark -ge point ]] && eval "$new_mark"'=$((mark+offset))' # Advance point. eval "$new_point"'=$((point+offset))'