Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: replace-string function



Here's a replace-string (and replace-pattern) widget implemented in
terms of a read-from-minibuffer widget.  The latter uses the new
features of 4.1 for recursive editing and limiting the area of the
editing buffer, so provides a better `user experience' than was possible
in 4.0 --- read-from-minibuffer allows you to use all zle functions as
normal.

The replace-pattern variant was designed to have familiar-looking syntax
for inserting the original string and parenthesised subexpressions.  I'm
not claiming familiar is necessarily optimal, however.

There are other things that aren't optimal.  The problem with `undo' is
noted in the manual page.

Completion in the `minibuffer' hasn't been handled --- I think there's
some way of tweaking the context so that the completion system sees what
you want it to at that point, but I can't remember how.

Also, it doesn't perform replacements across the cursor position.  This
was the easiest way to ensure the cursor position was maintained if the
length changed.

Also, it ought to use a separate history, but actually refers to the
standard line history.  I'd like a mechanism which makes it easier to
provide alternative sources for the history in special contexts.  This
is very hairy at the moment, as I discovered with zcalc.

Index: Doc/Zsh/contrib.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/contrib.yo,v
retrieving revision 1.24
diff -u -r1.24 contrib.yo
--- Doc/Zsh/contrib.yo	27 Jan 2003 16:41:15 -0000	1.24
+++ Doc/Zsh/contrib.yo	3 Feb 2003 10:38:02 -0000
@@ -588,6 +588,58 @@
 bindkey '^X^Z' predict-on
 bindkey '^Z' predict-off)
 )
+tindex(read-from-minibuffer)
+item(tt(read-from-minibuffer))(
+This is most useful when called as a function from inside a widget, but will
+work correctly as a widget in its own right.  It prompts for a value
+below the current command line; a value may be input using all of the
+standard zle operations (and not merely the restricted set available
+when executing, for example, tt(execute-named-cmd)).  The value is then
+returned to the calling function in the parameter tt($REPLY) and the
+editing buffer restored to its previous state.  If the read was aborted
+by a keyboard break (typically tt(^G)), the function returns status 1
+and tt($REPLY) is not set.  If an argument is supplied to the function
+it is taken as a prompt, otherwise `tt(? )' is used.
+
+The name is a slight misnomer, as in fact the shell's own minibuffer is
+not used.  Hence it is still possible to call tt(executed-named-cmd) and
+similar functions while reading a value.
+)
+tindex(replace-string)
+tindex(replace-pattern)
+item(tt(replace-string), tt(replace-pattern))(
+The function tt(replace-string) implements two widgets.
+If defined under the same name as the function, it prompts for two
+strings; the first (source) string will be replaced by the second
+everywhere it occurs in the line editing buffer.
+
+If the widget name contains the word `tt(pattern)', for example by
+defining the widget using the command `tt(zle -N replace-pattern
+replace-string)', then the replacement is done by pattern matching.  All
+zsh extended globbing patterns can be used in the source string; note
+that unlike filename generation the pattern does not need to match an
+entire word, nor do glob qualifiers have any effect.  In addition, the
+replacement string can contain parameter or command substitutions.
+Furthermore, a `tt(&)' in the replacement string will be replaced with
+the matched source string, and a backquoted digit `tt(\)var(N)' will be
+replaced by the var(N)th parenthesised expression matched.  The form
+`tt(\{)var(N)tt(})' may be used to protect the digit from following
+digits.
+
+For example, starting from the line:
+
+example(print This line contains fan and fond)
+
+and invoking tt(replace-pattern) with the source string `tt(f(?)n)' and
+the replacment string `tt(c\1r)' produces the not very useful line:
+
+example(print This line contains car and cord)
+
+The range of the replacement string can be limited by using the
+tt(narrow-to-region-invisible) widget.  One limitation of the current
+version is that tt(undo) will cycle through changes to the replacement
+and source strings before undoing the replacement itself.
+)
 tindex(smart-insert-last-word)
 item(tt(smart-insert-last-word))(
 This function may replace the tt(insert-last-word) widget, like so:
Index: Functions/Zle/read-from-minibuffer
===================================================================
RCS file: Functions/Zle/read-from-minibuffer
diff -N Functions/Zle/read-from-minibuffer
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Functions/Zle/read-from-minibuffer	3 Feb 2003 10:38:02 -0000
@@ -0,0 +1,20 @@
+local savelbuffer=$LBUFFER saverbuffer=$RBUFFER
+local savepredisplay=$PREDISPLAY savepostdisplay=$POSTDISPLAY
+
+LBUFFER=
+RBUFFER=
+PREDISPLAY="$PREDISPLAY$savelbuffer$saverbuffer$POSTDISPLAY
+${1:-? }"
+POSTDISPLAY=
+
+zle recursive-edit
+integer stat=$?
+
+(( stat )) || REPLY=$BUFFER
+
+LBUFFER=$savelbuffer
+RBUFFER=$saverbuffer
+PREDISPLAY=$savepredisplay
+POSTDISPLAY=$savepostdisplay
+
+return $stat
Index: Functions/Zle/replace-string
===================================================================
RCS file: Functions/Zle/replace-string
diff -N Functions/Zle/replace-string
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Functions/Zle/replace-string	3 Feb 2003 10:38:02 -0000
@@ -0,0 +1,45 @@
+emulate -L zsh
+setopt extendedglob
+
+autoload read-from-minibuffer
+
+local p1="Replace: " p2="   with: "
+local src rep REPLY MATCH MBEGIN MEND curwidget=$WIDGET
+local -a match mbegin mend
+
+read-from-minibuffer $p1 || return 1
+src=$REPLY
+
+read-from-minibuffer "$p1$src$p2" || return 1
+rep=$REPLY
+
+if [[ $curwidget = *pattern* ]]; then
+    local rep2
+    # The following horror is so that an & preceded by an even
+    # number of backslashes is active, without stripping backslashes,
+    # while preceded by an odd number of backslashes is inactive,
+    # with one backslash being stripped.  A similar logic applies
+    # to \digit.
+    while [[ $rep = (#b)([^\\]#)(\\\\)#(\\|)(\&|\\<->|\\\{<->\})(*) ]]; do
+	if [[ -n $match[3] ]]; then
+	    # Expression is quoted, strip quotes
+	    rep2="${match[1]}${match[2]}${match[4]}"
+	else
+	    rep2+="${match[1]}${match[2]}"
+	    if [[ $match[4] = \& ]]; then
+		rep2+='${MATCH}'
+	    elif [[ $match[4] = \\\{* ]]; then
+		rep2+='${match['${match[4][3,-2]}']}'
+	    else
+		rep2+='${match['${match[4][2,-1]}']}'
+	    fi
+	fi
+	rep=${match[5]}
+    done
+    rep2+=$rep
+    LBUFFER=${LBUFFER//(#bm)$~src/${(e)rep2}}
+    RBUFFER=${RBUFFER//(#bm)$~src/${(e)rep2}}
+else
+    LBUFFER=${LBUFFER//$src/$rep}
+    RBUFFER=${RBUFFER//$src/$rep}
+fi

-- 
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070


**********************************************************************
The information transmitted is intended only for the person or
entity to which it is addressed and may contain confidential 
and/or privileged material. 
Any review, retransmission, dissemination or other use of, or
taking of any action in reliance upon, this information by 
persons or entities other than the intended recipient is 
prohibited.  
If you received this in error, please contact the sender and 
delete the material from any computer.
**********************************************************************



Messages sorted by: Reverse Date, Date, Thread, Author