summaryrefslogtreecommitdiff
path: root/Functions/Zle
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Zle')
-rw-r--r--Functions/Zle/.distfiles1
-rw-r--r--Functions/Zle/modify-current-argument44
-rw-r--r--Functions/Zle/send-invisible85
-rw-r--r--Functions/Zle/split-shell-arguments2
4 files changed, 121 insertions, 11 deletions
diff --git a/Functions/Zle/.distfiles b/Functions/Zle/.distfiles
index 2ec4adc22..22eef1e6e 100644
--- a/Functions/Zle/.distfiles
+++ b/Functions/Zle/.distfiles
@@ -33,6 +33,7 @@ read-from-minibuffer
replace-string
replace-string-again
select-word-style
+send-invisible
smart-insert-last-word
split-shell-arguments
transpose-lines
diff --git a/Functions/Zle/modify-current-argument b/Functions/Zle/modify-current-argument
index 92ca7bdab..92851d600 100644
--- a/Functions/Zle/modify-current-argument
+++ b/Functions/Zle/modify-current-argument
@@ -14,7 +14,7 @@
setopt localoptions noksharrays multibyte
local -a reply
-integer REPLY REPLY2
+integer REPLY REPLY2 fromend endoffset
autoload -Uz split-shell-arguments
split-shell-arguments
@@ -30,6 +30,13 @@ if (( REPLY & 1 )); then
(( REPLY2 = ${#reply[REPLY]} + 1 ))
fi
+# Work out offset from end of string
+(( fromend = $REPLY2 - ${#reply[REPLY]} - 1 ))
+if (( fromend >= -1 )); then
+ # Cursor is near the end of the word, we'll try to keep it there.
+ endoffset=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]}"
@@ -37,15 +44,32 @@ 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\"
+
+if (( !endoffset )) && [[ ${repl[fromend,-1]} = ${ARG[fromend,-1]} ]]; then
+ # If the part of the string from here to the end hasn't changed,
+ # leave the cursor this distance from the end instead of the beginning.
+ endoffset=1
+fi
+
# 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 ))
+local left="${(j..)reply[1,REPLY-1]}${repl}"
+local right="${(j..)reply[REPLY+1,-1]}"
+
+if [[ endoffset -ne 0 && ${#repl} -ne 0 ]]; then
+ # Place cursor relative to end.
+ LBUFFER="$left"
+ RBUFFER="$right"
+ (( CURSOR += fromend ))
+else
+ BUFFER="$left$right"
+
+ # 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 ))
+fi
diff --git a/Functions/Zle/send-invisible b/Functions/Zle/send-invisible
new file mode 100644
index 000000000..d31fc76ae
--- /dev/null
+++ b/Functions/Zle/send-invisible
@@ -0,0 +1,85 @@
+#autoload
+
+# send-invisible reads a line from the terminal, displaying an
+# asterisk for each character typed. It stores the line in the
+# global variable INVISIBLE, and when finished reading, inserts
+# the string ${INVISIBLE} into the original command buffer.
+
+# If one argument is given, it is the prompt. The default is
+# "Non-echoed text: "
+
+# If two or three arguments are given, they form the prefix and
+# suffix of the inserted INVISIBLE. Defaults are '${' and '}'
+# but these can be replaced, for example with '"${' and '}"' to
+# enclose the whole word in double quotes, or '${(z)' and '}' to
+# split the value of $INVISIBLE like the shell parser.
+
+# To use:
+# autoload -Uz send-invisible
+# zle -N send-invisible
+# bindkey '^X ' send-invisible
+# Or more elaborately:
+# hidden-command() { send-invisible '% ' '${(z)' '}' }
+# zle -N hidden-command
+# bindkey '^X%' hidden-command
+
+# Shamelessly cribbed from read-from-minibuffer.
+
+emulate -L zsh
+
+# Hide the value of INVISIBLE in any output of set and typeset
+typeset -g -H INVISIBLE=
+
+local pretext="$PREDISPLAY$LBUFFER$RBUFFER$POSTDISPLAY"$'\n'
+
+# Can't directly make these locals because send-invisible is
+# called as a widget, so these special variables are already
+# local at the current level and wouldn't get restored
+local save_lbuffer=$LBUFFER
+local save_rbuffer=$RBUFFER
+local save_predisplay=$PREDISPLAY
+local save_postdisplay=$POSTDISPLAY
+local -a save_region_highlight
+save_region_highlight=("${region_highlight[@]}")
+
+{
+ local lb rb opn=${2:-'${'} cls=${3:-'}'}
+ LBUFFER=
+ RBUFFER=
+ PREDISPLAY="$pretext${1:-Non-echoed text: }"
+ POSTDISPLAY=
+ region_highlight=("P${(m)#pretext} ${(m)#PREDISPLAY} bold")
+
+ while zle -R && zle .read-command
+ do
+ # There are probably more commands that should go into
+ # the first clause here to harmlessly beep, because ...
+ case $REPLY in
+ (send-invisible|run-help|undefined-key|where-is|which-command)
+ zle .beep;;
+ (push-*|send-break) INVISIBLE=;&
+ (accept-*) break;;
+ (*)
+ LBUFFER=$lb
+ RBUFFER=$rb
+ zle $REPLY # ... this could expose something
+ lb=$LBUFFER
+ rb=$RBUFFER
+ INVISIBLE=$BUFFER
+ LBUFFER=${(l:$#LBUFFER::*:):-}
+ RBUFFER=${(l:$#RBUFFER::*:):-}
+ ;;
+ esac
+ done
+} always {
+ LBUFFER=$save_lbuffer
+ RBUFFER=$save_rbuffer
+ PREDISPLAY=$save_predisplay
+ POSTDISPLAY=$save_postdisplay
+ region_highlight=("${save_region_highlight[@]}")
+ zle -R
+
+ # Now that the highlight has been restored with all the old
+ # text and cursor positioning, insert the new text.
+ LBUFFER+=${INVISIBLE:+${opn}INVISIBLE${cls}}
+}
diff --git a/Functions/Zle/split-shell-arguments b/Functions/Zle/split-shell-arguments
index ee737a067..32b04fcb5 100644
--- a/Functions/Zle/split-shell-arguments
+++ b/Functions/Zle/split-shell-arguments
@@ -15,7 +15,7 @@ local -a bufwords lbufwords
local word
integer pos=1 cpos=$((CURSOR+1)) opos iword ichar
-bufwords=(${(z)BUFFER})
+bufwords=(${(Z+n+)BUFFER})
reply=()
while [[ ${BUFFER[pos]} = [[:space:]] ]]; do