1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Add support for more keyboard shortcuts (#410)

Add extra key bindings to the emacs and vi modes

This patch adds the following key bindings to the emacs and vi
editing modes:
- Support for Home key sequences ^[[1~ and ^[[7~ as well as End key
  sequences ^[[4~ and ^[[8~.
- Support for arrow key sequences ^[OA, ^[OB, ^[OC and ^[OD.
- Support for the following keyboard shortcuts (if the platform
  supports the expected escape sequence):
  - Ctrl-Left Arrow:  Go back one word
  - Alt-Left Arrow:   Go back one word     (Not supported on Haiku)
  - Ctrl-Right Arrow: Go forward one word
  - Alt-Right Arrow:  Go forward one word  (Not supported on Haiku)
  - Ctrl-G:           Cancel reverse search
  - Ctrl-Delete:      Delete next word     (Not supported on Haiku)
- Added a key binding for the Insert key, which differs in the
  emacs and vi editing modes:
  - In emacs mode, Insert escapes the next character.
  - In vi mode, Insert will switch the editor to insert mode (like
    in vim).

src/cmd/ksh93/edit/{emacs,vi}.c:
- Add support for the <M-Left> and <M-Right> sequences. Like in
  bash and mksh, these shortcuts move the cursor one word backward
  or forward (like the <Ctrl-Left> and <Ctrl-Right> shortcuts).
- Only attempt to process these shortcuts if the escape sequence
  begins with $'\E[1;'.

src/cmd/ksh93/edit/vi.c:
- If the shell isn't doing a reverse search, insert the bell
  character when Ctrl+G is input.
- Add the Ctrl-Delete shortcut as an alias of 'dw'. Calling
  ed_ungetchar twice does not work for 'dw', so Ctrl-Delete was
  implemented by using a vp->del_word variable.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
This commit is contained in:
Johnothan King 2022-01-31 13:09:50 -08:00 committed by Martijn Dekker
parent 9c313c7fe3
commit 9a5af738ef
6 changed files with 334 additions and 8 deletions

View file

@ -780,7 +780,7 @@ static void putstring(Emacs_t* ep,register char *sp)
static int escape(register Emacs_t* ep,register genchar *out,int count)
{
register int i,value;
int digit,ch;
int digit,ch,c,d;
digit = 0;
value = 0;
while ((i=ed_getchar(ep->ed,0)),digit(i))
@ -827,6 +827,7 @@ static int escape(register Emacs_t* ep,register genchar *out,int count)
case 'd': /* M-d == delete word */
case 'c': /* M-c == uppercase */
case 'f': /* M-f == move cursor forward one word */
forward:
{
i = cur;
while(value-- && i<eol)
@ -883,6 +884,7 @@ static int escape(register Emacs_t* ep,register genchar *out,int count)
case DELETE :
case '\b':
case 'h': /* M-h == delete the previous word */
backward:
{
i = cur;
while(value-- && i>0)
@ -1079,6 +1081,7 @@ static int escape(register Emacs_t* ep,register genchar *out,int count)
return(-1);
#endif
case '[': /* feature not in book */
case 'O': /* after running top <ESC>O instead of <ESC>[ */
switch(i=ed_getchar(ep->ed,1))
{
case 'A':
@ -1131,8 +1134,53 @@ static int escape(register Emacs_t* ep,register genchar *out,int count)
/* VT220 End key */
ed_ungetchar(ep->ed,cntl('E'));
return(-1);
case '1':
case '7':
/*
* ed_getchar() can only be run once on each character
* and shouldn't be used on non-existent characters.
*/
ch = ed_getchar(ep->ed,1);
if(ch == '~')
{ /* Home key */
ed_ungetchar(ep->ed,cntl('A'));
return(-1);
}
else if(i == '1' && ch == ';')
{
c = ed_getchar(ep->ed,1);
if(c == '3' || c == '5' || c == '9') /* 3 == Alt, 5 == Ctrl, 9 == iTerm2 Alt */
{
d = ed_getchar(ep->ed,1);
switch(d)
{
case 'D': /* Ctrl/Alt-Left arrow (go back one word) */
ch = 'b';
goto backward;
case 'C': /* Ctrl/Alt-Right arrow (go forward one word) */
ch = 'f';
goto forward;
}
ed_ungetchar(ep->ed,d);
}
ed_ungetchar(ep->ed,c);
}
ed_ungetchar(ep->ed,ch);
ed_ungetchar(ep->ed,i);
return(-1);
case '2': /* Insert key */
ch = ed_getchar(ep->ed,1);
if(ch == '~')
{
ed_ungetchar(ep->ed, cntl('V'));
return(-1);
}
ed_ungetchar(ep->ed,ch);
ed_ungetchar(ep->ed,i);
return(-1);
case '3':
if((ch=ed_getchar(ep->ed,1))=='~')
ch = ed_getchar(ep->ed,1);
if(ch == '~')
{ /*
* VT220 forward-delete key.
* Since ERASECHAR and EOFCHAR are usually both mapped to ^D, we
@ -1143,6 +1191,47 @@ static int escape(register Emacs_t* ep,register genchar *out,int count)
ed_ungetchar(ep->ed,ERASECHAR);
return(-1);
}
else if(ch == ';')
{
c = ed_getchar(ep->ed,1);
if(c == '5')
{
d = ed_getchar(ep->ed,1);
if(d == '~')
{
/* Ctrl-Delete (delete next word) */
ch = 'd';
goto forward;
}
ed_ungetchar(ep->ed,d);
}
ed_ungetchar(ep->ed,c);
}
ed_ungetchar(ep->ed,ch);
ed_ungetchar(ep->ed,i);
return(-1);
case '5': /* Haiku terminal Ctrl-Arrow key */
ch = ed_getchar(ep->ed,1);
switch(ch)
{
case 'D': /* Ctrl-Left arrow (go back one word) */
ch = 'b';
goto backward;
case 'C': /* Ctrl-Right arrow (go forward one word) */
ch = 'f';
goto forward;
}
ed_ungetchar(ep->ed,ch);
ed_ungetchar(ep->ed,i);
return(-1);
case '4':
case '8': /* rxvt */
ch = ed_getchar(ep->ed,1);
if(ch == '~')
{
ed_ungetchar(ep->ed,cntl('E')); /* End key */
return(-1);
}
ed_ungetchar(ep->ed,ch);
/* FALLTHROUGH */
default:
@ -1288,7 +1377,7 @@ static void search(Emacs_t* ep,genchar *out,int direction)
goto restore;
continue;
}
if(i == ep->ed->e_intr) /* end reverse search */
if(i == ep->ed->e_intr || i == cntl('G')) /* end reverse search */
goto restore;
if (i==usrkill)
{

View file

@ -112,6 +112,7 @@ typedef struct _vi_
int U_saved; /* original virtual saved */
genchar *U_space; /* used for U command */
genchar *u_space; /* used for u command */
unsigned char del_word; /* used for Ctrl-Delete */
#ifdef FIORDCHK
clock_t typeahead; /* typeahead occurred */
#else
@ -753,7 +754,7 @@ static int cntlmode(Vi_t *vp)
if(mvcursor(vp,c))
{
sync_cursor(vp);
if( c != '[' )
if( c != '[' && c != 'O' )
vp->repeat = 1;
continue;
}
@ -1036,7 +1037,7 @@ static int cntlmode(Vi_t *vp)
if(lookahead)
{
ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1));
if(c=='[')
if(c=='[' || c=='O')
continue;
}
/* FALLTHROUGH */
@ -1430,6 +1431,10 @@ static void getline(register Vi_t* vp,register int mode)
}
break;
case cntl('G'):
if(mode!=SEARCH)
goto fallback;
/* FALLTHROUGH */
case UINTR:
first_virt = 0;
cdelete(vp,cur_virt+1, BAD);
@ -1550,6 +1555,7 @@ static void getline(register Vi_t* vp,register int mode)
}
/* FALLTHROUGH */
default:
fallback:
if( mode == REPLACE )
{
if( cur_virt < last_virt )
@ -1583,7 +1589,7 @@ static void getline(register Vi_t* vp,register int mode)
static int mvcursor(register Vi_t* vp,register int motion)
{
register int count;
register int count, c, d;
register int tcur_virt;
register int incr = -1;
register int bound = 0;
@ -1612,7 +1618,8 @@ static int mvcursor(register Vi_t* vp,register int motion)
tcur_virt = last_virt;
break;
case '[':
case '[': /* feature not in book */
case 'O': /* after running top <ESC>O instead of <ESC>[ */
switch(motion=ed_getchar(vp->ed,-1))
{
case 'A':
@ -1659,6 +1666,47 @@ static int mvcursor(register Vi_t* vp,register int motion)
/* VT220 End key */
ed_ungetchar(vp->ed,'$');
return(1);
case '1':
case '7':
bound = ed_getchar(vp->ed,-1);
if(bound=='~')
{ /* Home key */
ed_ungetchar(vp->ed,'0');
return(1);
}
else if(motion=='1' && bound==';')
{
c = ed_getchar(vp->ed,-1);
if(c == '3' || c == '5' || c == '9') /* 3 == Alt, 5 == Ctrl, 9 == iTerm2 Alt */
{
d = ed_getchar(vp->ed,-1);
switch(d)
{
case 'D': /* Ctrl/Alt-Left arrow (go back one word) */
ed_ungetchar(vp->ed, 'b');
return(1);
case 'C': /* Ctrl/Alt-Right arrow (go forward one word) */
ed_ungetchar(vp->ed, 'w');
return(1);
}
ed_ungetchar(vp->ed,d);
}
ed_ungetchar(vp->ed,c);
}
ed_ungetchar(vp->ed,bound);
ed_ungetchar(vp->ed,motion);
return(0);
case '2':
bound = ed_getchar(vp->ed,-1);
if(bound=='~')
{
/* VT220 insert key */
ed_ungetchar(vp->ed,'i');
return(1);
}
ed_ungetchar(vp->ed,bound);
ed_ungetchar(vp->ed,motion);
return(0);
case '3':
bound = ed_getchar(vp->ed,-1);
if(bound=='~')
@ -1667,6 +1715,48 @@ static int mvcursor(register Vi_t* vp,register int motion)
ed_ungetchar(vp->ed,'x');
return(1);
}
else if(bound==';')
{
c = ed_getchar(vp->ed,-1);
if(c == '5')
{
d = ed_getchar(vp->ed,-1);
if(d == '~')
{
/* Ctrl-Delete */
vp->del_word = 1;
ed_ungetchar(vp->ed,'d');
return(1);
}
ed_ungetchar(vp->ed,d);
}
ed_ungetchar(vp->ed,c);
}
ed_ungetchar(vp->ed,bound);
ed_ungetchar(vp->ed,motion);
return(0);
case '5': /* Haiku terminal Ctrl-Arrow key */
bound = ed_getchar(vp->ed,-1);
switch(bound)
{
case 'D': /* Ctrl-Left arrow (go back one word) */
ed_ungetchar(vp->ed, 'b');
return(1);
case 'C': /* Ctrl-Right arrow (go forward one word) */
ed_ungetchar(vp->ed, 'w');
return(1);
}
ed_ungetchar(vp->ed,bound);
ed_ungetchar(vp->ed,motion);
return(0);
case '4':
case '8':
bound = ed_getchar(vp->ed,-1);
if(bound=='~')
{ /* End key */
ed_ungetchar(vp->ed,'$');
return(1);
}
ed_ungetchar(vp->ed,bound);
/* FALLTHROUGH */
default:
@ -2602,6 +2692,11 @@ chgeol:
case 'd': /** delete **/
if( mode )
c = vp->lastmotion;
else if( vp->del_word )
{
vp->del_word = 0;
c = 'w';
}
else
c = getcount(vp,ed_getchar(vp->ed,-1));
deleol:

View file

@ -21,7 +21,7 @@
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2022-01-28" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2022-01-31" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */

View file

@ -4922,6 +4922,15 @@ Move cursor forward one word.
editor's idea of a word is a string of characters
consisting of only letters, digits and underscores.)
.TP 10
.BI M-[1;3C
(Alt-Right arrow) Same as \fBM-f\fR.
.TP 10
.BI M-[1;5C
(Ctrl-Right arrow) Same as \fBM-f\fR.
.TP 10
.BI M-[1;9C
(iTerm2 Alt-Right arrow) Same as \fBM-f\fR.
.TP 10
.BI ^B
Move cursor backward (left) one character.
.TP 10
@ -4931,21 +4940,60 @@ Move cursor backward (left) one character.
.BI M-b
Move cursor backward one word.
.TP 10
.BI M-[1;3D
(Alt-Left arrow) Same as \fBM-b\fR.
.TP 10
.BI M-[1;5D
(Ctrl-Left arrow) Same as \fBM-b\fR.
.TP 10
.BI M-[1;9D
(iTerm2 Alt-Left arrow) Same as \fBM-b\fR.
.TP 10
.BI ^A
Move cursor to start of line.
.TP 10
.BI M-[H
(Home) Same as \fB^A\fR.
.TP 10
.BI M-[1~
Same as \fB^A\fR.
.TP 10
.BI M-[7~
Same as \fB^A\fR.
.TP 10
.BI ^E
Move cursor to end of line.
.TP 10
.BI M-[F
(End) Same as \fB^E\fR.
.TP 10
.BI M-[4~
Same as \fB^E\fR.
.TP 10
.BI M-[8~
Same as \fB^E\fR.
.TP 10
.BI M-[Y
Same as \fB^E\fR.
.TP 10
.BI M-OA
(Up Arrow) Same as \fBM-[A\fR.
.TP 10
.BI M-OB
(Down Arrow) Same as \fBM-[B\fR.
.TP 10
.BI M-OC
(Right Arrow) Same as \fBM-[C\fR.
.TP 10
.BI M-OD
(Left Arrow) Same as \fBM-[D\fR.
.TP 10
.BI M-O5C
(Ctrl-Right Arrow) Same as \fBM-f\fR.
.TP 10
.BI M-O5D
(Ctrl-Left Arrow) Same as \fBM-b\fR.
.TP 10
.BI ^] char
Move cursor forward to character
.I char
@ -4987,6 +5035,9 @@ Delete current character.
.BI M-d
Delete current word.
.TP 10
.BI M-[3;5~
(Ctrl-Delete) Same as \fBM-d\fR.
.TP 10
.BI M-^H
(Meta-backspace) Delete previous word.
.TP 10
@ -5130,6 +5181,9 @@ is accessed.
In this case a parameter of zero
reverses the direction of the search.
.TP 10
.B ^G
Exit reverse search mode.
.TP 10
.B ^O
Operate \- Execute the current line and fetch
the next line relative to current line from the
@ -5247,6 +5301,9 @@ which is not subject to any shell option.
.B M-^V
Display version of the shell.
.TP 10
.BI M-[2~
(Insert) Escape the next character.
.TP 10
.B M-#
If the line does not begin with a
.BR # ,
@ -5437,6 +5494,54 @@ Cursor to start of line.
.B ^[[H
(Home) Same as \f30\fP.
.TP 10
.B ^[[1~
Same as \f30\fP.
.TP 10
.B ^[[7~
Same as \f30\fP.
.TP 10
.B ^[[1;3D
(Alt-Left arrow) Same as \f3b\fP.
.TP 10
.B ^[[1;5D
(Ctrl-Left arrow) Same as \f3b\fP.
.TP 10
.B ^[[1;9D
(iTerm2 Alt-Left arrow) Same as \f3b\fP.
.TP 10
.B ^[[1;3C
(Alt-Right arrow) Same as \f3w\fP.
.TP 10
.B ^[[1;5C
(Ctrl-Right arrow) Same as \f3w\fP.
.TP 10
.B ^[[1;9C
(iTerm2 Alt-Right arrow) Same as \f3w\fP.
.TP 10
.B ^[[2~
(Insert) Same as \f3i\fP.
.TP 10
.B ^[[3;5~
(Ctrl-Delete) Same as \f3dw\fP.
.TP 10
.B ^[OA
(Up Arrow) Same as \f3^[[A\fP.
.TP 10
.B ^[OB
(Down Arrow) Same as \f3^[[B\fP.
.TP 10
.B ^[OC
(Right Arrow) Same as \f3^[[C\fP.
.TP 10
.B ^[OD
(Left Arrow) Same as \f3^[[D\fP.
.TP 10
.B ^[O5C
(Ctrl-Right Arrow) Same as \f3w\fP.
.TP 10
.B ^[O5D
(Ctrl-Left Arrow) Same as \f3b\fP.
.TP 10
.B ^
Cursor to first non-blank character in line.
.TP 10
@ -5446,9 +5551,18 @@ Cursor to end of line.
.B ^[[F
(End) Same as \f3$\fP.
.TP 10
.B ^[[4~
Same as \f3$\fP.
.TP 10
.B ^[[8~
Same as \f3$\fP.
.TP 10
.B ^[[Y
Same as \f3$\fP.
.TP 10
.B ^G
Exit reverse search mode.
.TP 10
.B %
Moves to balancing
.BR ( ,