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

PATCH v2 (complete): Implement zle -P


On Fri, Dec 31, 2010 at 9:28 AM, Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
> On Dec 30,  1:59pm, Wayne Davison wrote:
> }
> } I've coded up a patch that adds the -y option to zle so that I can define a
> } "zle -Ny yank" widget (as well as yank-pop), which marks it as being a
> } ZLE_YANK equivalent.
> This sort of follows from "zle -C".  For completion widgets, it was
> recognized that there are a set of behaviors which are the model for
> any newly-added widget, and the builtin completion widgets provide
> names for those behaviors.
> Perhaps a general solution would be that for *any* widget, not just
> a completion one, it should be possible to name a "prototype" widget
> whose behavior the new widget is intended to simulate or replace.
> "zle -P WIDGET PROTOTYPE-WIDGET [FUNCTION]" where "zle -N ..." becomes
> a special case equivalent to "zle -P WIDGET self-insert FUNCTION",
> or something to that effect (or maybe undefined-key instead).
> Then one could write, e.g.,
>   zle -P history-search-sideways history-incremental-search-forward
> and zle would "know" to invoke the minibuffer and re-call the function
> as each new keystroke is typed, without that needing to be coded as a
> loop in the widget itself.
> Then the question would be whether we still need "zle -C ..." other
> than for backward compatibility.
> The only other solution to this that I've thought of would be to follow
> the example of the auto-suffix-remove and auto-suffix-retain widgets,
> that is, have a special widget whose only effect is to have the side-
> effect of enabling yank-pop on the next interaction: yank-pop-enable
> perhaps (is yank-pop-disable ever needed?).

Here's a more complete patch for this. sorry about sending a bunch of
incremental patches, I seem to tend to do that.

On Mon, Aug 31, 2015 at 7:11 PM, Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx> wrote:
> It's not entirely clear what this does without documentation, but as the
> flags are a bit obcsure anyway, it might not be all that clear how to
> document it, either.

Indeed, I'm not sure if a user can figure out which widgets are useful
to override in this way.  Would it be more useful to just do what Wayne
did originally and let zle -N take some flags that specify which specific
flags the new widget should have after all? The advantage of this method
is that if we add more flags, users can wrap those widgets without us
remembering to add a new zle -N flag as well.

I replaced all of my zle -C with zle -P and my custom completion widgets
work the same, so that's a good sign at least.

The following are the usecases from the other mails to centralize.

I can now do this (which is why I originally found this old thread, if
anyone was curious):

