summaryrefslogtreecommitdiff
path: root/Functions/Zle/select-word-match
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Zle/select-word-match')
-rw-r--r--Functions/Zle/select-word-match120
1 files changed, 120 insertions, 0 deletions
diff --git a/Functions/Zle/select-word-match b/Functions/Zle/select-word-match
new file mode 100644
index 000000000..8440852ab
--- /dev/null
+++ b/Functions/Zle/select-word-match
@@ -0,0 +1,120 @@
+# 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[ws-before-cursor] ]]; then
+ word=$matched_words[ws-before-cursor]
+ else
+ word=$matched_words[word-before-cursor]$matched_words[ws-before-cursor]
+ 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[ws-after-cursor] ]]; then
+ if [[ $style = i ]]; then
+ # just skip the whitespace
+ word=$matched_words[ws-after-cursor]
+ else
+ # skip the whitespace plus word
+ word=$matched_words[ws-after-cursor]$matched_words[word-after-cursor]
+ fi
+ else
+ if [[ $style = i ]]; then
+ # skip the word
+ word=$matched_words[word-after-cursor]
+ else
+ # skip word and following whitespace
+ word=$matched_words[word-after-cursor]$matched_words[ws-after-word]
+ fi
+ fi
+ (( CURSOR += ${#word} - 1 ))
+ else
+ match-words-by-style
+
+ if (( ${matched_words[is-word-start]} )); then
+ # 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[start]}"
+ fi
+
+ if [[ -n "${matched_words[ws-after-cursor]}" ]]; then
+ if [[ -n "${matched_words[ws-before-cursor]}" ]] || (( CURSOR == 0 )); then
+ # whitespace either side, select it
+ (( pos1 = CURSOR - ${#matched_words[ws-before-cursor]} ))
+ (( pos2 = CURSOR + ${#matched_words[ws-after-cursor]} ))
+ 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[word-after-cursor]} ))
+ fi
+
+ if [[ $style = a ]]; then
+ if [[ -n "${matched_words[ws-after-cursor]}" && ( -n "${matched_words[ws-before-cursor]}" || CURSOR -eq 0 ) ]]; then
+ # in the middle of whitespace so grab a word
+ if [[ -n "${matched_words[word-after-cursor]}" ]]; then
+ (( pos2 += ${#matched_words[word-after-cursor]} )) # preferably the one after
+ else
+ (( pos1 -= ${#matched_words[word-before-cursor]} )) # otherwise the one before
+ fi
+ elif [[ -n "${matched_words[ws-after-word]}" ]]; then
+ (( pos2 += ${#matched_words[ws-after-word]} ))
+ elif [[ -n "${matched_words[ws-before-cursor]}" ]]; then
+ # couldn't grab whitespace forwards so try backwards
+ (( pos1 -= ${#matched_words[ws-before-cursor]} ))
+ elif (( pos1 > 0 )); then
+ # There might have been whitespace before the word
+ (( CURSOR = pos1 ))
+ match-words-by-style
+ if [[ -n "${matched_words[ws-before-cursor]}" ]]; then
+ (( pos1 -= ${#matched_words[ws-before-cursor]} ))
+ 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