implement new evaluate-region editing command (!MKSH_SMALL)

while here, fixup x_redraw refactoring x_clrtoeol, x_pprompt, and x_lastpos
This commit is contained in:
tg
2016-07-26 21:37:26 +00:00
parent 04454868ff
commit 9dbd643504
3 changed files with 158 additions and 95 deletions

239
edit.c
View File

@ -28,7 +28,7 @@
#ifndef MKSH_NO_CMDLINE_EDITING #ifndef MKSH_NO_CMDLINE_EDITING
__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.301 2016/07/26 20:43:14 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/edit.c,v 1.302 2016/07/26 21:37:24 tg Exp $");
/* /*
* in later versions we might use libtermcap for this, but since external * in later versions we might use libtermcap for this, but since external
@ -961,7 +961,6 @@ static size_t x_fword(bool);
static void x_goto(char *); static void x_goto(char *);
static char *x_bs0(char *, char *) MKSH_A_PURE; static char *x_bs0(char *, char *) MKSH_A_PURE;
static void x_bs3(char **); static void x_bs3(char **);
static int x_size_str(char *);
static int x_size2(char *, char **); static int x_size2(char *, char **);
static void x_zots(char *); static void x_zots(char *);
static void x_zotc3(char **); static void x_zotc3(char **);
@ -986,6 +985,7 @@ static void x_e_puts(const char *);
static int x_fold_case(int); static int x_fold_case(int);
#endif #endif
static char *x_lastcp(void); static char *x_lastcp(void);
static void x_lastpos(void);
static void do_complete(int, Comp_type); static void do_complete(int, Comp_type);
static size_t x_nb2nc(size_t) MKSH_A_PURE; static size_t x_nb2nc(size_t) MKSH_A_PURE;
@ -1121,6 +1121,7 @@ static struct x_defbindings const x_defbindings[] = {
#endif #endif
#ifndef MKSH_SMALL #ifndef MKSH_SMALL
/* more non-standard ones */ /* more non-standard ones */
{ XFUNC_eval_region, 1, CTRL('E') },
{ XFUNC_edit_line, 2, 'e' } { XFUNC_edit_line, 2, 'e' }
#endif #endif
}; };
@ -1380,15 +1381,11 @@ x_ins(const char *s)
x_lastcp(); x_lastcp();
x_adj_ok = tobool(xcp >= xlp); x_adj_ok = tobool(xcp >= xlp);
x_zots(cp); x_zots(cp);
/* has x_adjust() been called? */
if (adj == x_adj_done) {
/* no */
cp = xlp;
while (cp > xcp)
x_bs3(&cp);
}
if (xlp == xep - 1) if (xlp == xep - 1)
x_redraw('\r'); x_redraw('\r');
else if (adj == x_adj_done)
/* x_adjust() has not been called */
x_lastpos();
x_adj_ok = true; x_adj_ok = true;
return (0); return (0);
} }
@ -1495,10 +1492,7 @@ x_delete(size_t nc, bool push)
/*x_goto(xcp);*/ /*x_goto(xcp);*/
x_adj_ok = true; x_adj_ok = true;
xlp_valid = false; xlp_valid = false;
cp = x_lastcp(); x_lastpos();
while (cp > xcp)
x_bs3(&cp);
x_modified(); x_modified();
return; return;
} }
@ -1617,15 +1611,6 @@ x_bs3(char **p)
x_e_putc2('\b'); x_e_putc2('\b');
} }
static int
x_size_str(char *cp)
{
int size = 0;
while (*cp)
size += x_size2(cp, &cp);
return (size);
}
static int static int
x_size2(char *cp, char **dcp) x_size2(char *cp, char **dcp)
{ {
@ -2066,67 +2051,70 @@ x_cls(int c MKSH_A_UNUSED)
return (KSTD); return (KSTD);
} }
/* output cr (if ≠ 0), then redraw the line, clearing to EOL if needed */ /*
* clear line from x_col (current cursor position) to xx_cols - 2,
* then output lastch, then go back to x_col; if lastch is space,
* clear with termcap instead of spaces, or not if line_was_cleared;
* lastch MUST be an ASCII character with wcwidth(lastch) == 1
*/
static void static void
x_redraw(int cr) x_clrtoeol(int lastch, bool line_was_cleared)
{ {
int i, j; int col;
char *cp;
int limit = xx_cols; /*XXX tbm in the next commit */
x_adj_ok = false; if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
x_e_putc2(cr ? cr : '\r'); shf_puts("\033[K", shl_out);
x_flush(); line_was_cleared = true;
if (xbp == xbuf) { }
if (lastch == ' ' && line_was_cleared)
return;
col = x_col;
while (col < (xx_cols - 2)) {
x_putc(' ');
++col;
}
x_putc(lastch);
++col;
while (col > x_col) {
x_putc('\b');
--col;
}
}
/* output the prompt, assuming a line has just been started */
static void
x_pprompt(void)
{
if (prompt_trunc != -1) if (prompt_trunc != -1)
pprompt(prompt, prompt_trunc); pprompt(prompt, prompt_trunc);
x_col = pwidth; x_col = pwidth;
} }
/* output CR, then redraw the line, clearing to EOL if needed (cr ≠ 0, LF) */
static void
x_redraw(int cr)
{
int lch;
x_adj_ok = false;
/* clear the line */
x_e_putc2(cr ? cr : '\r');
x_flush();
/* display the prompt */
if (xbp == xbuf)
x_pprompt();
x_displen = xx_cols - 2 - x_col; x_displen = xx_cols - 2 - x_col;
/* display the line content */
xlp_valid = false; xlp_valid = false;
x_zots(xbp); x_zots(xbp);
if (limit >= xx_cols || xbp != xbuf || xep > xlp) /* check whether there is more off-screen */
limit = xx_cols; lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' ';
if (limit == xx_cols && x_term_mode == 1 && xbp == xbuf && xep <= xlp) /* clear the rest of the line */
shf_puts("\033[K", shl_out); x_clrtoeol(lch, !cr || cr == '\n');
else if (limit >= 0) { /* go back to actual cursor position */
if (xep > xlp) x_lastpos();
/* we fill the line */
i = 0;
else {
char *cpl = xbp;
i = limit;
while (cpl < xlp)
i -= x_size2(cpl, &cpl);
}
j = 0;
while ((j < i) || (x_col < (xx_cols - 2))) {
if (!(x_col < (xx_cols - 2)))
break;
x_e_putc2(' ');
j++;
}
i = ' ';
if (xep > xlp) {
/* more off screen */
if (xbp > xbuf)
i = '*';
else
i = '>';
} else if (xbp > xbuf)
i = '<';
x_e_putc2(i);
j++;
while (j--)
x_e_putc2('\b');
}
cp = xlp;
while (cp > xcp)
x_bs3(&cp);
x_adj_ok = true; x_adj_ok = true;
return;
} }
static int static int
@ -3240,22 +3228,10 @@ x_fold_case(int c)
* NAME: * NAME:
* x_lastcp - last visible char * x_lastcp - last visible char
* *
* SYNOPSIS:
* x_lastcp()
*
* DESCRIPTION: * DESCRIPTION:
* This function returns a pointer to that char in the * This function returns a pointer to that char in the
* edit buffer that will be the last displayed on the * edit buffer that will be the last displayed on the
* screen. The sequence: * screen.
*
* cp = x_lastcp();
* while (cp > xcp)
* x_bs3(&cp);
*
* Will position the cursor correctly on the screen.
*
* RETURN VALUE:
* cp or NULL
*/ */
static char * static char *
x_lastcp(void) x_lastcp(void)
@ -3277,6 +3253,16 @@ x_lastcp(void)
return (xlp); return (xlp);
} }
/* correctly position the cursor on the screen from end of visible area */
static void
x_lastpos(void)
{
char *cp = x_lastcp();
while (cp > xcp)
x_bs3(&cp);
}
static void static void
x_mode(bool onoff) x_mode(bool onoff)
{ {
@ -5087,9 +5073,7 @@ redraw_line(bool newl)
x_putc('\r'); x_putc('\r');
x_putc('\n'); x_putc('\n');
} }
if (prompt_trunc != -1) x_pprompt();
pprompt(prompt, prompt_trunc);
x_col = pwidth;
morec = ' '; morec = ' ';
} }
@ -5244,9 +5228,7 @@ ed_mov_opt(int col, char *wb)
if (col < x_col) { if (col < x_col) {
if (col + 1 < x_col - col) { if (col + 1 < x_col - col) {
x_putc('\r'); x_putc('\r');
if (prompt_trunc != -1) x_pprompt();
pprompt(prompt, prompt_trunc);
x_col = pwidth;
while (x_col++ < col) while (x_col++ < col)
x_putcf(*wb++); x_putcf(*wb++);
} else { } else {
@ -5539,4 +5521,79 @@ x_initterm(const char *termtype)
if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7)) if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7))
x_term_mode = 1; x_term_mode = 1;
} }
#ifndef MKSH_SMALL
static char *
x_eval_region_helper(const char *cmd, size_t len)
{
char * volatile cp;
newenv(E_ERRH);
if (!kshsetjmp(e->jbuf)) {
char *wds = alloc(len + 3, ATEMP);
wds[0] = FUNSUB;
memcpy(wds + 1, cmd, len);
wds[len + 1] = '\0';
wds[len + 2] = EOS;
cp = evalstr(wds, DOSCALAR);
strdupx(cp, cp, AEDIT);
} else
cp = NULL;
quitenv(NULL);
return (cp);
}
static int
x_eval_region(int c MKSH_A_UNUSED)
{
char *evbeg, *evend, *cp;
size_t newlen;
/* only for LINE overflow checking */
size_t restlen;
if (xmp == NULL) {
evbeg = xbuf;
evend = xep;
} else if (xmp < xcp) {
evbeg = xmp;
evend = xcp;
} else {
evbeg = xcp;
evend = xmp;
}
x_e_putc2('\r');
x_clrtoeol(' ', false);
x_flush();
x_mode(false);
cp = x_eval_region_helper(evbeg, evend - evbeg);
x_mode(true);
if (cp == NULL) {
/* command cannot be parsed */
x_eval_region_err:
x_e_putc2(7);
x_redraw('\r');
return (KSTD);
}
newlen = strlen(cp);
restlen = xep - evend;
/* check for LINE overflow, until this is dynamically allocated */
if (evbeg + newlen + restlen >= xend)
goto x_eval_region_err;
xmp = evbeg;
xcp = evbeg + newlen;
xep = xcp + restlen;
memmove(xcp, evend, restlen + /* NUL */ 1);
memcpy(xmp, cp, newlen);
afree(cp, AEDIT);
x_adjust();
x_modified();
return (KSTD);
}
#endif /* !MKSH_SMALL */
#endif /* !MKSH_NO_CMDLINE_EDITING */ #endif /* !MKSH_NO_CMDLINE_EDITING */

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2009, 2010, 2015 * Copyright (c) 2009, 2010, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -19,7 +19,7 @@
*/ */
#if defined(EMACSFN_DEFNS) #if defined(EMACSFN_DEFNS)
__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.7 2015/12/12 21:08:44 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.8 2016/07/26 21:37:25 tg Exp $");
#define FN(cname,sname,flags) static int x_##cname(int); #define FN(cname,sname,flags) static int x_##cname(int);
#elif defined(EMACSFN_ENUMS) #elif defined(EMACSFN_ENUMS)
#define FN(cname,sname,flags) XFUNC_##cname, #define FN(cname,sname,flags) XFUNC_##cname,
@ -54,6 +54,7 @@ FN(end_of_text, "eot", 0)
FN(enumerate, "list", 0) FN(enumerate, "list", 0)
FN(eot_del, "eot-or-delete", XF_ARG) FN(eot_del, "eot-or-delete", XF_ARG)
FN(error, "error", 0) FN(error, "error", 0)
FN(eval_region, "evaluate-region", 0)
FN(expand, "expand-file", 0) FN(expand, "expand-file", 0)
#ifndef MKSH_SMALL #ifndef MKSH_SMALL
FN(fold_capitalise, "capitalize-word", XF_ARG) FN(fold_capitalise, "capitalize-word", XF_ARG)

