From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1879 invoked from network); 14 Dec 2006 11:29:02 -0000 X-Spam-Checker-Version: SpamAssassin 3.1.7 (2006-10-05) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=AWL,BAYES_00, FORGED_RCVD_HELO autolearn=ham version=3.1.7 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 14 Dec 2006 11:29:02 -0000 Received-SPF: none (ns1.primenet.com.au: domain at sunsite.dk does not designate permitted sender hosts) Received: (qmail 40495 invoked from network); 14 Dec 2006 11:28:52 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 14 Dec 2006 11:28:52 -0000 Received: (qmail 20046 invoked by alias); 14 Dec 2006 11:28:38 -0000 Mailing-List: contact zsh-users-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 11083 Received: (qmail 20037 invoked from network); 14 Dec 2006 11:28:36 -0000 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by sunsite.dk with SMTP; 14 Dec 2006 11:28:36 -0000 Received: (qmail 39080 invoked from network); 14 Dec 2006 11:28:36 -0000 Received: from cluster-c.mailcontrol.com (168.143.177.190) by a.mx.sunsite.dk with SMTP; 14 Dec 2006 11:28:32 -0000 Received: from cameurexb01.EUROPE.ROOT.PRI ([62.189.241.200]) by rly07c.srv.mailcontrol.com (MailControl) with ESMTP id kBEBR4Dd008714 for ; Thu, 14 Dec 2006 11:28:21 GMT Received: from news01.csr.com ([10.103.143.38]) by cameurexb01.EUROPE.ROOT.PRI with Microsoft SMTPSVC(6.0.3790.1830); Thu, 14 Dec 2006 11:22:23 +0000 Date: Thu, 14 Dec 2006 11:22:21 +0000 From: Peter Stephenson To: zsh-users@sunsite.dk Subject: PATCH: argument splitting (was Re: zsh widget to resolve symlinks) Message-Id: <20061214112221.067c5af0.pws@csr.com> In-Reply-To: <200612101756.kBAHuDLN003968@pwslaptop.csr.com> References: <457B55D3.9030003@mikelward.com> <200612101756.kBAHuDLN003968@pwslaptop.csr.com> Organization: Cambridge Silicon Radio X-Mailer: Sylpheed version 2.2.10 (GTK+ 2.10.4; i386-redhat-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-OriginalArrivalTime: 14 Dec 2006 11:22:23.0289 (UTC) FILETIME=[205A9A90:01C71F72] X-Scanned-By: MailControl A-07-06-00 (www.mailcontrol.com) on 10.67.0.117 Peter Stephenson wrote: > It occurred to me recently that a generic widget to split the line into > shell words and whitespace would be extremely useful, making this sort > of task much easier. However, I haven't got around to it yet. Here it is, with a handy example function that makes some simple replacements on the current shell argument very easy. I'll commit it on the main line, but I believe the functions here should run fine in released versions of 4.2. The original problem should now reduce to the following code: autoload -U modify-current-argument modify-current-argument '${(q)$(eval realpath "$ARG")}' The eval and the (q) are there to try to be careful about quoting, use of ~'s, etc. (Some of the other documentation for ZLE functions in the subsection "Widgets" should really go into "Utility Functions", too.) Index: Doc/Zsh/contrib.yo =================================================================== RCS file: /cvsroot/zsh/zsh/Doc/Zsh/contrib.yo,v retrieving revision 1.64 diff -u -r1.64 contrib.yo --- Doc/Zsh/contrib.yo 10 Oct 2006 11:24:38 -0000 1.64 +++ Doc/Zsh/contrib.yo 14 Dec 2006 11:07:01 -0000 @@ -1142,6 +1142,60 @@ ) 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 Index: Functions/Zle/modify-current-argument =================================================================== RCS file: Functions/Zle/modify-current-argument diff -N Functions/Zle/modify-current-argument --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Functions/Zle/modify-current-argument 14 Dec 2006 11:07:02 -0000 @@ -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 )) Index: Functions/Zle/split-shell-arguments =================================================================== RCS file: Functions/Zle/split-shell-arguments diff -N Functions/Zle/split-shell-arguments --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Functions/Zle/split-shell-arguments 14 Dec 2006 11:07:02 -0000 @@ -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 -- Peter Stephenson Software Engineer CSR PLC, Churchill House, Cambridge Business Park, Cowley Road Cambridge, CB4 0WZ, UK Tel: +44 (0)1223 692070 To access the latest news from CSR copy this link into a web browser: http://www.csr.com/email_sig.php