zsh-users
 help / color / mirror / code / Atom feed
* Zle history feature like that of many IRC clients?
@ 2011-12-19 22:39 milk
  2011-12-20  3:17 ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: milk @ 2011-12-19 22:39 UTC (permalink / raw)
  To: zsh-users

[-- Attachment #1: Type: text/plain, Size: 561 bytes --]

Hello,

I'm wondering if, by any means, it would be possible to have Zle act in the
manner of many IRC clients? Specifically, if I were to type something, then
press the down arrow and get a blank input line, type something else and
execute it, then press up twice and get what I typed previously (but did
not execute) back again? I'm aware of the buffer stack, but I'm looking for
a method that works with the regular history. insert-pound is also nice,
but more effort than what I'm used to from IRC. Thanks for any ideas or
suggestions.

Kind regards,
Milky

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Zle history feature like that of many IRC clients?
  2011-12-19 22:39 Zle history feature like that of many IRC clients? milk
@ 2011-12-20  3:17 ` Bart Schaefer
  2011-12-20 16:24   ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: Bart Schaefer @ 2011-12-20  3:17 UTC (permalink / raw)
  To: zsh-users

On Dec 19, 10:39pm, milk wrote:
:
: Specifically, if I were to type something, then
: press the down arrow and get a blank input line

So what you're asking for is to bind down-arrow to a sort of "put this
in the history but don't send it" action?

Presumably, though, if you've already pressed up-arrow at least once so
you are in the middle of the history, then you just want down-arrow to
move through the history like it normally does.

Let's build it up from some pieces.  First, a "fake-accept-line" to
not really execute the command, but put it on the history.  There are
several different ways to do this with preexec hooks etc., but let's
just do it the old-fashioned way:

    fake-accept-line() {
      if [[ -n "$BUFFER" ]];
      then
	print -S "$BUFFER"
	zle .send-break
      fi
      return 0
    }
    zle -N fake-accept-line

If your version of zsh doesn't have "print -S", use "-s" instead.  It's
not perfect but it'll do.  Using "zle .send-break" at the end sets the
correct return value and makes the history from "print -S" immediately
available for recall, but interrupts whatever else you have going on so
it does mean that fake-accept-line has to be the last thing you want to
do before getting a new prompt.

The leading dot in ".send-break" tells zle to call the widget by its
unalterable name, so you won't be messed up by some other configuration
rebinding the widget.  Make note of this for below.  Also note that you
can't create an unalterable name for a user-defined widget, those are
only for builtins.

Next you want a function that does down-line-or-history [probably; you
might want down-history instead, but I can't tell from your description]
unless there is nowhere to go, in which case it does fake-accept-line.

    down-or-fake-accept-line() {
      (( HISTNO == HISTCMD )) && zle fake-accept-line
      zle .down-line-or-history "$@"                     
    }
    zle -N down-or-fake-accept-line

There I've done a shortcut:  I know fake-accept-line is going to break
out of the surrounding function if it does anything at all, so there's
no need for an else-case, I can fall through to down-line-or-history.

Finally you just need to bind it to a key.  To avoid all the messiness
of figuring out what sequence of characters your down-arrow key may
send, it's often easier to simply grab the widget that is already bound
to that key and alias it to your new widget.

    zle -N down-line-or-history down-or-fake-accept-line

This has now swapped out down-line-or history for this widget anywhere
that down-line-or-history was used (such as, all the default keymaps),
but not inside down-or-fake-accept-line itself, where we have avoided
infinite recursion by using the unalterable name.

If you prefer not to do it this way, add a bindkey command such as

    bindkey '^[[B' down-or-fake-accept-line

which depending on your terminal might mean down-arrow.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Zle history feature like that of many IRC clients?
  2011-12-20  3:17 ` Bart Schaefer
@ 2011-12-20 16:24   ` Bart Schaefer
  2011-12-21  6:33     ` milk
  0 siblings, 1 reply; 5+ messages in thread
From: Bart Schaefer @ 2011-12-20 16:24 UTC (permalink / raw)
  To: zsh-users

An additional twist occurred to me overnight:

On Dec 19,  7:17pm, Bart Schaefer wrote:
:
:     down-or-fake-accept-line() {
:       (( HISTNO == HISTCMD )) && zle fake-accept-line
:       zle .down-line-or-history "$@"                     
:     }

That's going to call fake-accept-line a bit too soon if you are in a
multi-line buffer.  The fix is pretty simple:

    down-or-fake-accept-line() {
      if (( HISTNO == HISTCMD )) && [[ "$RBUFFER" != *$'\n'* ]];
      then
        zle fake-accept-line
      fi
      zle .down-line-or-history "$@"                     
    }

(Rewritten as an "if" to avoid line wrap.)  This assumes that if there
are newlines to the right of the cursor, then we're somewhere in the
middle of a multi-line buffer and should keep moving down.

If you wanted down-history instead of down-line-or-history, then the
original test would be correct (but you should change which widget is
called at the end).


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Zle history feature like that of many IRC clients?
  2011-12-20 16:24   ` Bart Schaefer
@ 2011-12-21  6:33     ` milk
  2011-12-21  9:07       ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: milk @ 2011-12-21  6:33 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-users

[-- Attachment #1: Type: text/plain, Size: 745 bytes --]

Bingo, thanks so much! The only change I have made is to comment out the
"zle .send-break" in the fake-accept-line() so that the content of the line
is not echoed back on hitting down.

There are issues with slightly more complex management though. "test 111"
<down> "test 222" <up> <enter> then there's no more "test 222" in the
buffer. Also "test 123" <down> "test 789" <up> alter "test 123" to "test
123456" <down> <enter> (on "test 789") <up> <up> gives "test 123" still.

But still, the basics you have provided will be a great help to myself, my
coming from a much longer history of IRC usage than bash/zsh usage :)

P.S. I'm basing the behaviour on Irssi.

P.P.S. I stuck this in the wiki -
http://zshwiki.org/home/zle/ircclientlikeinput

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Zle history feature like that of many IRC clients?
  2011-12-21  6:33     ` milk
@ 2011-12-21  9:07       ` Bart Schaefer
  0 siblings, 0 replies; 5+ messages in thread
From: Bart Schaefer @ 2011-12-21  9:07 UTC (permalink / raw)
  To: zsh-users

On Dec 21,  6:33am, milk wrote:
}
} Bingo, thanks so much! The only change I have made is to comment out
} the "zle .send-break" in the fake-accept-line() so that the content of
} the line is not echoed back on hitting down.