9
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.404 2016/07/25 21:05:23 tg Exp $ .\" $MirOS: src/bin/mksh/mksh.1,v 1.405 2016/07/26 21:37:26 tg Exp $
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $ .\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
.\"- .\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -76,7 +76,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to .\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always. .\" use our own definition. And .Dd must come *first*, always.
.\" .\"
.Dd $Mdocdate: July 25 2016 $ .Dd $Mdocdate: July 26 2016 $
.\" .\"
.\" Check which macro package we use, and do other -mdoc setup. .\" Check which macro package we use, and do other -mdoc setup.
.\" .\"
@ -5618,6 +5618,11 @@ otherwise,
.Ic delete\-char\-forward . .Ic delete\-char\-forward .
.It error: (not bound) .It error: (not bound)
Error (ring the bell). Error (ring the bell).
.It evaluate\-region: \*(ha[\*(haE
Evaluates the text between the mark and the cursor position
.Pq the entire line if no mark is set
as function substitution (if it cannot be parsed, the editing state is
unchanged and the bell is rung to signal an error); $? is updated accordingly.
.It exchange\-point\-and\-mark: \*(haX\*(haX .It exchange\-point\-and\-mark: \*(haX\*(haX
Places the cursor where the mark is and sets the mark to where the cursor was. Places the cursor where the mark is and sets the mark to where the cursor was.
.It expand\-file: \*(ha[* .It expand\-file: \*(ha[*