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

Re: [PATCH] Bind quasi-default Home, End and Delete keys in built-in keymaps



On Tue, Dec 13, 2016 at 02:27:57AM +0100, Frank Terbeck wrote:
> Teubel György wrote:
> > Make Home, End and Delete keys work by default as expected by most users.
> [...]
> > +item(tt(beginning-of-line) (tt(^A) tt(ESC-[1~)) (unbound) (unbound))(
> 
> Unfortunately, it's not that easy. Those special keys are not stable,
> even across a small sample of terminals and terminal emulators. You'd
> have to take a look at termcap or terminfo to get a reasonably well
> founded escape sequence for them, which also requires the terminal to be
> in smkx mode, while the line editor is active.

You are right, it should be done by querying termcap capabilities.  Here is a
modified version of the patch.  I used a tighter version of the add_cursor_key()
function.  In addition, the Insert key is bound to 'vi-insert' in vi command mode.

> Zsh does have a terminfo module and it can be used to set up some well
> known special keys. But those are best added to a global configuration
> file, rather than being hard coded in C. This is what Debian does¹ for
> example.

I know that there is a terminfo (and a termcap) module.  The difference is that
when it is in the default bindings (as the arrow keys are, for example), then these
keys work out of the box, without additional configuration.  This increases user
experience a bit.

> Regards, Frank

---
 Doc/Zsh/zle.yo       | 14 +++++++-------
 Src/Zle/zle_keymap.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 Src/init.c           |  3 ++-
 Src/zsh.h            |  6 +++++-
 4 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index d68365b94..18a73b416 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -1231,12 +1231,12 @@ item(tt(vi-backward-word-end) (unbound) (tt(ge)) (unbound))(
 Move to the end of the previous word, vi-style.
 )
 tindex(beginning-of-line)
-item(tt(beginning-of-line) (tt(^A)) (unbound) (unbound))(
+item(tt(beginning-of-line) (tt(^A) tt(HOME)) (unbound) (unbound))(
 Move to the beginning of the line.  If already at the beginning
 of the line, move to the beginning of the previous line, if any.
 )
 tindex(vi-beginning-of-line)
-item(tt(vi-beginning-of-line))(
+item(tt(vi-beginning-of-line) (unbound) (tt(HOME)) (tt(HOME)))(
 Move to the beginning of the line, without changing lines.
 )
 tindex(down-line)
@@ -1244,12 +1244,12 @@ item(tt(down-line) (unbound) (unbound) (unbound))(
 Move down a line in the buffer.
 )
 tindex(end-of-line)
-item(tt(end-of-line) (tt(^E)) (unbound) (unbound))(
+item(tt(end-of-line) (tt(^E) tt(END)) (unbound) (unbound))(
 Move to the end of the line.  If already at the end
 of the line, move to the end of the next line, if any.
 )
 tindex(vi-end-of-line)
-item(tt(vi-end-of-line) (unbound) (tt($)) (unbound))(
+item(tt(vi-end-of-line) (unbound) (tt($) tt(END)) (tt(END)))(
 Move to the end of the line.
 If an argument is given to this command, the cursor will be moved to
 the end of the line (argument - 1) lines down.
@@ -1778,11 +1778,11 @@ from the cursor position to the endpoint of the movement.
 If the command is tt(vi-delete), kill the current line.
 )
 tindex(delete-char)
-item(tt(delete-char))(
+item(tt(delete-char) (tt(DELETE)) (unbound) (unbound))(
 Delete the character under the cursor.
 )
 tindex(vi-delete-char)
-item(tt(vi-delete-char) (unbound) (tt(x)) (unbound))(
+item(tt(vi-delete-char) (unbound) (tt(x) tt(DELETE)) (tt(DELETE)))(
 Delete the character under the cursor,
 without going past the end of the line.
 )
@@ -1814,7 +1814,7 @@ item(tt(vi-indent) (unbound) (tt(>)) (unbound))(
 Indent a number of lines.
 )
 tindex(vi-insert)
-item(tt(vi-insert) (unbound) (tt(i)) (unbound))(
+item(tt(vi-insert) (unbound) (tt(i) tt(INSERT)) (unbound))(
 Enter insert mode.
 )
 tindex(vi-insert-bol)
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 04eb70675..c6b6bdc0a 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1280,6 +1280,38 @@ add_cursor_key(Keymap km, int tccode, Thingy thingy, int defchar)
     }
 }
 
+/* interrogate termcap for special keys and add binding to keymap */
+
+/**/
+static void
+bind_termcap_key(Keymap km, int tccode, Thingy thingy)
+{
+    char buf[2048];
+
+    /*
+     * Be careful not to try too hard with bindings for dubious or
+     * dysfunctional terminals.
+     */
+    if (tccan(tccode) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
+	/*
+	 * We can use the real termcap sequence.  We need to
+	 * persuade termcap to output `move cursor 1 char' and capture it.
+	 */
+	cursorptr = buf;
+	tputs(tcstr[tccode], 1, add_cursor_char);
+	*cursorptr = '\0';
+
+	/*
+	 * Sanity checking.  If the key is zero-length (unlikely,
+	 * but this is termcap we're talking about), or it's a single
+	 * character, then we don't bind it.
+	 */
+	if (buf[0] && buf[1] && (buf[0] != Meta || buf[2])) {
+	    bindkey(km, buf, refthingy(thingy), NULL);
+	}
+    }
+}
+
 /* Create the default keymaps.  For efficiency reasons, this function   *
  * assigns directly to the km->first array.  It knows that there are no *
  * prefix bindings in the way, and that it is using a simple keymap.    */
@@ -1342,12 +1374,20 @@ default_bindings(void)
     vimaps[1] = amap;
     for (i = 0; i < 2; i++) {
 	kptr = vimaps[i];
-	/* vi command and insert modes: arrow keys */
+	/* vi command and insert modes: arrow keys,
+	 * home, end and delete */
 	add_cursor_key(kptr, TCUPCURSOR, t_uplineorhistory, 'A');
 	add_cursor_key(kptr, TCDOWNCURSOR, t_downlineorhistory, 'B');
 	add_cursor_key(kptr, TCLEFTCURSOR, t_vibackwardchar, 'D');
 	add_cursor_key(kptr, TCRIGHTCURSOR, t_viforwardchar, 'C');
+	bind_termcap_key(kptr, TCHOMEKEY, refthingy(t_vibeginningofline));
+	bind_termcap_key(kptr, TCENDKEY, refthingy(t_viendofline));
+	bind_termcap_key(kptr, TCDELETEKEY, refthingy(t_videletechar));
     }
+
+    /* Insert key enters insert mode */
+    bind_termcap_key(amap, TCINSERTKEY, refthingy(t_viinsert));
+
     vilmaps[0] = oppmap;
     vilmaps[1] = vismap;
     for (i = 0; i < 2; i++) {
@@ -1386,11 +1426,14 @@ default_bindings(void)
     bindkey(amap, "guu", NULL, "gugu");
     bindkey(amap, "gUU", NULL, "gUgU");
 
-    /* emacs mode: arrow keys */ 
+    /* emacs mode: arrow keys, home, end and delete */
     add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A');
     add_cursor_key(emap, TCDOWNCURSOR, t_downlineorhistory, 'B');
     add_cursor_key(emap, TCLEFTCURSOR, t_backwardchar, 'D');
     add_cursor_key(emap, TCRIGHTCURSOR, t_forwardchar, 'C');
+    bind_termcap_key(emap, TCHOMEKEY, refthingy(t_beginningofline));
+    bind_termcap_key(emap, TCENDKEY, refthingy(t_endofline));
+    bind_termcap_key(emap, TCDELETEKEY, refthingy(t_deletechar));
    
     /* emacs mode: ^X sequences */
     bindkey(emap, "\30*",   refthingy(t_expandword), NULL);
diff --git a/Src/init.c b/Src/init.c
index c12043b88..7b632394e 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -670,7 +670,8 @@ static char *tccapnams[TC_COUNT] = {
     "cl", "le", "LE", "nd", "RI", "up", "UP", "do",
     "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta",
     "md", "so", "us", "me", "se", "ue", "ch",
-    "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB"
+    "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB",
+    "kh", "@7", "kD", "kI"
 };
 
 /**/
diff --git a/Src/zsh.h b/Src/zsh.h
index f22d8b135..6f6544d09 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2524,7 +2524,11 @@ struct ttyinfo {
 #define TCBACKSPACE    31
 #define TCFGCOLOUR     32
 #define TCBGCOLOUR     33
-#define TC_COUNT       34
+#define TCHOMEKEY      34
+#define TCENDKEY       35
+#define TCDELETEKEY    36
+#define TCINSERTKEY    37
+#define TC_COUNT       38
 
 #define tccan(X) (tclen[X])
 
-- 
2.11.0



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