Does that really do what you want?  Without the send-break, the line
added to the history by "print -S" is not available for recall until
after the next real accept-line (i.e., after you press enter).  That
is, without the send-break you should see that

 % test AAA<enter>
 % test BBB<down><up>

does not in fact recall "test BBB", rather it recalls "test AAA".

Also the operation of down-or-fake-accept-line depends on the action of
send-break to interrupt the following "zle .down-line-or-history", but
as it turns out that's a no-op when ((HISTNO == HISTCMD)) so there is
no visible change to the behavior, just a useless action.

You can replace "zle .send-break" with

    BUFFER=''
    zle .accept-line

but then you'll still get an extra prompt.  There's no way to insert a
line into the recallable history without ending and restarting zle,
which means you must do one of send-break or accept-line (or one of
the variants thereof).

} There are issues with slightly more complex management though. "test 111"
} <down> "test 222" <up> <enter> then there's no more "test 222" in the
} buffer.

The only line that gets saved to the history is the one that was shown
at the prompt at the instant that you pressed enter.  Shell history is
not a text conversation, it's a series of events, and if you don't tell
the shell to execute the command, then it's not an event and is thrown
away.  "print -S" creates a phony event, but it's still only added to
the end of the history; you can't insert stuff in the middle, and you
can't remove stuff once it's been added (except by having it fall off
because the number of events has exceeded HISTSIZE).

You could write a function similar to down-or-fake-accept-line that is
called up-or-fake-accept-line and kicks in when you press <up>, but then
you'll always be adding at least two events to the history -- the phony
one when you <up>, followed by the real one when you <enter>.  So if you
did edit/<up>/<down>/edit/<up>/<down>/edit/<enter> you'd get *three*
events, one phony one at each <up> and a real one at <enter>.

Also, because of the need to end/restart zle to cause the history to
be permanently updated, there are a bunch of tricks necessary to get
the <up> to happen after the prompt is reprinted.

} Also "test 123" <down> "test 789" <up> alter "test 123" to "test
} 123456" <down> <enter> (on "test 789") <up> <up> gives "test 123" still.

Well, that's how zsh history works.  You can't alter the past (except
during a single editor "session", which remembers your changes until
you eventually hit enter, and then discards them).  You can only append
new events to the end.

You're going to be a lot happier if you get used to the event-based
semantics of shell history rather than attempt to coerce the shell
history into a text stream.


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-12-21  9:08 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-19 22:39 Zle history feature like that of many IRC clients? milk
2011-12-20  3:17 ` Bart Schaefer
2011-12-20 16:24   ` Bart Schaefer
2011-12-21  6:33     ` milk
2011-12-21  9:07       ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).