summary refs log tree commit diff
path: root/Functions
diff options
context:
space:
mode:
Diffstat (limited to 'Functions')
-rw-r--r--Functions/Zle/select-word-match121
1 files changed, 121 insertions, 0 deletions
diff --git a/Functions/Zle/select-word-match b/Functions/Zle/select-word-match
new file mode 100644
index 000000000..24620c995
--- /dev/null
+++ b/Functions/Zle/select-word-match
@@ -0,0 +1,121 @@
+# 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