1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-09 19:51:26 +01:00

users/11083: functions for handling shell arguments in ZLE

This commit is contained in:
Peter Stephenson 2006-12-15 11:44:26 +00:00
parent d8e36bffa2
commit 937dc9a84d
5 changed files with 193 additions and 19 deletions

View file

@ -1,3 +1,10 @@
2006-12-15 Peter Stephenson <pws@csr.com>
* users/11083: Doc/Zsh/contrib.yo, Functions/Zle/.distfiles,
Functions/modify-current-argument,
Functions/split-shell-arguments: functions for handling spliting
of shell arguments in ZLE.
2006-12-13 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 23052: Doc/Zsh/builtins.yo, Src/subst.c: multibyte

View file

@ -1142,6 +1142,60 @@ investigate the command word found. The default is tt(whence -c).
)
enditem()
subsect(Utility Functions)
These functions are useful in constructing widgets. They
should be loaded with `tt(autoload -U) var(function)' and called
as indicated from user-defined widgets.
startitem()
tindex(split-shell-arguments)
item(tt(split-shell-arguments))(
This function splits the line currently being edited into shell arguments
and whitespace. The result is stored in the array tt(reply). The array
contains all the parts of the line in order, starting with any whitespace
before the first argument, and finishing with any whitespace after the last
argument. Hence (so long as the option tt(KSH_ARRAYS) is not set)
whitespace is given by odd indices in the array and arguments by
even indices. Note that no stripping of quotes is done; joining together
all the elements of tt(reply) in order is guaranteed to produce the
original line.
The parameter tt(REPLY) is set to the index of the word in tt(reply) which
contains the character after the cursor, where the first element has index
1. The parameter tt(REPLY2) is set to the index of the character under the
cursor in that word, where the first character has index 1.
Hence tt(reply), tt(REPLY) and tt(REPLY2) should all be made local to
the enclosing function.
See the function tt(modify-current-argument), described below, for
an example of how to call this function.
)
tindex(modify-current-argument)
item(tt(modify-current-argument) var(expr-using-)tt($ARG))(
This function provides a simple method of allowing user-defined widgets
to modify the command line argument under the cursor (or immediately to the
left of the cursor if the cursor is between arguments). The argument
should be an expression which when evaluated operates on the shell
parameter tt(ARG), which will have been set to the command line argument
under the cursor. The expression should be suitably quoted to prevent
it being evaluated too early.
For example, a user-defined widget containing the following code
converts the characters in the argument under the cursor into all upper
case:
example(modify-current-word '${(U)ARG}')
The following strips any quoting from the current word (whether backslashes
or one of the styles of quotes), and replaces it with single quoting
throughout:
example(modify-current-word '${(qq)${(Q)ARG}}')
)
enditem()
subsect(Styles)
The behavior of several of the above widgets can be controlled by the use

View file

@ -1,24 +1,28 @@
DISTFILES_SRC='
.distfiles
backward-kill-word-match backward-word-match
capitalize-word-match copy-earlier-word
cycle-completion-positions define-composed-chars
delete-whole-word-match down-case-word-match
down-line-or-beginning-search edit-command-line
forward-word-match history-beginning-search-menu
history-pattern-search history-search-end
incarg incremental-complete-word
insert-composed-char insert-files
insert-unicode-char keeper
keymap+widget kill-word-match
backward-kill-word-match backward-word-match
capitalize-word-match copy-earlier-word
cycle-completion-positions define-composed-chars
delete-whole-word-match down-case-word-match
down-line-or-beginning-search edit-command-line
forward-word-match history-beginning-search-menu
history-pattern-search history-search-end
incarg incremental-complete-word
insert-composed-char insert-files
insert-unicode-char keeper
keymap+widget kill-word-match
match-word-context
match-words-by-style narrow-to-region
narrow-to-region-invisible predict-on
quote-and-complete-word read-from-minibuffer
replace-string select-word-style
match-words-by-style
modify-current-argument
narrow-to-region
narrow-to-region-invisible predict-on
quote-and-complete-word read-from-minibuffer
replace-string select-word-style
replace-string-again
smart-insert-last-word transpose-words-match
up-case-word-match up-line-or-beginning-search
url-quote-magic which-command
zed-set-file-name
smart-insert-last-word
split-shell-arguments
transpose-words-match
up-case-word-match up-line-or-beginning-search
url-quote-magic which-command
zed-set-file-name
'

View file

@ -0,0 +1,51 @@
# 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 ))

View file

@ -0,0 +1,58 @@
# Split a command line into shell arguments and whitespace in $reply.
# Odd elements (starting from 1) are whitespace, even elements
# are shell arguments (possibly quoted strings). Whitespace at
# start and end is always included in the array but may be an empty string.
# $REPLY holds NO_KSH_ARRAYS index of current word in $reply.
# $REPLY2 holds NO_KSH_ARRAYS index of current character in current word.
# Hence ${reply[$REPLY][$REPLY2]} is the character under the cursor.
#
# reply, REPLY, REPLY2 should therefore be local to the enclosing function.
#
# The following formula replaces the current shell word, or previous word
# if the cursor is on whitespace, by uppercasing all characters.
emulate -L zsh
setopt extendedglob
local -a bufwords lbufwords
local word
integer pos=1 cpos=$((CURSOR+1)) opos iword ichar
bufwords=(${(z)BUFFER})
reply=()
while [[ ${BUFFER[pos]} = [[:space:]] ]]; do
(( pos++ ))
done
reply+=${BUFFER[1,pos-1]}
(( cpos < pos )) && (( iword = 1, ichar = cpos ))
for word in "${bufwords[@]}"; do
(( opos = pos ))
(( pos += ${#word} ))
reply+=("$word")
if (( iword == 0 && cpos < pos )); then
(( iword = ${#reply} ))
(( ichar = cpos - opos + 1 ))
fi
(( opos = pos ))
while [[ ${BUFFER[pos]} = [[:space:]] ]]; do
(( pos++ ))
done
reply+=("${BUFFER[opos,pos-1]}")
if (( iword == 0 && cpos < pos )); then
(( iword = ${#reply} ))
(( ichar = cpos - opos + 1 ))
fi
done
if (( iword == 0 )); then
# At the end of the line, so off the indexable positions
# (but still a valid cursor position).
(( REPLY = ${#reply} ))
(( REPLY2 = 1 ))
else
(( REPLY = iword ))
(( REPLY2 = ichar ))
fi