# Select the entire word around the cursor. Intended for use as # a vim-style text object in vi mode but with customisable # word boundaries. # # For example: # autoload -U select-word-match # zle -N select-in-camel select-word-match # bindkey -M viopp ic select-in-camel # zstyle ':zle:*-camel' word-style normal-subword emulate -L zsh setopt extendedglob local curcontext=:zle:$WIDGET local -a matched_words # Start and end of range of characters integer pos1 pos2 num=${NUMERIC:-1} local style word # choose between inner word or a word style of widget for style in $1 ${${WIDGET#*-}[1]} $KEYS[1] "i"; do [[ $style = [ai] ]] && break done autoload -Uz match-words-by-style while (( num-- )); do if (( MARK > CURSOR )); then # if cursor is at the start of the selection, just move back a word match-words-by-style if [[ $style = i && -n $matched_words[3] ]]; then word=$matched_words[3] else word=$matched_words[2]$matched_words[3] fi if [[ -n $word ]]; then (( CURSOR -= ${#word} )) else return 1 fi elif (( MARK >= 0 && MARK < CURSOR )); then # cursor at the end, move forward a word (( CURSOR+1 == $#BUFFER )) && return 1 (( CURSOR++ )) match-words-by-style if [[ -n $matched_words[4] ]]; then if [[ $style = i ]]; then # just skip the whitespace word=$matched_words[4] else # skip the whitespace plus word word=$matched_words[4]$matched_words[5] fi else if [[ $style = i ]]; then # skip the word word=$matched_words[5] else # skip word and following whitespace word=$matched_words[5]$matched_words[6] fi fi (( CURSOR += ${#word} - 1 )) else match-words-by-style if [[ -n "${matched_words[3]}" ]]; then # There's whitespace before the cursor, so the word we are selecting # starts at the cursor position. pos1=$CURSOR else # No whitespace before us, so select any wordcharacters there. pos1="${#matched_words[1]}" fi if [[ -n "${matched_words[4]}" ]]; then if [[ -n "${matched_words[3]}" ]] || (( CURSOR == 0 )); then # whitespace either side, select it (( pos1 = CURSOR - ${#matched_words[3]} )) (( pos2 = CURSOR + ${#matched_words[4]} )) else # There's whitespace at the cursor position, so only select # up to the cursor position. (( pos2 = CURSOR + 1 )) fi else # No whitespace at the cursor position, so select the # current character and any following wordcharacters. (( pos2 = CURSOR + ${#matched_words[5]} )) fi if [[ $style = a ]]; then if [[ -n "${matched_words[4]}" && ( -n "${matched_words[3]}" || CURSOR -eq 0 ) ]]; then # in the middle of whitespace so grab a word if [[ -n "${matched_words[5]}" ]]; then (( pos2 += ${#matched_words[5]} )) # preferably the one after else (( pos1 -= ${#matched_words[2]} )) # otherwise the one before fi elif [[ -n "${matched_words[6]}" ]]; then (( pos2 += ${#matched_words[6]} )) elif [[ -n "${matched_words[3]}" ]]; then # couldn't grab whitespace forwards so try backwards (( pos1 -= ${#matched_words[3]} )) elif (( pos1 > 0 )); then # There might have been whitespace before the word (( CURSOR = pos1 )) match-words-by-style if [[ -n "${matched_words[3]}" ]]; then (( pos1 -= ${#matched_words[3]} )) fi fi fi (( MARK = pos1, CURSOR = pos2-1 )) fi done if [[ $KEYMAP == vicmd ]] && (( !REGION_ACTIVE )); then (( CURSOR++ )) # Need to include cursor position for operators fi