# Take an expression suitable for interpolation in double quotes that # performs a replacement on the parameter "ARG". Replaces the # shell argument (which may be a quoted string) under or before the # cursor with that. Ensure the expression is suitable quoted. # # For example, to uppercase the entire shell argument: # modify-current-word '${(U)ARG}' # To strip the current quoting from the word (whether backslashes or # single, double or dollar quotes) and use single quotes instead: # modify-current-word '${(qq)${(Q)ARG}}' # Retain most options from the calling function for the eval. # Reset some that might confuse things. setopt localoptions noksharrays multibyte local -a reply integer REPLY REPLY2 autoload -U split-shell-arguments split-shell-arguments # Can't do this unless there's some text under or left of us. (( REPLY < 2 )) && return 1 # Get the index of the word we want. if (( REPLY & 1 )); then # Odd position; need previous word. (( REPLY-- )) # Pretend position was just after the end of it. (( REPLY2 = ${#reply[REPLY]} + 1 )) fi # Length of all characters before current. # Force use of character (not index) counting and join without IFS. integer wordoff="${(cj..)#reply[1,REPLY-1]}" # Replacement for current word. This could do anything to ${reply[REPLY]}. local ARG="${reply[REPLY]}" repl eval repl=\"$1\" # New line: all words before and after current word, with # no additional spaces since we've already got the whitespace # and the replacement word in the middle. BUFFER="${(j..)reply[1,REPLY-1]}${repl}${(j..)reply[REPLY+1,-1]}" # Keep cursor at same position in replaced word. # Redundant here, but useful if $repl changes the length. # Limit to the next position after the end of the word. integer repmax=$(( ${#repl} + 1 )) # Remember CURSOR starts from offset 0 for some reason, so # subtract 1 from positions. (( CURSOR = wordoff + (REPLY2 > repmax ? repmax : REPLY2) - 1 ))