From 2c5ea79f1762dcbafca8d08bc5bdb47d75670f03 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 3 Apr 2008 11:38:55 +0000 Subject: 24782: initial go at highlighting of characters in zle command lines --- Src/Zle/zle.h | 31 ++ Src/Zle/zle_main.c | 2 + Src/Zle/zle_move.c | 11 + Src/Zle/zle_params.c | 4 + Src/Zle/zle_refresh.c | 1076 ++++++++++++++++++++++++++++++++++++++----------- Src/Zle/zle_utils.c | 11 +- 6 files changed, 896 insertions(+), 239 deletions(-) (limited to 'Src/Zle') diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index be6976a31..44450ab16 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -325,6 +325,37 @@ enum suffixtype { SUFTYP_NEGRNG /* Range of characters not to match */ }; + +#ifdef MULTIBYTE_SUPPORT +/* + * We use a wint_t here, since we need an invalid character as a + * placeholder and wint_t guarantees that we can use WEOF to do this. + */ +typedef wint_t REFRESH_CHAR; +#else +typedef char REFRESH_CHAR; +#endif + +/* + * Description of one screen cell in zle_refresh.c + */ +typedef struct { + /* The (possibly wide) character */ + REFRESH_CHAR chr; + /* + * Its attributes. 'On' attributes (TXT_ATTR_ON_MASK) are + * applied before the character, 'off' attributes (TXT_ATTR_OFF_MASK) + * after it. 'On' attributes are present for all characters that + * need the effect; 'off' attributes are only present for the + * last character in the sequence. + */ + REFRESH_CHAR atr; +} REFRESH_ELEMENT; + +/* A string of screen cells */ +typedef REFRESH_ELEMENT *REFRESH_STRING; + + #ifdef DEBUG #define METACHECK() \ DPUTS(zlemetaline == NULL, "line not metafied") diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 0b109cd19..5c36f4557 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1076,6 +1076,7 @@ zlecore(void) freeheap(); } + region_active = 0; popheap(); } @@ -1933,6 +1934,7 @@ finish_(UNUSED(Module m)) getkeyptr = NULL; zfree(clwords, clwsize * sizeof(char *)); + zle_refresh_finish(); return 0; } diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c index 9b91f8885..7f2748da6 100644 --- a/Src/Zle/zle_move.c +++ b/Src/Zle/zle_move.c @@ -183,7 +183,12 @@ backwardchar(UNUSED(char **args)) int setmarkcommand(UNUSED(char **args)) { + if (zmult < 0) { + region_active = 0; + return 0; + } mark = zlecs; + region_active = 1; return 0; } @@ -193,11 +198,17 @@ exchangepointandmark(UNUSED(char **args)) { int x; + if (zmult == 0) { + region_active = 1; + return 0; + } x = mark; mark = zlecs; zlecs = x; if (zlecs > zlell) zlecs = zlell; + if (zmult > 0) + region_active = 1; return 0; } diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index 817d6e57a..f46a02f05 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -90,6 +90,9 @@ static const struct gsu_integer pending_gsu = static const struct gsu_array killring_gsu = { get_killring, set_killring, unset_killring }; +/* implementation is in zle_refresh.c */ +static const struct gsu_array region_highlight_gsu = +{ get_region_highlight, set_region_highlight, unset_region_highlight }; #define GSU(X) ( (GsuScalar)(void*)(&(X)) ) static struct zleparam { @@ -120,6 +123,7 @@ static struct zleparam { { "PREBUFFER", PM_SCALAR | PM_READONLY, GSU(prebuffer_gsu), NULL }, { "PREDISPLAY", PM_SCALAR, GSU(predisplay_gsu), NULL }, { "RBUFFER", PM_SCALAR, GSU(rbuffer_gsu), NULL }, + { "region_highlight", PM_ARRAY, GSU(region_highlight_gsu), NULL }, { "WIDGET", PM_SCALAR | PM_READONLY, GSU(widget_gsu), NULL }, { "WIDGETFUNC", PM_SCALAR | PM_READONLY, GSU(widgetfunc_gsu), NULL }, { "WIDGETSTYLE", PM_SCALAR | PM_READONLY, GSU(widgetstyle_gsu), NULL }, diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 3f3de2c68..34cadb0eb 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -29,67 +29,53 @@ #include "zle.mdh" -#ifdef MULTIBYTE_SUPPORT -/* - * We use a wint_t here, since we need an invalid character as a - * placeholder and wint_t guarantees that we can use WEOF to do this. - */ -typedef wint_t *REFRESH_STRING; -typedef wint_t REFRESH_CHAR; +#define ZR_equal(zr1, zr2) ((zr1).chr == (zr2).chr && (zr1).atr == (zr2).atr) -/* - * Unfortunately, that means the pointer is the wrong type for - * wmemset and friends. - */ static void -ZR_memset(wint_t *dst, wchar_t wc, int len) +ZR_memset(REFRESH_ELEMENT *dst, REFRESH_ELEMENT rc, int len) { while (len--) - *dst++ = wc; + *dst++ = rc; } -#define ZR_memcpy(d, s, l) memcpy((d), (s), (l)*sizeof(wint_t)) + +#define ZR_memcpy(d, s, l) memcpy((d), (s), (l)*sizeof(REFRESH_ELEMENT)) + static void -ZR_strcpy(wint_t *dst, wint_t *src) +ZR_strcpy(REFRESH_ELEMENT *dst, const REFRESH_ELEMENT *src) { - while ((*dst++ = *src++) != L'\0') + while ((*dst++ = *src++).chr != ZWC('\0')) ; } + static size_t -ZR_strlen(wint_t *wstr) +ZR_strlen(const REFRESH_ELEMENT *wstr) { int len = 0; - while (*wstr++ != L'\0') + while (wstr++->chr != ZWC('\0')) len++; return len; } + /* * Simplified strcmp: we don't need the sign, just whether - * the strings are equal. + * the strings and their attributes are equal. */ static int -ZR_strncmp(wint_t *wstr1, wint_t *wstr2, int len) +ZR_strncmp(const REFRESH_ELEMENT *wstr1, const REFRESH_ELEMENT *wstr2, int len) { while (len--) { - if (!*wstr1 || !*wstr2) - return (*wstr1 == *wstr2) ? 0 : 1; - if (*wstr1++ != *wstr2++) + if (!wstr1->chr || !wstr2->chr) + return !ZR_equal(*wstr1, *wstr2); + if (!ZR_equal(*wstr1, *wstr2)) return 1; + wstr1++; + wstr2++; } return 0; } -#else -typedef char *REFRESH_STRING; -typedef char REFRESH_CHAR; - -#define ZR_memset memset -#define ZR_memcpy memcpy -#define ZR_strcpy strcpy -#define ZR_strlen strlen -#define ZR_strncmp strncmp -#endif #include "zle_refresh.pro" @@ -160,52 +146,407 @@ ZLE_STRING_T predisplay, postdisplay; int predisplaylen, postdisplaylen; +/* + * Attributes used for highlighting special (unprintable) characters + * displayed on screen. + */ + +static int special_atr_on, special_atr_off; + +/* Flags for the region_highlight structure */ +enum { + /* Offsets include predisplay */ + ZRH_PREDISPLAY = 1 +}; + +/* + * Attributes used for highlighting regions. + * and mark. + */ +struct region_highlight { + /* Attributes turned on in the region */ + int atr; + /* Start of the region */ + int start; + /* + * End of the region: position of the first character not highlighted + * (the same system as for point and mark). + */ + int end; + /* + * Any of the flags defined above. + */ + int flags; +}; +/* + * Array of region highlights, no special termination. + * The first element (0) always describes the region between + * point and mark. Any other elements are set by the user + * via the parameter region_highlight. + */ +struct region_highlight *region_highlights; +/* + * Number of elements in region_highlights. + * This includes the region between point and mark, element 0. + */ +int n_region_highlights; + +/* + * Flag that highlighting of the region is active. + */ +/**/ +int region_active; + #ifdef HAVE_SELECT /* cost of last update */ /**/ int cost; -# define SELECT_ADD_COST(X) cost += X -# define zputc(a) zwcputc(a), cost++ -# define zwrite(a, b) zwcwrite(a, b), cost += (b * ZLE_CHAR_SIZE) +# define SELECT_ADD_COST(X) (cost += X) +# define zputc(a) (zwcputc(a, NULL), cost++) +# define zwrite(a, b) (zwcwrite((a), (b)), \ + cost += ((b) * ZLE_CHAR_SIZE)) #else # define SELECT_ADD_COST(X) -# define zputc(a) zwcputc(a) -# define zwrite(a, b) zwcwrite(a, b) +# define zputc(a) zwcputc(a, NULL) +# define zwrite(a, b) zwcwrite((a), (b)) #endif +static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 }; +static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 }; +static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 }; +static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 }; +static const REFRESH_ELEMENT zr_ht = { ZWC('\t'), 0 }; +static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 }; + +/* + * Constant arrays to be copied into place: these are memcpy'd, + * so don't have terminating NULLs. + */ +static const REFRESH_ELEMENT zr_end_ellipsis[] = { + { ZWC(' '), 0 }, + { ZWC('<'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC(' '), 0 }, +}; +#define ZR_END_ELLIPSIS_SIZE \ + (sizeof(zr_end_ellipsis)/sizeof(zr_end_ellipsis[0])) + +static const REFRESH_ELEMENT zr_mid_ellipsis1[] = { + { ZWC(' '), 0 }, + { ZWC('<'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, +}; +#define ZR_MID_ELLIPSIS1_SIZE \ + (sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0])) + +static const REFRESH_ELEMENT zr_mid_ellipsis2[] = { + { ZWC('>'), 0 }, + { ZWC(' '), 0 }, +}; +#define ZR_MID_ELLIPSIS2_SIZE \ + (sizeof(zr_mid_ellipsis2)/sizeof(zr_mid_ellipsis2[0])) + +static const REFRESH_ELEMENT zr_start_ellipsis[] = { + { ZWC('>'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, + { ZWC('.'), 0 }, +}; +#define ZR_START_ELLIPSIS_SIZE \ + (sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0])) + + +/* Defines the available types of highlighting */ +struct highlight { + const char *name; + int mask_on; + int mask_off; +}; + +static const struct highlight highlights[] = { + { "none", 0, TXT_ATTR_ON_MASK }, + { "bold", TXTBOLDFACE, 0 }, + { "standout", TXTSTANDOUT, 0 }, + { "underline", TXTUNDERLINE, 0 }, + { NULL, 0, 0 } +}; + +/* + * Match a set of highlights in the given teststr. + * Set *on_var to reflect the values found. + */ + +static void +match_highlight(const char *teststr, int *on_var) +{ + int found = 1; + + *on_var = 0; + while (found && *teststr) { + const struct highlight *hl; + + found = 0; + for (hl = highlights; hl->name; hl++) { + if (strpfx(hl->name, teststr)) { + const char *val = teststr + strlen(hl->name); + + if (*val == ',') + val++; + else if (*val) + break; + + *on_var |= hl->mask_on; + *on_var &= ~hl->mask_off; + teststr = val; + found = 1; + } + } + } +} + + +/* + * Parse the variable zle_highlight to decide how to highlight characters + * and regions. Set defaults for anything not explicitly covered. + */ + +/**/ +void zle_set_highlight(void) +{ + char **atrs = getaparam("zle_highlight"); + int special_atr_on_set = 0; + int region_atr_on_set = 0; + + special_atr_on = 0; + if (!region_highlights) { + region_highlights = (struct region_highlight *) + zshcalloc(sizeof(struct region_highlight)); + n_region_highlights = 1; + } else { + region_highlights->atr = 0; + } + + if (atrs) { + for (; *atrs; atrs++) { + if (!strcmp(*atrs, "none")) { + /* reset attributes for consistency... usually unnecessary */ + special_atr_on = region_highlights->atr = 0; + special_atr_on_set = region_atr_on_set = 1; + } else if (strpfx("special:", *atrs)) { + match_highlight(*atrs + 8, &special_atr_on); + special_atr_on_set = 1; + } else if (strpfx("region:", *atrs)) { + match_highlight(*atrs + 7, ®ion_highlights->atr); + region_atr_on_set = 1; + } + } + } + + /* Defaults */ + if (!special_atr_on_set) + special_atr_on = TXTSTANDOUT; + if (!region_atr_on_set) + region_highlights->atr = TXTSTANDOUT; + special_atr_off = special_atr_on << TXT_ATTR_OFF_ON_SHIFT; +} + + +/* + * Interface to the region_highlight ZLE parameter. + * Converts betwen a format like "P32 42 underline,bold" to + * the format in the region_highlights variable. Note that + * the region_highlights variable stores the internal (point/mark) + * region in element zero. + */ + +/**/ +char ** +get_region_highlight(UNUSED(Param pm)) +{ + int arrsize = n_region_highlights; + char **retarr, **arrp; + + /* region_highlights may not have been set yet */ + if (!arrsize) + arrsize = 1; + arrp = retarr = (char **)zhalloc(arrsize*sizeof(char *)); + /* ignore NULL termination */ + arrsize--; + if (arrsize) { + struct region_highlight *rhp; + + /* ignore point/mark at start */ + for (rhp = region_highlights+1; arrsize--; rhp++, arrp++) { + char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE]; + int atrlen = 0, alloclen, done1; + const struct highlight *hp; + + sprintf(digbuf1, "%d", rhp->start); + sprintf(digbuf2, "%d", rhp->end); + + for (hp = highlights; hp->name; hp++) { + if (hp->mask_on & rhp->atr) { + if (atrlen) + atrlen++; /* comma */ + atrlen += strlen(hp->name); + } + } + if (atrlen == 0) + atrlen = 4; /* none */ + alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) + + 3; /* 2 spaces, 1 0 */ + if (rhp->flags & ZRH_PREDISPLAY) + alloclen += 2; /* "P " */ + *arrp = (char *)zhalloc(alloclen * sizeof(char)); + /* + * On input we allow a space after the flags. + * I haven't put a space here because I think it's + * marginally easier to have the output always split + * into three words, and then check the first to + * see if there are flags. However, it's arguable. + */ + sprintf(*arrp, "%s%s %s ", + (rhp->flags & ZRH_PREDISPLAY) ? "P" : "", + digbuf1, digbuf2); + if (atrlen) { + for (hp = highlights, done1 = 0; hp->name; hp++) { + if (hp->mask_on & rhp->atr) { + if (done1) + strcat(*arrp, ","); + strcat(*arrp, hp->name); + done1 = 1; + } + } + } else + strcat(*arrp, "none"); + } + } + *arrp = '\0'; + return retarr; +} + + +/**/ +void +set_region_highlight(UNUSED(Param pm), char **aval) +{ + int len; + struct region_highlight *rhp; + + len = aval ? arrlen(aval) : 0; + if (n_region_highlights != len + 1) { + /* no null termination, but include point/mark region at start */ + n_region_highlights = len + 1; + region_highlights = (struct region_highlight *) + zrealloc(region_highlights, + sizeof(struct region_highlight) * n_region_highlights); + } + + if (!aval) + return; + + for (rhp = region_highlights + 1; *aval; rhp++, aval++) { + char *strp, *oldstrp; + + oldstrp = *aval; + if (*oldstrp == 'P') { + rhp->flags = ZRH_PREDISPLAY; + oldstrp++; + } + else + rhp->flags = 0; + while (inblank(*oldstrp)) + oldstrp++; + + rhp->start = (int)zstrtol(oldstrp, &strp, 10); + if (strp == oldstrp) + rhp->start = -1; + + while (inblank(*strp)) + strp++; + + oldstrp = strp; + rhp->end = (int)zstrtol(strp, &strp, 10); + if (strp == oldstrp) + rhp->end = -1; + + while (inblank(*strp)) + strp++; + + match_highlight(strp, &rhp->atr); + } +} + + /**/ void -zwcputc(ZLE_INT_T c) +unset_region_highlight(Param pm, int exp) +{ + if (exp) { + set_region_highlight(pm, NULL); + stdunsetfn(pm, exp); + } +} + + +/**/ +void +zwcputc(const REFRESH_ELEMENT *c, REFRESH_CHAR *curatrp) { #ifdef MULTIBYTE_SUPPORT mbstate_t mbstate; int i; VARARR(char, mbtmp, MB_CUR_MAX + 1); +#endif - if (c == WEOF) - return; + /* + * Don't output "on" attributes in a string of characters with + * the same attributes. + */ + if ((c->atr & TXT_ATTR_ON_MASK) && + (!curatrp || + ((*curatrp & TXT_ATTR_ON_MASK) != (c->atr & TXT_ATTR_ON_MASK)))) + settextattributes(c->atr & TXT_ATTR_ON_MASK); - memset(&mbstate, 0, sizeof(mbstate_t)); - if ((i = wcrtomb(mbtmp, (wchar_t)c, &mbstate)) > 0) - fwrite(mbtmp, i, 1, shout); +#ifdef MULTIBYTE_SUPPORT + if (c->chr != WEOF) { + memset(&mbstate, 0, sizeof(mbstate_t)); + if ((i = wcrtomb(mbtmp, (wchar_t)c->chr, &mbstate)) > 0) + fwrite(mbtmp, i, 1, shout); + } #else - fputc(c, shout); + fputc(c->chr, shout); #endif + + if (c->atr & TXT_ATTR_OFF_MASK) + settextattributes(c->atr & TXT_ATTR_OFF_MASK); + if (curatrp) { + /* + * Remember the current attributes: those that are turned + * on, less those that are turned off again. + */ + *curatrp = (c->atr & TXT_ATTR_ON_MASK) & + ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); + } } static int -zwcwrite(REFRESH_STRING s, size_t i) +zwcwrite(const REFRESH_STRING s, size_t i) { -#ifdef MULTIBYTE_SUPPORT size_t j; + REFRESH_CHAR curatr = 0; for (j = 0; j < i; j++) - zwcputc(s[j]); + zwcputc(s + j, &curatr); return i; /* TODO something better for error indication */ -#else - return fwrite(s, i, 1, shout); -#endif } /* Oct/Nov 94: some code savagely redesigned to fix several bugs - @@ -214,8 +555,8 @@ zwcwrite(REFRESH_STRING s, size_t i) any queries about updates to mason@primenet.com.au */ static REFRESH_STRING - *nbuf = NULL, /* new video buffer line-by-line char array */ - *obuf = NULL; /* old video buffer line-by-line char array */ + *nbuf = NULL, /* new video buffer line-by-line array */ + *obuf = NULL; /* old video buffer line-by-line array */ static int more_start, /* more text before start of screen? */ more_end, /* more stuff after end of screen? */ olnct, /* previous number of lines */ @@ -228,14 +569,31 @@ static int more_start, /* more text before start of screen? */ vmaxln, /* video maximum number of lines */ winw, winh, rwinh, /* window width & height */ winpos, /* singlelinezle: line's position in window */ - winprompt; /* singlelinezle: part of lprompt showing */ + winprompt, /* singlelinezle: part of lprompt showing */ + winw_alloc = -1, /* allocated window width */ + winh_alloc = -1; /* allocates window height */ + +static void +freevideo(void) +{ + if (nbuf) { + int ln; + for (ln = 0; ln != winh_alloc; ln++) { + zfree(nbuf[ln], (winw_alloc + 2) * sizeof(**nbuf)); + zfree(obuf[ln], (winw_alloc + 2) * sizeof(**obuf)); + } + free(nbuf); + free(obuf); + nbuf = NULL; + obuf = NULL; + } +} /**/ void resetvideo(void) { int ln; - static int lwinw = -1, lwinh = -1; /* last window width & height */ winw = columns; /* terminal width */ if (termflags & TERM_SHORT) @@ -245,31 +603,24 @@ resetvideo(void) rwinh = lines; /* keep the real number of lines */ vln = vmaxln = winprompt = 0; winpos = -1; - if (lwinw != winw || lwinh != winh) { - if (nbuf) { - for (ln = 0; ln != lwinh; ln++) { - zfree(nbuf[ln], (lwinw + 2) * sizeof(**nbuf)); - zfree(obuf[ln], (lwinw + 2) * sizeof(**obuf)); - } - free(nbuf); - free(obuf); - } + if (winw_alloc != winw || winh_alloc != winh) { + freevideo(); nbuf = (REFRESH_STRING *)zshcalloc((winh + 1) * sizeof(*nbuf)); obuf = (REFRESH_STRING *)zshcalloc((winh + 1) * sizeof(*obuf)); nbuf[0] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf)); obuf[0] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**obuf)); - lwinw = winw; - lwinh = winh; + winw_alloc = winw; + winh_alloc = winh; } for (ln = 0; ln != winh + 1; ln++) { if (nbuf[ln]) { - nbuf[ln][0] = ZWC('\n'); - nbuf[ln][1] = ZWC('\0'); + nbuf[ln][0] = zr_nl; + nbuf[ln][1] = zr_zr; } if (obuf[ln]) { - obuf[ln][0] = ZWC('\n'); - obuf[ln][1] = ZWC('\0'); + obuf[ln][0] = zr_nl; + obuf[ln][1] = zr_zr; } } @@ -286,9 +637,9 @@ resetvideo(void) } if (lpromptw) { - ZR_memset(nbuf[0], ZWC(' '), lpromptw); - ZR_memset(obuf[0], ZWC(' '), lpromptw); - nbuf[0][lpromptw] = obuf[0][lpromptw] = ZWC('\0'); + ZR_memset(nbuf[0], zr_sp, lpromptw); + ZR_memset(obuf[0], zr_sp, lpromptw); + nbuf[0][lpromptw] = obuf[0][lpromptw] = zr_zr; } vcs = lpromptw; @@ -350,8 +701,8 @@ static int cleareol, /* clear to end-of-line (if can't cleareod) */ static int nextline(Rparams rpms, int wrapped) { - nbuf[rpms->ln][winw+1] = wrapped ? ZWC('\n') : ZWC('\0'); - *rpms->s = ZWC('\0'); + nbuf[rpms->ln][winw+1] = wrapped ? zr_nl : zr_zr; + *rpms->s = zr_zr; if (rpms->ln != winh - 1) rpms->ln++; else { @@ -383,7 +734,7 @@ nextline(Rparams rpms, int wrapped) static void snextline(Rparams rpms) { - *rpms->s = ZWC('\0'); + *rpms->s = zr_zr; if (rpms->ln != winh - 1) rpms->ln++; else @@ -415,19 +766,19 @@ snextline(Rparams rpms) /**/ static void -settextattributes(void) +settextattributes(int atr) { - if (txtchangeisset(TXTNOBOLDFACE)) + if (txtchangeisset(atr, TXTNOBOLDFACE)) tsetcap(TCALLATTRSOFF, 0); - if (txtchangeisset(TXTNOSTANDOUT)) + if (txtchangeisset(atr, TXTNOSTANDOUT)) tsetcap(TCSTANDOUTEND, 0); - if (txtchangeisset(TXTNOUNDERLINE)) + if (txtchangeisset(atr, TXTNOUNDERLINE)) tsetcap(TCUNDERLINEEND, 0); - if (txtchangeisset(TXTBOLDFACE)) + if (txtchangeisset(atr, TXTBOLDFACE)) tsetcap(TCBOLDFACEBEG, 0); - if (txtchangeisset(TXTSTANDOUT)) + if (txtchangeisset(atr, TXTSTANDOUT)) tsetcap(TCSTANDOUTBEG, 0); - if (txtchangeisset(TXTUNDERLINE)) + if (txtchangeisset(atr, TXTUNDERLINE)) tsetcap(TCUNDERLINEBEG, 0); } @@ -435,20 +786,21 @@ settextattributes(void) mod_export void zrefresh(void) { - static int inlist; /* avoiding recursion */ - int iln; /* current line as index in loops */ + static int inlist; /* avoiding recursion */ + int iln; /* current line as index in loops */ int t0 = -1; /* tmp */ - ZLE_STRING_T tmpline, /* line with added pre/post text */ + ZLE_STRING_T tmpline, /* line with added pre/post text */ t, /* pointer into the real buffer */ scs, /* pointer to cursor position in real buffer */ - u; /* pointer for status line stuff */ - REFRESH_STRING *qbuf; /* tmp */ + u; /* pointer for status line stuff */ + REFRESH_STRING *qbuf; /* tmp */ int tmpcs, tmpll; /* ditto cursor position and line length */ - int tmpalloced; /* flag to free tmpline when finished */ - int remetafy; /* flag that zle line is metafied */ + int tmppos; /* t - tmpline */ + int tmpalloced; /* flag to free tmpline when finished */ + int remetafy; /* flag that zle line is metafied */ struct rparams rpms; #ifdef MULTIBYTE_SUPPORT - int width; /* width of wide character */ + int width; /* width of wide character */ #endif @@ -491,6 +843,21 @@ zrefresh(void) tmpalloced = 0; } + /* this will create region_highlights if it's still NULL */ + zle_set_highlight(); + + if (region_active) { + if (zlecs <= mark) { + region_highlights->start = zlecs; + region_highlights->end = mark; + } else { + region_highlights->start = mark; + region_highlights->end = zlecs; + } + } else { + region_highlights->start = region_highlights->end = -1; + } + if (clearlist && listshown > 0) { if (tccan(TCCLEAREOD)) { int ovln = vln, ovcs = vcs; @@ -574,10 +941,10 @@ zrefresh(void) zputs("\n", shout); /* works with both hasam and !hasam */ } else { txtchange = pmpt_attr; - settextattributes(); + settextattributes(txtchange); } if (clearflag) { - zputc(ZWC('\r')); + zputc(&zr_cr); vcs = 0; moveto(0, lpromptw); } @@ -615,9 +982,33 @@ zrefresh(void) rpms.nvln = -1; rpms.s = nbuf[rpms.ln = 0] + lpromptw; - t = tmpline; rpms.sen = *nbuf + winw; - for (; t < tmpline+tmpll; t++) { + for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { + int base_atr_on = 0, base_atr_off = 0, ireg; + struct region_highlight *rhp; + /* + * Calculate attribute based on region. + * HERE: we may need to be smarter about turning + * attributes off if bailing out before the end of the + * region. + */ + for (ireg = 0, rhp = region_highlights; + ireg < n_region_highlights; + ireg++, rhp++) { + int offset; + if (rhp->flags & ZRH_PREDISPLAY) + offset = 0; /* include predisplay in start end */ + else + offset = predisplaylen; /* increment over it */ + if (rhp->start + offset <= tmppos && + tmppos < rhp->end + offset) { + base_atr_on |= rhp->atr; + if (tmppos == rhp->end + offset - 1 || + tmppos == tmpll - 1) + base_atr_off |= rhp->atr << TXT_ATTR_OFF_ON_SHIFT; + } + } + if (t == scs) /* if cursor is here, remember it */ rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln]; @@ -631,20 +1022,32 @@ zrefresh(void) /* text wrapped */ if (nextline(&rpms, 1)) break; - } else - do - *rpms.s++ = ZWC(' '); - while ((++t0) & 7); + } else { + do { + rpms.s->chr = ZWC(' '); + rpms.s->atr = base_atr_on; + rpms.s++; + } while ((++t0) & 7); + rpms.s[-1].atr |= base_atr_off; + } } #ifdef MULTIBYTE_SUPPORT else if (iswprint(*t) && (width = wcwidth(*t)) > 0) { if (width > rpms.sen - rpms.s) { + int started = 0; /* * Too wide to fit. Insert spaces to end of current line. */ do { - *rpms.s++ = ZWC(' '); + /* HERE highlight */ + rpms.s->chr = ZWC(' '); + if (!started) + started = 1; + rpms.s->atr = special_atr_on | base_atr_on; + rpms.s++; } while (rpms.s < rpms.sen); + if (started) + rpms.s[-1].atr |= special_atr_off | base_atr_off; if (nextline(&rpms, 1)) break; if (t == scs) { @@ -656,13 +1059,28 @@ zrefresh(void) /* * The screen width is too small to fit even one * occurrence. + * + * HERE highlight */ - *rpms.s++ = ZWC('?'); + rpms.s->chr = ZWC('?'); + rpms.s->atr = special_atr_on | special_atr_off | + base_atr_on | base_atr_off; + rpms.s++; } else { /* We can fit it without reaching the end of the line. */ - *rpms.s++ = *t; - while (--width > 0) - *rpms.s++ = WEOF; + rpms.s->chr = *t; + /* + * As we don't actually output the WEOF, we attach + * any off attributes to the character itself. + */ + rpms.s->atr = base_atr_on | base_atr_off; + rpms.s++; + while (--width > 0) { + rpms.s->chr = WEOF; + /* Not used, but be consistent... */ + rpms.s->atr = base_atr_on | base_atr_off; + rpms.s++; + } } } #endif @@ -671,23 +1089,34 @@ zrefresh(void) && (unsigned)*t <= 0xffU #endif ) { /* other control character */ - *rpms.s++ = ZWC('^'); + /* HERE highlight */ + rpms.s->chr = ZWC('^'); + rpms.s->atr = special_atr_on | base_atr_on; + rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ + rpms.s[-1].atr |= special_atr_off | base_atr_off; if (nextline(&rpms, 1)) break; } - *rpms.s++ = (((unsigned int)*t & ~0x80u) > 31) ? ZWC('?') : (*t | ZWC('@')); + rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ? + ZWC('?') : (*t | ZWC('@')); + rpms.s->atr = special_atr_on | special_atr_off | + base_atr_on | base_atr_off; + rpms.s++; } #ifdef MULTIBYTE_SUPPORT else { /* * Not printable or zero width. * Resort to hackery. + * + * HERE: highlight */ char dispchars[11]; char *dispptr = dispchars; wchar_t wc; + int started = 0; if ((unsigned)*t > 0xffffU) { sprintf(dispchars, "<%.08x>", (unsigned)*t); @@ -697,21 +1126,33 @@ zrefresh(void) while (*dispptr) { if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { - *rpms.s++ = wc; + rpms.s->chr = wc; + if (!started) + started = 1; + rpms.s->atr = special_atr_on | base_atr_on; + rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ + if (started) { + rpms.s[-1].atr |= special_atr_off | base_atr_off; + started = 0; + } if (nextline(&rpms, 1)) break; } } dispptr++; } + if (started) + rpms.s[-1].atr |= special_atr_off | base_atr_off; if (*dispptr) /* nextline said stop processing */ break; } #else else { /* normal character */ - *rpms.s++ = *t; + rpms.s->chr = *t; + rpms.s->atr = base_atr_on | base_atr_off; + rpms.s++; } #endif if (rpms.s == rpms.sen) { @@ -726,7 +1167,7 @@ zrefresh(void) (rpms.nvcs = rpms.s - (nbuf[rpms.nvln = rpms.ln])) == winw) { /* text wrapped */ (void)nextline(&rpms, 1); - *rpms.s = ZWC('\0'); + *rpms.s = zr_zr; rpms.nvcs = 0; rpms.nvln++; } @@ -736,7 +1177,7 @@ zrefresh(void) if (statusline) { rpms.tosln = rpms.ln + 1; - nbuf[rpms.ln][winw + 1] = ZWC('\0'); /* text not wrapped */ + nbuf[rpms.ln][winw + 1] = zr_zr; /* text not wrapped */ snextline(&rpms); u = statusline; for (; u < statusline + statusll; u++) { @@ -746,32 +1187,49 @@ zrefresh(void) /* Handle wide characters as above */ if (width > rpms.sen - rpms.s) { do { - *rpms.s++ = ZWC(' '); + *rpms.s++ = zr_sp; } while (rpms.s < rpms.sen); - nbuf[rpms.ln][winw + 1] = ZWC('\n'); + nbuf[rpms.ln][winw + 1] = zr_nl; snextline(&rpms); } if (width > rpms.sen - rpms.s) { - *rpms.s++ = ZWC('?'); + /* HERE: highlight */ + rpms.s->chr = ZWC('?'); + rpms.s->atr = special_atr_on | special_atr_off; + rpms.s++; } else { - *rpms.s++ = *u; - while (--width > 0) - *rpms.s++ = WEOF; + rpms.s->chr = *u; + rpms.s->atr = 0; + rpms.s++; + while (--width > 0) { + rpms.s->chr = WEOF; + rpms.s->atr = 0; + rpms.s++; + } } } else #endif if (ZC_icntrl(*u)) { /* simplified processing in the status line */ - *rpms.s++ = ZWC('^'); + /* HERE: highlight */ + rpms.s->chr = ZWC('^'); + rpms.s->atr = special_atr_on; + rpms.s++; if (rpms.s == rpms.sen) { - nbuf[rpms.ln][winw + 1] = ZWC('\n');/* text wrapped */ + nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */ snextline(&rpms); } - *rpms.s++ = (((unsigned int)*u & ~0x80u) > 31) ? ZWC('?') : (*u | ZWC('@')); - } else - *rpms.s++ = *u; + rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31) + ? ZWC('?') : (*u | ZWC('@')); + rpms.s->atr = special_atr_on | special_atr_off; + rpms.s++; + } else { + rpms.s->chr = *u; + rpms.s->atr = 0; + rpms.s++; + } if (rpms.s == rpms.sen) { - nbuf[rpms.ln][winw + 1] = ZWC('\n'); /* text wrapped */ + nbuf[rpms.ln][winw + 1] = zr_nl; /* text wrapped */ snextline(&rpms); } } @@ -783,42 +1241,82 @@ zrefresh(void) snextline(&rpms); } } - *rpms.s = ZWC('\0'); + *rpms.s = zr_zr; /* insert <.... at end of last line if there is more text past end of screen */ -/* TODO: if we start overwriting in the middle of a wide character, mayhem - * will ensue. - */ if (more_end) { +#ifdef MULTIBYTE_SUPPORT + int extra_ellipsis = 0; +#endif if (!statusline) rpms.tosln = winh; rpms.s = nbuf[rpms.tosln - 1]; rpms.sen = rpms.s + winw - 7; for (; rpms.s < rpms.sen; rpms.s++) { - if (*rpms.s == ZWC('\0')) { - for (; rpms.s < rpms.sen; ) - *rpms.s++ = ZWC(' '); + if (rpms.s->chr == ZWC('\0')) { + ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s); + /* make sure we don't trigger the WEOF test */ + rpms.sen->chr = ZWC('\0'); break; } } - ZR_memcpy(rpms.sen, ZWS(" <.... "), 7); - nbuf[rpms.tosln - 1][winw] = nbuf[rpms.tosln - 1][winw + 1] - = ZWC('\0'); + /* rpms.s is no longer needed */ +#ifdef MULTIBYTE_SUPPORT + /* + * Ensure we don't start overwriting in the middle of a wide + * character. + */ + while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) { + extra_ellipsis++; + rpms.sen--; + } +#endif + ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE); +#ifdef MULTIBYTE_SUPPORT + /* Extend to the end if we backed off for a wide character */ + if (extra_ellipsis) { + rpms.sen += ZR_END_ELLIPSIS_SIZE; + ZR_memset(rpms.sen, zr_dt, extra_ellipsis); + } +#endif + nbuf[rpms.tosln - 1][winw] = nbuf[rpms.tosln - 1][winw + 1] = zr_zr; } /* insert <....> at end of first status line if status is too big */ if (rpms.more_status) { +#ifdef MULTIBYTE_SUPPORT + int extra_ellipsis = 0; +#endif rpms.s = nbuf[rpms.tosln]; rpms.sen = rpms.s + winw - 8; for (; rpms.s < rpms.sen; rpms.s++) { - if (*rpms.s == ZWC('\0')) { - for (; rpms.s < rpms.sen; ) - *rpms.s++ = ZWC(' '); + if (rpms.s->chr == ZWC('\0')) { + ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s); break; } } - ZR_memcpy(rpms.sen, ZWS(" <....> "), 8); - nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = ZWC('\0'); + /* rpms.s is no longer needed */ +#ifdef MULTIBYTE_SUPPORT + /* + * Ensure we don't start overwriting in the middle of a wide + * character. + */ + while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) { + extra_ellipsis++; + rpms.sen--; + } +#endif + ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE); + rpms.sen += ZR_MID_ELLIPSIS1_SIZE; +#ifdef MULTIBYTE_SUPPORT + /* Extend if we backed off for a wide character */ + if (extra_ellipsis) { + ZR_memset(rpms.sen, zr_dt, extra_ellipsis); + rpms.sen += extra_ellipsis; + } +#endif + ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE); + nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr; } nlnct = rpms.ln + 1; @@ -837,12 +1335,12 @@ zrefresh(void) (int)ZR_strlen(nbuf[0]) + rpromptw < winw - 1; } else { /* insert >.... on first line if there is more text before start of screen */ - ZR_memset(nbuf[0], ZWC(' '), lpromptw); + ZR_memset(nbuf[0], zr_sp, lpromptw); t0 = winw - lpromptw; - t0 = t0 > 5 ? 5 : t0; - ZR_memcpy(nbuf[0] + lpromptw, ZWS(">...."), t0); - ZR_memset(nbuf[0] + lpromptw + t0, ZWC(' '), winw - t0 - lpromptw); - nbuf[0][winw] = nbuf[0][winw + 1] = ZWC('\0'); + t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0; + ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0); + ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw); + nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr; } for (iln = 0; iln < nlnct; iln++) { @@ -858,7 +1356,7 @@ zrefresh(void) nbuf[iln] && obuf[iln] && ZR_strncmp(nbuf[iln], obuf[iln], 16)) { if (tccan(TCDELLINE) && obuf[iln + 1] && - obuf[iln + 1][0] && nbuf[iln] && + obuf[iln + 1][0].chr && nbuf[iln] && !ZR_strncmp(nbuf[iln], obuf[iln + 1], 16)) { moveto(iln, 0); tcout(TCDELLINE); @@ -893,17 +1391,23 @@ zrefresh(void) vcs = winw - 1; /* reset character attributes to that set by the main prompt */ txtchange = pmpt_attr; - if (txtchangeisset(TXTNOBOLDFACE) && (rpmpt_attr & TXTBOLDFACE)) + if (txtchangeisset(txtchange, TXTNOBOLDFACE) && + (rpmpt_attr & TXTBOLDFACE)) tsetcap(TCALLATTRSOFF, 0); - if (txtchangeisset(TXTNOSTANDOUT) && (rpmpt_attr & TXTSTANDOUT)) + if (txtchangeisset(txtchange, TXTNOSTANDOUT) && + (rpmpt_attr & TXTSTANDOUT)) tsetcap(TCSTANDOUTEND, 0); - if (txtchangeisset(TXTNOUNDERLINE) && (rpmpt_attr & TXTUNDERLINE)) + if (txtchangeisset(txtchange, TXTNOUNDERLINE) && + (rpmpt_attr & TXTUNDERLINE)) tsetcap(TCUNDERLINEEND, 0); - if (txtchangeisset(TXTBOLDFACE) && (rpmpt_attr & TXTNOBOLDFACE)) + if (txtchangeisset(txtchange, TXTBOLDFACE) && + (rpmpt_attr & TXTNOBOLDFACE)) tsetcap(TCBOLDFACEBEG, 0); - if (txtchangeisset(TXTSTANDOUT) && (rpmpt_attr & TXTNOSTANDOUT)) + if (txtchangeisset(txtchange, TXTSTANDOUT) && + (rpmpt_attr & TXTNOSTANDOUT)) tsetcap(TCSTANDOUTBEG, 0); - if (txtchangeisset(TXTUNDERLINE) && (rpmpt_attr & TXTNOUNDERLINE)) + if (txtchangeisset(txtchange, TXTUNDERLINE) && + (rpmpt_attr & TXTNOUNDERLINE)) tsetcap(TCUNDERLINEBEG, 0); } } @@ -920,7 +1424,7 @@ individually */ /* reset character attributes */ if (clearf && postedit) { if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) - settextattributes(); + settextattributes(txtchange); } clearf = 0; oput_rpmpt = put_rpmpt; @@ -942,7 +1446,7 @@ singlelineout: fflush(shout); /* make sure everything is written out */ if (tmpalloced) - zfree(tmpline, tmpll); + zfree(tmpline, tmpll * sizeof(*tmpline)); /* if we have a new list showing, note it; if part of the list has been overwritten, redisplay it. We have to metafy line back before calling @@ -972,11 +1476,11 @@ singlelineout: #define tc_leftcurs(X) (void) tcmultout(TCLEFT, TCMULTLEFT, (X)) static int -wpfxlen(REFRESH_STRING s, REFRESH_STRING t) +wpfxlen(const REFRESH_ELEMENT *s, const REFRESH_ELEMENT *t) { int i = 0; - while (*s && *s == *t) + while (s->chr && ZR_equal(*s, *t)) s++, t++, i++; return i; } @@ -1004,7 +1508,7 @@ refreshline(int ln) ollen = ZR_strlen(ol); } else { - static REFRESH_CHAR nullchr = ZWC('\0'); + static REFRESH_ELEMENT nullchr = { ZWC('\0'), 0 }; ol = &nullchr; ollen = 0; } @@ -1027,9 +1531,9 @@ refreshline(int ln) p1 = zhalloc((winw + 2) * sizeof(*p1)); if (nllen) ZR_memcpy(p1, nl, nllen); - ZR_memset(p1 + nllen, ZWC(' '), winw - nllen); - p1[winw] = ZWC('\0'); - p1[winw + 1] = (nllen < winw) ? ZWC('\0') : nl[winw + 1]; + ZR_memset(p1 + nllen, zr_sp, winw - nllen); + p1[winw] = zr_zr; + p1[winw + 1] = (nllen < winw) ? zr_zr : nl[winw + 1]; if (ln && nbuf[ln]) ZR_memcpy(nl, p1, winw + 2); /* next time obuf will be up-to-date */ else @@ -1038,8 +1542,8 @@ refreshline(int ln) } else if (ollen > nllen) { /* make new line at least as long as old */ p1 = zhalloc((ollen + 1) * sizeof(*p1)); ZR_memcpy(p1, nl, nllen); - ZR_memset(p1 + nllen, ZWC(' '), ollen - nllen); - p1[ollen] = ZWC('\0'); + ZR_memset(p1 + nllen, zr_sp, ollen - nllen); + p1[ollen] = zr_zr; nl = p1; nllen = ollen; } @@ -1054,10 +1558,13 @@ refreshline(int ln) else { col_cleareol = -1; if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) { - for (i = nllen; i && nl[i - 1] == ZWC(' '); i--); - for (j = ollen; j && ol[j - 1] == ZWC(' '); j--); + /* HERE: watch for change of attributes */ + for (i = nllen; i && ZR_equal(nl[i - 1], zr_sp); i--) + ; + for (j = ollen; j && ZR_equal(ol[j - 1], zr_sp); j--) + ; if ((j > i + tclen[TCCLEAREOL]) /* new buf has enough spaces */ - || (nllen == winw && nl[winw - 1] == ZWC(' '))) + || (nllen == winw && ZR_equal(nl[winw - 1], zr_sp))) col_cleareol = i; } } @@ -1065,21 +1572,21 @@ refreshline(int ln) /* 2b: first a new trick for automargin niceness - good for cut and paste */ if (hasam && vcs == winw) { - if (nbuf[vln] && nbuf[vln][vcs + 1] == ZWC('\n')) { + if (nbuf[vln] && nbuf[vln][vcs + 1].chr == ZWC('\n')) { vln++, vcs = 1; - if (nbuf[vln] && *nbuf[vln]) { - zputc(*nbuf[vln]); + if (nbuf[vln] && nbuf[vln]->chr) { + zputc(nbuf[vln]); } else - zputc(ZWC(' ')); /* I don't think this should happen */ + zputc(&zr_sp); /* I don't think this should happen */ if (ln == vln) { /* better safe than sorry */ nl++; - if (*ol) + if (ol->chr) ol++; ccs = 1; } /* else hmmm... I wonder what happened */ } else { vln++, vcs = 0; - zputc(ZWC('\n')); + zputc(&zr_nl); } } ins_last = 0; @@ -1100,9 +1607,9 @@ refreshline(int ln) * Realign to a real character after any jiggery pokery at * the start of the line. */ - while (*nl == WEOF) { + while (nl->chr == WEOF) { nl++, ccs++, vcs++; - if (*ol) + if (ol->chr) ol++; } #endif @@ -1111,18 +1618,19 @@ refreshline(int ln) for (;;) { #ifdef MULTIBYTE_SUPPORT - if ((!*nl || *nl != WEOF) && (!*ol || *ol != WEOF)) { + if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) { #endif - if (*nl && *ol && nl[1] == ol[1]) { + if (nl->chr && ol->chr && ZR_equal(nl[1], ol[1])) { /* skip only if second chars match */ #ifdef MULTIBYTE_SUPPORT int ccs_was = ccs; #endif /* skip past all matching characters */ - for (; *nl && (*nl == *ol); nl++, ol++, ccs++) ; + for (; nl->chr && ZR_equal(*nl, *ol); nl++, ol++, ccs++) + ; #ifdef MULTIBYTE_SUPPORT /* Make sure ol and nl are pointing to real characters */ - while ((*nl == WEOF || *ol == WEOF) && ccs > ccs_was) { + while ((nl->chr == WEOF || ol->chr == WEOF) && ccs > ccs_was) { nl--; ol--; ccs--; @@ -1130,12 +1638,12 @@ refreshline(int ln) #endif } - if (!*nl) { + if (!nl->chr) { if (ccs == winw && hasam && char_ins > 0 && ins_last && vcs != winw) { nl--; /* we can assume we can go back here */ moveto(ln, winw - 1); - zputc(*nl); + zputc(nl); vcs++; return; /* write last character in line */ } @@ -1156,21 +1664,21 @@ refreshline(int ln) } /* we've written out the new but yet to clear rubbish due to inserts */ - if (!*nl) { + if (!nl->chr) { i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins; if (tccan(TCDEL) && (tcdelcost(i) <= i + 1)) tc_delchars(i); else { vcs += i; while (i-- > 0) - zputc(ZWC(' ')); + zputc(&zr_sp); } return; } /* if we've reached the end of the old buffer, then there are few tricks we can do, so we just dump out what we must and clear if we can */ - if (!*ol) { + if (!ol->chr) { i = (col_cleareol >= 0) ? col_cleareol : nllen; i -= vcs; if (i < 0) { @@ -1192,26 +1700,35 @@ refreshline(int ln) /* inserting & deleting chars: we can if there's no right-prompt */ if ((ln || !put_rpmpt || !oput_rpmpt) #ifdef MULTIBYTE_SUPPORT - && *ol != WEOF && *nl != WEOF + && ol->chr != WEOF && nl->chr != WEOF #endif - && nl[1] && ol[1] && nl[1] != ol[1]) { + && nl[1].chr && ol[1].chr && !ZR_equal(nl[1], ol[1])) { /* deleting characters - see if we can find a match series that makes it cheaper to delete intermediate characters eg. oldline: hifoobar \ hopefully cheaper here to delete two newline: foobar / characters, then we have six matches */ if (tccan(TCDEL)) { - for (i = 1; *(ol + i); i++) + for (i = 1; ol[i].chr; i++) if (tcdelcost(i) < wpfxlen(ol + i, nl)) { tc_delchars(i); ol += i; char_ins -= i; #ifdef MULTIBYTE_SUPPORT - while (*ol == WEOF) { + while (ol->chr == WEOF) { ol++; char_ins--; } #endif + /* + * If the sequence we're deleting ended + * by turning off an attribute, make sure + * it stays turned off. I don't think we + * should need this. + */ + if (ol[-1].atr & TXT_ATTR_OFF_MASK) + settextattributes(ol[-1].atr & + TXT_ATTR_OFF_MASK); i = 0; break; } @@ -1223,13 +1740,13 @@ refreshline(int ln) undesired scrolling occurs due to `illegal' characters on screen */ if (tccan(TCINS) && (vln != lines - 1)) { /* not on last line */ - for (i = 1; *(nl + i); i++) + for (i = 1; nl[i].chr; i++) if (tcinscost(i) < wpfxlen(nl + i, ol)) { tc_inschars(i); zwrite(nl, i); nl += i; #ifdef MULTIBYTE_SUPPORT - while (*nl == WEOF) { + while (nl->chr == WEOF) { nl++; i++; } @@ -1237,13 +1754,13 @@ refreshline(int ln) char_ins += i; ccs = (vcs += i); /* if we've pushed off the right, truncate oldline */ - for (i = 0; *(ol + i) && i < winw - ccs; i++); + for (i = 0; ol[i].chr && i < winw - ccs; i++); #ifdef MULTIBYTE_SUPPORT - while (ol[i] == WEOF) + while (ol[i].chr == WEOF) i++; #endif if (i >= winw - ccs) { - *(ol + i) = ZWC('\0'); + ol[i] = zr_zr; ins_last = 1; } i = 0; @@ -1263,11 +1780,19 @@ refreshline(int ln) * in case we were tidying up a funny-width character when we * reached the end of the new line... */ - if (!*nl) + if (!nl->chr) break; do { #endif - zputc(*nl); + /* + * If an attribute was on here but isn't any more, + * output the sequence to turn it off. + */ + int now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK; + if (now_off) + settextattributes(now_off << TXT_ATTR_OFF_ON_SHIFT); + + zputc(nl); nl++, ol++; ccs++, vcs++; #ifdef MULTIBYTE_SUPPORT @@ -1275,7 +1800,8 @@ refreshline(int ln) * Make sure we always overwrite the complete width of * a character that was there before. */ - } while ((*ol == WEOF && *nl) || (*nl == WEOF && *ol)); + } while ((ol->chr == WEOF && nl->chr) || + (nl->chr == WEOF && ol->chr)); #endif } } @@ -1287,22 +1813,22 @@ refreshline(int ln) void moveto(int ln, int cl) { - ZLE_INT_T c; + const REFRESH_ELEMENT *rep; if (vcs == winw) { vln++, vcs = 0; if (!hasam) { - zputc(ZWC('\r')); - zputc(ZWC('\n')); + zputc(&zr_cr); + zputc(&zr_nl); } else { - if ((vln < nlnct) && nbuf[vln] && *nbuf[vln]) - c = *nbuf[vln]; + if ((vln < nlnct) && nbuf[vln] && nbuf[vln]->chr) + rep = nbuf[vln]; else - c = ZWC(' '); - zputc(c); - zputc(ZWC('\r')); - if ((vln < olnct) && obuf[vln] && *obuf[vln]) - *obuf[vln] = c; + rep = &zr_sp; + zputc(rep); + zputc(&zr_cr); + if ((vln < olnct) && obuf[vln] && obuf[vln]->chr) + *obuf[vln] = *rep; } } @@ -1330,9 +1856,9 @@ moveto(int ln, int cl) continue; } } - zputc(ZWC('\r')), vcs = 0; /* safety precaution */ + zputc(&zr_cr), vcs = 0; /* safety precaution */ while (ln > vln) { - zputc(ZWC('\n')); + zputc(&zr_nl); vln++; } } @@ -1420,7 +1946,7 @@ tc_rightcurs(int ct) tcout(TCRIGHT); else { if (i != 0) - zputc('\r'); + zputc(&zr_cr); tc_upcurs(lprompth - 1); zputs(lpromptbuf, shout); if (lpromptwof == winw) @@ -1431,13 +1957,13 @@ tc_rightcurs(int ct) } if (nbuf[vln]) { - for (j = 0, t = nbuf[vln]; *t && (j < i); j++, t++); + for (j = 0, t = nbuf[vln]; t->chr && (j < i); j++, t++); if (j == i) - for ( ; *t && ct; ct--, t++) - zputc(*t); + for ( ; t->chr && ct; ct--, t++) + zputc(t); } while (ct--) - zputc(ZWC(' ')); /* not my fault your terminal can't go right */ + zputc(&zr_sp); /* not my fault your terminal can't go right */ } /**/ @@ -1448,8 +1974,8 @@ tc_downcurs(int ct) if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) { while (ct--) - zputc(ZWC('\n')); - zputc(ZWC('\r')), ret = -1; + zputc(&zr_nl); + zputc(&zr_cr), ret = -1; } return ret; } @@ -1488,13 +2014,21 @@ mod_export int redisplay(UNUSED(char **args)) { moveto(0, 0); - zputc(ZWC('\r')); /* extra care */ + zputc(&zr_cr); /* extra care */ tc_upcurs(lprompth - 1); resetneeded = 1; clearflag = 0; return 0; } +/* + * Show as much of the line buffer as we can in single line mode. + * TBD: all termcap effects are turned off in this mode, so + * there's no point in using character attributes. We should + * decide what we're going to do and either remove the handling + * from here or enable it in tsetcap(). + */ + /**/ static void singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) @@ -1530,38 +2064,88 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } /* prompt is not directly copied into the video buffer */ - ZR_memset(vbuf, ZWC(' '), lpromptw); + ZR_memset(vbuf, zr_sp, lpromptw); vp = vbuf + lpromptw; - *vp = ZWC('\0'); + *vp = zr_zr; for (t0 = 0; t0 < tmpll; t0++) { + int base_atr_on = 0, base_atr_off = 0, ireg; + struct region_highlight *rhp; + /* + * Calculate attribute based on region. + * HERE: we may need to be smarter about turning + * attributes off if bailing out before the end of the + * region. + */ + for (ireg = 0, rhp = region_highlights; + ireg < n_region_highlights; + ireg++, rhp++) { + int offset; + if (rhp->flags & ZRH_PREDISPLAY) + offset = 0; /* include predisplay in start end */ + else + offset = predisplaylen; /* increment over it */ + if (rhp->start + offset <= t0 && + t0 < rhp->end + offset) { + base_atr_on |= rhp->atr; + if (t0 == rhp->end + offset - 1 || + t0 == tmpll - 1) + base_atr_off |= rhp->atr << TXT_ATTR_OFF_ON_SHIFT; + } + } + if (tmpline[t0] == ZWC('\t')) { - for (*vp++ = ZWC(' '); (vp - vbuf) & 7; ) - *vp++ = ZWC(' '); + REFRESH_ELEMENT sp = zr_sp; + sp.atr = base_atr_on; + for (*vp++ = zr_sp; (vp - vbuf) & 7; ) + *vp++ = zr_sp; + vp[-1].atr |= base_atr_off; } else if (tmpline[t0] == ZWC('\n')) { - *vp++ = ZWC('\\'); - *vp++ = ZWC('n'); + /* HERE highlight */ + vp->chr = ZWC('\\'); + vp->atr = special_atr_on | base_atr_on; + vp++; + vp->chr = ZWC('n'); + vp->atr = special_atr_on | special_atr_off | + base_atr_on | base_atr_off; + vp++; #ifdef MULTIBYTE_SUPPORT } else if (iswprint(tmpline[t0])) { int width; - *vp++ = tmpline[t0]; + vp->chr = tmpline[t0]; + vp->atr = base_atr_on; + vp++; width = wcwidth(tmpline[t0]); - while (--width > 0) - *vp++ = WEOF; + while (--width > 0) { + vp->chr = WEOF; + vp->atr = base_atr_on; + vp++; + } + vp[-1].atr |= base_atr_off; #endif } else if (ZC_icntrl(tmpline[t0])) { + /* HERE: highlight */ ZLE_INT_T t = tmpline[++t0]; - *vp++ = ZWC('^'); - *vp++ = (((unsigned int)t & ~0x80u) > 31) ? ZWC('?') : (t | ZWC('@')); - } else - *vp++ = tmpline[t0]; + vp->chr = ZWC('^'); + vp->atr = special_atr_on | base_atr_on; + vp++; + vp->chr = (((unsigned int)t & ~0x80u) > 31) ? + ZWC('?') : (t | ZWC('@')); + vp->atr = special_atr_on | special_atr_off | base_atr_on | + base_atr_off; + vp++; + } else { + vp->chr = tmpline[t0]; + vp->atr = base_atr_on | base_atr_off; + vp++; + } if (t0 == tmpcs) nvcs = vp - vbuf - 1; } if (t0 == tmpcs) nvcs = vp - vbuf; - *vp = ZWC('\0'); + *vp = zr_zr; /* determine which part of the new line buffer we want for the display */ if (winpos == -1) @@ -1570,11 +2154,14 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) if ((winpos = nvcs - ((winw - hasam) / 2)) < 0) winpos = 0; } - if (winpos) - vbuf[winpos] = ZWC('<'); /* line continues to the left */ + if (winpos) { + vbuf[winpos].chr = ZWC('<'); /* line continues to the left */ + vbuf[winpos].atr = 0; + } if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) { - vbuf[winpos + winw - hasam - 1] = ZWC('>'); /* line continues to right */ - vbuf[winpos + winw - hasam] = ZWC('\0'); + vbuf[winpos + winw - hasam - 1].chr = ZWC('>'); /* line continues to right */ + vbuf[winpos + winw - hasam - 1].atr = 0; + vbuf[winpos + winw - hasam] = zr_zr; } ZR_strcpy(nbuf[0], vbuf + winpos); zfree(vbuf, vsiz * sizeof(*vbuf)); @@ -1644,28 +2231,30 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) * nastiness may be around. */ if (vp - *nbuf >= owinprompt) - for (; *vp && *vp == *refreshop; t0++, vp++, refreshop++) ; + for (; vp->chr && ZR_equal(*vp, *refreshop); + t0++, vp++, refreshop++) + ; - if (!*vp && !*refreshop) + if (!vp->chr && !refreshop->chr) break; singmoveto(t0); /* move to where we do all output from */ - if (!*refreshop) { + if (!refreshop->chr) { if ((t0 = ZR_strlen(vp))) zwrite(vp, t0); vcs += t0; break; } - if (!*vp) { + if (!vp->chr) { if (tccan(TCCLEAREOL)) tcout(TCCLEAREOL); else - for (; *refreshop++; vcs++) - zputc(ZWC(' ')); + for (; refreshop++->chr; vcs++) + zputc(&zr_sp); break; } - zputc(*vp); + zputc(vp); vcs++, t0++; vp++, refreshop++; } @@ -1688,7 +2277,7 @@ singmoveto(int pos) do this now because it's easier (to code) */ if ((!tccan(TCMULTLEFT) || pos == 0) && (pos <= vcs / 2)) { - zputc(ZWC('\r')); + zputc(&zr_cr); vcs = 0; } @@ -1699,3 +2288,16 @@ singmoveto(int pos) vcs = pos; } + +/* Provided for unloading the module in a modular fashion */ + +/**/ +void +zle_refresh_finish(void) +{ + freevideo(); + + if (region_highlights) + zfree(region_highlights, + sizeof(struct region_highlight) * n_region_highlights); +} diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index d702be845..1027f2083 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -384,6 +384,7 @@ spaceinline(int ct) if (mark > zlecs) mark += ct; } + region_active = 0; } /**/ @@ -408,6 +409,7 @@ shiftchars(int to, int cnt) } zleline[zlell = to] = ZWC('\0'); } + region_active = 0; } /**/ @@ -724,8 +726,12 @@ getzlequery(void) else c = ZC_tolower(c); /* echo response and return */ - if (c != ZWC('\n')) - zwcputc(c); + if (c != ZWC('\n')) { + REFRESH_ELEMENT re; + re.chr = c; + re.atr = 0; + zwcputc(&re, NULL); + } return c == ZWC('y'); } @@ -903,6 +909,7 @@ int handlefeep(UNUSED(char **args)) { zbeep(); + region_active = 0; return 0; } -- cgit 1.4.1