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

@ -56,6 +56,15 @@ New features in built-in commands:
Note that to use these options the operating system must support the
corresponding resource limit.
New command line editor features:
- Various keys on extended PC keyboards are now handled as expected in the
emacs and vi built-in line editors: Ctrl or Alt + left or right arrow (go
back or forward one word), Ctrl+G (cancel reverse search), Ctrl+Delete
(delete next word). In addition, the Insert key now escapes the next
character in emacs and enters insert mode in vi, and the arrow keys are
recognized on more terminals.
New features in shell options:
- The new --histreedit and --histverify options modify history expansion

19
NEWS
View file

@ -3,6 +3,25 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
Any uppercase BUG_* names are modernish shell bug IDs.
2022-01-31:
- Improved keyboard support for the vi and emacs built-in line editors:
- Added support for Home key sequences ^[[1~ and ^[[7~ as well as End key
sequences ^[[4~ and ^[[8~.
- Added support for arrow key sequences ^[OA, ^[OB, ^[OC and ^[OD.
- Added 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 keybind 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).
2022-01-28:
- Fixed longstanding job control breakage that occurred on the interactive

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 ( ,