function _start_paste() {
  local content
  local a b
  local oldcutbuf="$CUTBUFFER"
  # I haven't quite decided how I want to control quoting yet.
  [[ $_SPACE_AFTER_PASTE_QUOTE = 1 ]]; a=$?
  (( $+NUMERIC )) || [[ $KEYS = $'\e\e'* ]]; b=$?
  zle .$WIDGET -N content
  if [[ $a -ne $b ]]; then
    CUTBUFFER=${(q-)content}' '
  zle .yank
zle -P bracketed-paste bracketed-paste _start_paste

And my pastes are appropriately highlit. (Wouldn't it really be more
useful to have the middle argument optional? I see how it might be
confusing if you're used to the zle -N behavior though.)

% zle -P myyank yank myyank
% myyank () {
    zle -M hello
    zle .yank
  # put asdfdδ in the cutbuffer here
  # press yank and myyank alternatingly
  # each word gets highlighted appropriately in both cases

 Src/Zle/zle_main.c   |  5 +++--
 Src/Zle/zle_thingy.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index e610ae1..6df7367 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1396,7 +1396,8 @@ execzlefunc(Thingy func, char **args, int set_bindk)
 	    opts[XTRACE] = oxt;
 	    sfcontext = osc;
-	    lastcmd = 0;
+	    if (!(w->flags & ZLE_NOTCOMMAND))
+		lastcmd = w->flags;
 	    r = 1;
 	    redup(osi, 0);
@@ -1975,7 +1976,7 @@ zle_main_entry(int cmd, va_list ap)
 static struct builtin bintab[] = {
     BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaM:ldDANmrsLRp", NULL),
     BUILTIN("vared",   0, bin_vared,   1,  1, 0, "aAcef:hi:M:m:p:r:t:", NULL),
-    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "aAcCDFgGIKlLmMNrRTUw", NULL),
+    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "aAcCDFgGIKlLmMNPrRTUw", NULL),
 /* The order of the entries in this table has to match the *HOOK
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 7fd3a59..20a1de9 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -347,6 +347,7 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func))
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
 	{ 'C', bin_zle_complete, 3, 3 },
+	{ 'P', bin_zle_prototype, 2, 3},
 	{ 'R', bin_zle_refresh, 0, -1 },
 	{ 'M', bin_zle_mesg, 1, 1 },
 	{ 'U', bin_zle_unget, 1, 1 },
@@ -591,6 +592,50 @@ bin_zle_new(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 static int
+bin_zle_prototype(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
+    Thingy t;
+    Widget w, pw;
+    char *funcname = args[2] ? args[2] : args[0];
+    t = rthingy((args[1][0] == '.') ? args[1] : dyncat(".", args[1]));
+    pw = t->widget;
+    unrefthingy(t);
+    if (!pw) {
+	zwarnnam(name, "invalid widget `%s'", args[1]);
+	return 1;
+    }
+    if (pw->flags & ZLE_ISCOMP &&
+	require_module("zsh/complete", NULL) == 1)
+    {
+	zwarnnam(name, "can't load complete module");
+	return 1;
+    }
+    w = zalloc(sizeof(*w));
+    w->flags = pw->flags & ~(WIDGET_INT|ZLE_ISCOMP);
+    w->first = NULL;
+    if (pw->flags & ZLE_ISCOMP) {
+	w->flags |= WIDGET_NCOMP;
+	w->u.comp.fn = pw->u.fn;
+	w->u.comp.wid = ztrdup(args[1]);
+	w->u.comp.func = ztrdup(funcname);
+    } else {
+	w->u.fnnam = ztrdup(funcname);
+    }
+    if (bindwidget(w, rthingy(args[0]))) {
+	freewidget(w);
+	zwarnnam(name, "widget name `%s' is protected", args[0]);
+	return 1;
+    }
+    if (w->flags & WIDGET_NCOMP)
+	hascompwidgets++;
+    return 0;
+static int
 bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
     Thingy t;
diff --git i/Doc/Zsh/zle.yo w/Doc/Zsh/zle.yo
index c06e226..a8b7476 100644
--- i/Doc/Zsh/zle.yo
+++ w/Doc/Zsh/zle.yo
@@ -404,6 +404,7 @@ xitem(tt(zle) tt(-D) var(widget) ...)
 xitem(tt(zle) tt(-A) var(old-widget) var(new-widget))
 xitem(tt(zle) tt(-N) var(widget) [ var(function) ])
 xitem(tt(zle) tt(-C) var(widget) var(completion-widget) var(function))
+xitem(tt(zle) tt(-P) var(widget) var(prototype-widget) [ var(function) ])
 xitem(tt(zle) tt(-R) [ tt(-c) ] [ var(display-string) ] [ var(string) ... ])
 xitem(tt(zle) tt(-M) var(string))
 xitem(tt(zle) tt(-U) var(string))
@@ -474,6 +475,20 @@ ifzman(zmanref(zshcompwid))\
 ifnzman(noderef(Completion Widgets))\
+item(tt(-P) var(widget) var(prototype-widget) [ var(function) ])(
+Create a user-defined widget.  If there is already a widget with the
+specified name, it is overwritten.  When the new
+widget is invoked from within the editor, the specified shell var(function)
+is called.  If no function name is specified, it defaults to
+the same name as the widget.  The widget will behave as the given
+var(prototype-widget).  If it is a completion widget then this works like
+tt(zle -C) above.  An example of a useful case is wrapping the tt(yank)
+widget, without using tt(zle -P), the yanked text will not be highlighted.
+For further information, see
+ifzman(the section `Widgets' below)\
+ifnzman(noderef(Zle Widgets))\
 item(tt(-R) [ tt(-c) ] [ var(display-string) ] [ var(string) ... ])(
 Redisplay the command line; this is to be called from within a user-defined
 widget to allow changes to become visible.  If a var(display-string) is
@@ -986,18 +1001,19 @@ The name of the widget currently being executed; read-only.
 item(tt(WIDGETFUNC) (scalar))(
 The name of the shell function that implements a widget defined with
-either tt(zle -N) or tt(zle -C).  In the former case, this is the second
+either tt(zle -N), tt(zle -C) or tt(zle -P).  In the former case, this is the second
 argument to the tt(zle -N) command that defined the widget, or
-the first argument if there was no second argument.  In the latter case
-this is the third argument to the tt(zle -C) command that defined the
-widget.  Read-only.
+the first argument if there was no second argument.  For tt(zle -C)
+and tt(zle -P), this is the third argument to the tt(zle -C) command
+that defined the widget.  Read-only.
 item(tt(WIDGETSTYLE) (scalar))(
 Describes the implementation behind the completion widget currently being
-executed; the second argument that followed tt(zle -C) when the widget was
-defined.  This is the name of a builtin completion widget.  For widgets
-defined with tt(zle -N) this is set to the empty string.  Read-only.
+executed; the second argument that followed tt(zle -C) or tt(zle -P) when
+the widget was defined.  This is the name of a builtin completion widget.
+For widgets defined with tt(zle -N) this is set to the empty string.
 item(tt(ZLE_STATE) (scalar))(


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