mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-12-05 01:10:17 +00:00
54074, 54082: add an opaque key for use in zle_highlight/region_highlight to allow mixing of colours
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
2025-11-17 Oliver Kiddle <opk@zsh.org>
|
||||
|
||||
* 54074, 54082: Doc/Zsh/zle.yo, Src/prompt.c, Src/Zle/termquery.c,
|
||||
Src/Zle/zle_main.c, Src/Zle/zle_refresh.c, Src/Modules/nearcolor.c,
|
||||
Test/X04zlehighlight.ztst: add an opaque key for use in
|
||||
zle_highlight/region_highlight to allow mixing of colours
|
||||
|
||||
* 54083: Src/Zle/zle_keymap.c: fix for cursor shape in viopp mode
|
||||
|
||||
* 54075: Doc/Zsh/prompt.yo, Src/Modules/watch.c, Src/Zle/complist.c,
|
||||
|
||||
@@ -2905,7 +2905,14 @@ With the other fields 30 applies by default for tt(special), 20 for tt(region)
|
||||
and tt(isearch) and 15 for tt(paste). Highlighting defined in
|
||||
tt(region_highlight) defaults to layer 10 and would take precedence over
|
||||
highlighting for any fields of tt(zle_highlight) that are assigned to the same
|
||||
layer.)
|
||||
layer.
|
||||
)
|
||||
item(tt(opacity=)var(fg)tt(%)[tt(/)var(bg)tt(%)])(
|
||||
Instead of replacing colors in higher layers, the colors can be mixed. The
|
||||
opacity is specified as a percentage where tt(0%) is fully transparent and
|
||||
tt(100%) represents the default behavior of replacing the underlying colour.
|
||||
If a single value is specified, it applies to both foreground and background.
|
||||
)
|
||||
enditem()
|
||||
|
||||
In addition, the simple highlighting types can be prefixed with tt("no") to
|
||||
|
||||
@@ -146,7 +146,7 @@ mapRGBto256(int red, int green, int blue)
|
||||
static int
|
||||
getnearestcolor(UNUSED(Hookdef dummy), Color_rgb col)
|
||||
{
|
||||
/* we add 1 to the colours so that colour 0 (black) is
|
||||
/* we add 1 to the colours so that colour 0 (default) is
|
||||
* distinguished from runhookdef() indicating that no
|
||||
* hook function is registered */
|
||||
if (tccolours == 256)
|
||||
|
||||
@@ -439,16 +439,26 @@ handle_color(int bg, int red, int green, int blue)
|
||||
{
|
||||
char *colour;
|
||||
|
||||
if (bg == 1) { /* background color */
|
||||
/* scale by Rec.709 coefficients for lightness */
|
||||
setsparam(MODEVAR, ztrdup(
|
||||
0.2126f * red + 0.7152f * green + 0.0722f * blue <= 127 ?
|
||||
"dark" : "light"));
|
||||
switch (bg) {
|
||||
case 0: /* foreground color */
|
||||
memo_term_color &= ~TXT_ATTR_FG_MASK;
|
||||
memo_term_color |= TXT_ATTR_FG_24BIT | (zattr) ((((red << 8)
|
||||
+ green) << 8) + blue) << TXT_ATTR_FG_COL_SHIFT;
|
||||
break;
|
||||
case 1: /* background color */
|
||||
memo_term_color &= ~TXT_ATTR_BG_MASK;
|
||||
memo_term_color |= TXT_ATTR_BG_24BIT | (zattr) ((((red << 8)
|
||||
+ green) << 8) + blue) << TXT_ATTR_BG_COL_SHIFT;
|
||||
/* scale by Rec.709 coefficients for lightness */
|
||||
setsparam(MODEVAR, ztrdup(
|
||||
0.2126f * red + 0.7152f * green + 0.0722f * blue <= 127 ?
|
||||
"dark" : "light"));
|
||||
break;
|
||||
case 2: /* cursor color */
|
||||
memo_cursor = (red << 24) | (green << 16) | (blue << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
if (bg == 2) /* cursor color */
|
||||
memo_cursor = (red << 24) | (green << 16) | (blue << 8);
|
||||
|
||||
colour = zalloc(8);
|
||||
sprintf(colour, "#%02x%02x%02x", red, green, blue);
|
||||
setsparam(COLORVAR[bg], colour);
|
||||
|
||||
@@ -1277,7 +1277,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
|
||||
raw_rp = rp;
|
||||
rpromptbuf = promptexpand(rp ? *rp : NULL, 1, markers[2], NULL, NULL);
|
||||
rpmpt_attr = txtcurrentattrs;
|
||||
prompt_attr = mixattrs(pmpt_attr, pmpt_attr, rpmpt_attr);
|
||||
prompt_attr = mixattrs(pmpt_attr, pmpt_attr & TXT_ATTR_ALL, rpmpt_attr);
|
||||
free_prepostdisplay();
|
||||
|
||||
zlereadflags = flags;
|
||||
@@ -2032,7 +2032,7 @@ reexpandprompt(void)
|
||||
|
||||
new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, markers[2], NULL, NULL);
|
||||
rpmpt_attr = txtcurrentattrs;
|
||||
prompt_attr = mixattrs(pmpt_attr, pmpt_attr, rpmpt_attr);
|
||||
prompt_attr = mixattrs(pmpt_attr, pmpt_attr & TXT_ATTR_ALL, rpmpt_attr);
|
||||
free(rpromptbuf);
|
||||
rpromptbuf = new_rprompt;
|
||||
} while (looping != reexpanding);
|
||||
|
||||
@@ -208,7 +208,7 @@ int predisplaylen, postdisplaylen;
|
||||
* and for ellipsis continuation markers.
|
||||
*/
|
||||
|
||||
static zattr default_attr, special_attr, special_mask, ellipsis_attr;
|
||||
static zattr default_attr, default_mask, special_attr, special_mask, ellipsis_attr;
|
||||
|
||||
/*
|
||||
* Layer applied to highlighting for special characters
|
||||
@@ -330,7 +330,7 @@ zle_set_highlight(void)
|
||||
int ellipsis_attr_set = 0;
|
||||
struct region_highlight *rhp;
|
||||
|
||||
special_attr = default_attr = 0;
|
||||
special_attr = default_attr = special_mask = default_mask = 0;
|
||||
if (!region_highlights) {
|
||||
region_highlights = (struct region_highlight *)
|
||||
zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight));
|
||||
@@ -354,12 +354,12 @@ zle_set_highlight(void)
|
||||
for (; *atrs; atrs++) {
|
||||
if (!strcmp(*atrs, "none")) {
|
||||
/* reset attributes for consistency... usually unnecessary */
|
||||
special_attr = default_attr = 0;
|
||||
special_attr = default_attr = special_mask = default_mask = 0;
|
||||
special_attr_set = 1;
|
||||
paste_attr_set = region_attr_set =
|
||||
isearch_attr_set = suffix_attr_set = 1;
|
||||
} else if (strpfx("default:", *atrs)) {
|
||||
match_highlight(*atrs + 8, &default_attr, NULL, NULL);
|
||||
match_highlight(*atrs + 8, &default_attr, &default_mask, NULL);
|
||||
} else if (strpfx("special:", *atrs)) {
|
||||
match_highlight(*atrs + 8, &special_attr, &special_mask,
|
||||
&special_layer);
|
||||
@@ -1206,7 +1206,7 @@ zrefresh(void)
|
||||
rpms.s = nbuf[rpms.ln = 0] + lpromptw;
|
||||
rpms.sen = *nbuf + winw;
|
||||
for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
|
||||
zattr base_attr = mixattrs(default_attr, default_attr, prompt_attr);
|
||||
zattr base_attr = mixattrs(default_attr, default_mask, prompt_attr);
|
||||
zattr all_attr = 0;
|
||||
struct region_highlight *rhp;
|
||||
int layer, nextlayer = 0;
|
||||
@@ -2452,7 +2452,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
|
||||
|
||||
for (t0 = 0; t0 < tmpll; t0++) {
|
||||
unsigned ireg;
|
||||
zattr base_attr = 0;
|
||||
zattr base_attr = mixattrs(default_attr, default_attr, prompt_attr);
|
||||
zattr all_attr;
|
||||
struct region_highlight *rhp;
|
||||
/*
|
||||
|
||||
159
Src/prompt.c
159
Src/prompt.c
@@ -45,6 +45,11 @@ mod_export zattr txtpendingattrs;
|
||||
/**/
|
||||
mod_export zattr txtunknownattrs;
|
||||
|
||||
/* detected default attributes for the terminal if any */
|
||||
|
||||
/**/
|
||||
mod_export zattr memo_term_color;
|
||||
|
||||
/* the command stack for use with %_ in prompts */
|
||||
|
||||
/**/
|
||||
@@ -1759,6 +1764,32 @@ tunsetattrs(zattr newattrs)
|
||||
txtpendingattrs &= ~TXT_ATTR_BG_MASK;
|
||||
}
|
||||
|
||||
void
|
||||
map256toRGB(zattr *atr, int shift, zattr set24)
|
||||
{
|
||||
unsigned colour, red, green, blue;
|
||||
|
||||
if (*atr & set24)
|
||||
return;
|
||||
|
||||
if ((colour = ((*atr >> shift) & 0xff)) < 16)
|
||||
return;
|
||||
|
||||
if (colour >= 16 && colour < 232) {
|
||||
colour -= 16;
|
||||
blue = !!colour * 0x37 + 40 * (colour % 6);
|
||||
colour /= 6;
|
||||
green = !!colour * 0x37 + 40 * (colour % 6);
|
||||
colour /= 6;
|
||||
red = !!colour * 0x37 + 40 * colour;
|
||||
} else {
|
||||
red = green = blue = 8 + 10 * (colour - 232);
|
||||
}
|
||||
|
||||
*atr &= ~((zattr) 0xffffff << shift);
|
||||
*atr |= set24 | (zattr) ((((red << 8) + green) << 8) + blue) << shift;
|
||||
}
|
||||
|
||||
/* Merge two attribute sets.
|
||||
* secondary is the background base attributes
|
||||
* primary is attributes to be overlaid, taking precedence.
|
||||
@@ -1770,16 +1801,76 @@ tunsetattrs(zattr newattrs)
|
||||
mod_export zattr
|
||||
mixattrs(zattr primary, zattr mask, zattr secondary)
|
||||
{
|
||||
zattr select = mask & TXT_ATTR_ALL;
|
||||
zattr mix = 0; /* attributes resulting from colour mixing */
|
||||
zattr keep; /* attributes from secondary */
|
||||
zattr replace = mask & TXT_ATTR_ALL; /* attributes from primary */
|
||||
zattr toset = TXT_ATTR_FG_MASK;
|
||||
zattr isset = TXTFGCOLOUR;
|
||||
zattr istrue = TXT_ATTR_FG_24BIT;
|
||||
unsigned int shift = TXT_ATTR_FG_COL_SHIFT;
|
||||
int opacity, i;
|
||||
|
||||
if (mask & TXTFGCOLOUR)
|
||||
select |= TXT_ATTR_FG_MASK;
|
||||
if (mask & TXTBGCOLOUR)
|
||||
select |= TXT_ATTR_BG_MASK;
|
||||
if (mask & TXT_ATTR_FONT_WEIGHT)
|
||||
select |= TXT_ATTR_FONT_WEIGHT;
|
||||
replace |= TXT_ATTR_FONT_WEIGHT;
|
||||
if (mask & TXTFGCOLOUR)
|
||||
replace |= TXT_ATTR_FG_MASK;
|
||||
if (mask & TXTBGCOLOUR)
|
||||
replace |= TXT_ATTR_BG_MASK;
|
||||
keep = ~replace;
|
||||
|
||||
return (primary & select) | (secondary & ~select);
|
||||
do {
|
||||
if (mask & isset && (opacity = (mask >> shift) & 127)) {
|
||||
zattr argb, brgb;
|
||||
/* we may know the default colours from the startup query */
|
||||
zattr aatt = (primary & isset) ? primary : memo_term_color;
|
||||
zattr batt = (secondary & isset) ? secondary : memo_term_color;
|
||||
|
||||
keep &= ~toset;
|
||||
replace &= ~toset;
|
||||
|
||||
if (tccolours == 256) {
|
||||
map256toRGB(&aatt, shift, istrue);
|
||||
map256toRGB(&batt, shift, istrue);
|
||||
}
|
||||
|
||||
/* can only mix if we now have truecolor */
|
||||
if (aatt & batt & istrue) {
|
||||
mix |= istrue | isset;
|
||||
for (i = 0; i < 24; i += 8) {
|
||||
argb = (aatt >> (shift + i)) & 0xff;
|
||||
brgb = (batt >> (shift + i)) & 0xff;
|
||||
mix |= ((argb * (100 - opacity) + brgb * opacity) / 100)
|
||||
<< (shift + i);
|
||||
}
|
||||
if (!truecolor_terminal() && (!empty(GETCOLORATTR->funcs) ||
|
||||
!load_module("zsh/nearcolor", NULL, 1))) {
|
||||
struct color_rgb color = {
|
||||
(mix >> (shift + 16)) & 0xff,
|
||||
(mix >> (shift + 8)) & 0xff,
|
||||
(mix >> shift) & 0xff
|
||||
};
|
||||
int color_idx = runhookdef(GETCOLORATTR, &color) - 1;
|
||||
if (color_idx >= 0) {
|
||||
mix &= ~toset;
|
||||
mix |= isset | ((zattr) color_idx << shift);
|
||||
}
|
||||
}
|
||||
} else if (opacity <= 50)
|
||||
replace |= toset;
|
||||
else
|
||||
keep |= toset;
|
||||
}
|
||||
|
||||
if (isset == TXTBGCOLOUR)
|
||||
break;
|
||||
|
||||
shift = TXT_ATTR_BG_COL_SHIFT;
|
||||
toset = TXT_ATTR_BG_COL_MASK;
|
||||
isset = TXTBGCOLOUR;
|
||||
istrue = TXT_ATTR_BG_24BIT;
|
||||
} while (1);
|
||||
|
||||
return (primary & replace) | (secondary & keep) | mix;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -1839,6 +1930,7 @@ match_named_colour(const char **teststrp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
truecolor_terminal()
|
||||
{
|
||||
@@ -1964,9 +2056,11 @@ match_highlight(const char *teststr, zattr *on_var, zattr *setmask, int *layer)
|
||||
break;
|
||||
found = 1;
|
||||
/* skip out of range colours but keep scanning attributes */
|
||||
if (atr != TXT_ERROR)
|
||||
if (atr != TXT_ERROR) {
|
||||
*on_var &= is_fg ? (zattr) ~TXT_ATTR_FG_MASK : (zattr) ~TXT_ATTR_BG_MASK;
|
||||
*on_var |= atr;
|
||||
mask |= is_fg ? TXTFGCOLOUR : TXTBGCOLOUR;
|
||||
mask |= is_fg ? TXTFGCOLOUR : TXTBGCOLOUR;
|
||||
}
|
||||
} else if (layer && strpfx("layer=", teststr)) {
|
||||
teststr += 6;
|
||||
*layer = (int) zstrtol(teststr, (char **) &teststr, 10);
|
||||
@@ -1975,6 +2069,29 @@ match_highlight(const char *teststr, zattr *on_var, zattr *setmask, int *layer)
|
||||
else if (*teststr && *teststr != ' ')
|
||||
break;
|
||||
found = 1;
|
||||
} else if (strpfx("opacity=", teststr)) {
|
||||
teststr += 8;
|
||||
zulong opacity = zstrtol(teststr, (char **) &teststr, 10);
|
||||
if (opacity > 100)
|
||||
break;
|
||||
if (*teststr == '%')
|
||||
teststr++;
|
||||
/* invert sense so 0 is fully opaque */
|
||||
mask |= (100 - opacity) << TXT_ATTR_FG_COL_SHIFT;
|
||||
if (*teststr == '/') {
|
||||
teststr++;
|
||||
opacity = zstrtol(teststr, (char **) &teststr, 10);
|
||||
if (opacity > 100)
|
||||
break;
|
||||
if (*teststr == '%')
|
||||
teststr++;
|
||||
}
|
||||
mask |= (100 - opacity) << TXT_ATTR_BG_COL_SHIFT;
|
||||
if (*teststr == ',')
|
||||
teststr++;
|
||||
else if (*teststr && *teststr != ' ')
|
||||
break;
|
||||
found = 1;
|
||||
} else {
|
||||
int turn_off = 0;
|
||||
for (hl = highlights; !found && hl->name; hl++) {
|
||||
@@ -2156,6 +2273,30 @@ output_highlight(zattr atr, zattr mask, char *buf)
|
||||
strcpy(ptr, "none");
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (mask & (TXT_ATTR_FG_COL_MASK | TXT_ATTR_BG_COL_MASK)) {
|
||||
unsigned fg_op, bg_op;
|
||||
char tmp[13];
|
||||
size_t len;
|
||||
|
||||
if (atrlen) {
|
||||
atrlen++;
|
||||
if (buf) {
|
||||
strcpy(ptr, ",");
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
atrlen += 8;
|
||||
fg_op = (mask >> TXT_ATTR_FG_COL_SHIFT) & 127;
|
||||
bg_op = (mask >> TXT_ATTR_BG_COL_SHIFT) & 127;
|
||||
len = sprintf(buf ? ptr : tmp, "opacity=%u%%", 100 - fg_op);
|
||||
atrlen += len;
|
||||
if (buf)
|
||||
ptr += len;
|
||||
if (fg_op != bg_op)
|
||||
atrlen += sprintf(buf ? ptr : tmp, "/%u%%", 100 - bg_op);
|
||||
}
|
||||
|
||||
return atrlen;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,46 @@
|
||||
0:basic region_highlight with 8 colors
|
||||
>0mCDE|32|true
|
||||
|
||||
zpty_start
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abcde" region_highlight=( "2 5 faint,bold" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:later highlight attribute takes precedence over earlier one that contradicts
|
||||
>0m: 1mabc0mde
|
||||
|
||||
zpty_start
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abcde" region_highlight=( "2 5 faint,unrecognised,bold" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:unrecognised highlight attribute and any subsequent ones ignored
|
||||
>0m: 2mabc0mde
|
||||
|
||||
zpty_start
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abcd" region_highlight=( "2 5 bold" "3 4 nobold" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:disable highlight attribute with "no" prefix
|
||||
>0m: 1ma0mb1mc0md
|
||||
|
||||
zpty_start
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abcdefg" region_highlight=( "2 5 faint,layer=14" "3 4 bold,layer=12" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:use layers to override precedence of ordering
|
||||
>0m: 2mabc0mdefg
|
||||
|
||||
zpty_start
|
||||
zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin" ); typeset -p region_highlight }'
|
||||
zpty_input 'zle -N rh_widget'
|
||||
@@ -160,6 +200,18 @@
|
||||
0:basic region_highlight with near-color (hex-triplets at input)
|
||||
>0mCDE|3232|true
|
||||
|
||||
zpty_start
|
||||
zpty_input 'zmodload zsh/nearcolor'
|
||||
zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#000000,bg=#ffffff" ); }'
|
||||
zpty_input 'zle -N rh_widget'
|
||||
zpty_input 'bindkey "\C-a" rh_widget'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command
|
||||
zpty_line 1 p # the line of interest, preserving escapes ("p")
|
||||
zpty_stop
|
||||
0:basic region_highlight with near-color using extremes - all black and all white
|
||||
>0mCDE|316|BCDE|4231|true
|
||||
|
||||
zpty_start
|
||||
zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); rh2; }'
|
||||
zpty_input 'rh2() { region_highlight+=( "1 2 fg=red" ); }' # `r' in red; the above line would be too long
|
||||
@@ -208,6 +260,38 @@
|
||||
0:zle $widgetname -f nolast
|
||||
>0m0m: clear-screen
|
||||
|
||||
zpty_start
|
||||
zpty_input '.term.extensions[(r)truecolor]=-truecolor'
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abc" region_highlight=( "2 4 fg=230" "3 5 fg=132,opacity=50%" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:overlapping backgrounds with opacity and nearcolor
|
||||
>0m: CDE|3230|aCDE|3181|bCDE|3132|c
|
||||
|
||||
zpty_start
|
||||
zpty_input '.term.extensions[(r)truecolor]=-truecolor'
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abcde" region_highlight=( "3 4 fg=149,opacity=80" "5 6 fg=182,opacity=49" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:opacity with an unknown base colour
|
||||
>0m: aCDE|3149|b0mcde
|
||||
|
||||
zpty_start
|
||||
zpty_input 'zle-line-pre-redraw() { BUFFER=": abcde" region_highlight=( "2 7 bg=#d5e7c2,fg=#00005f,bold" "4 6 bg=#e0ff50,fg=#e00020,opacity=60/40" ) }'
|
||||
zpty_input 'zle -N zle-line-pre-redraw'
|
||||
zpty_enable_zle
|
||||
zpty_input $'\C-a'
|
||||
zpty_line 1 p
|
||||
zpty_stop
|
||||
0:foreground and background opacity
|
||||
>0m: 1m38;2;0;0;95m48;2;213;231;194mab38;2;134;0;57m48;2;217;240;148mcd38;2;0;0;95m48;2;213;231;194me
|
||||
|
||||
%clean
|
||||
|
||||
zmodload -ui zsh/zpty
|
||||
|
||||
Reference in New Issue
Block a user