zsh-workers
 help / color / mirror / code / Atom feed
From: Roman Perepelitsa <roman.perepelitsa@gmail.com>
To: Daniel Shahaf <d.s@daniel.shahaf.name>
Cc: Marlon Richert <marlon.richert@gmail.com>,
	Zsh hackers list <zsh-workers@zsh.org>
Subject: Re: region_highlight converts `fg=default` to `none`, which is not the same
Date: Thu, 15 Oct 2020 09:37:21 +0200	[thread overview]
Message-ID: <CAN=4vMqencidrpknTheyjAYQRT9BHN8ZLXRFk2Z38Gfwx9mxyQ@mail.gmail.com> (raw)
In-Reply-To: <20201014204621.4cf5b2b0@tarpaulin.shahaf.local2>

[-- Attachment #1: Type: text/plain, Size: 4591 bytes --]

On Wed, Oct 14, 2020 at 10:46 PM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> z-sy-h does «region_highlight_copy=("${region_highlight[@]}"); …
> region_highlight=("${region_highlight_copy[@]}");» around invoking
> a highlighter, so the serialization bug could explain the observed ZLE
> behaviour, couldn't it?

I haven't tried reproducing the problem with z-sy-h. Instead, I've
used the following code from `zsh -f`:

    zle-line-pre-redraw() {
      region_highlight=('0 5 fg=1' '1 2 fg=default' '3 4 none')
    }
    zle -N zle-line-pre-redraw

If you type "12345", odd and only odd numbers are supposed to be red.
The actual behavior is that all numbers are red.

I now took a look at how highlighting is applied (function zrefresh in
Src/Zle/zle_refresh.c), then read the docs again, and I think there is
a problem. If the same character is affected by two highlight
specifications, how should it be highlighted? For example, if the
first spec sets fg=1 and the second sets bg=2, how should the
character be highlighted? What about fg=1 plus underline? Or underline
plus fg=1?

I could imagine two simple merging strategies:

1. All attributes are merged, so fg=1 + bg=2 + underline would result
in underlined red text on green background.
2. The second highlight completely overrides the first. fg=1 + bg=2 +
underline results in underlined text with the default color and no
background.

The meaning of "none" naturally follows from the choice of merging
strategy. In the first case region highlight with "none" spec has no
effect (X + none => X). In the second case such a region is displayed
without any highlighting (X + none => no highlighting).

The actual code does something else. If a spec has fg or bg with any
value other than "default", then the spec completely overrides the
previous spec. Otherwise the spec is merged with the previous spec
with one exception: fg=default and bg=default have no effect (fg=1 +
fg=default,underline => fg=1,underline).

Note: "special" highlight is merged with a different algorithm. All
other highlights, including "region", "isearch" and "paste", are
merged the same way as the elements of region_highlights.

A few examples of what the current code does:

- fg=1 + bg=2 => bg=2
  * the second spec completely overrides the first
- fg=1 + underline => fg=1,underline
  * specs are merged
- underline + fg=1 => fg=1
  * the second spec completely overrides the first
- fg=1 + none => fg=1
  * specs are merged
- fg=1 + fg=default,underline => fg=1,underline
  * specs are merged except that fg=default has no effect

This doesn't look ideal. So, how can we fix it?

"The second highlight completely overrides the first" will change the
meaning of "none" from "ignore this spec completely" to "display text
with no highlighting". For example, if you set
zle_highlight=(special:none), special characters will have no
highlighting whatsoever, while currently they would get highlighted by
region_highlight.

"All attributes are merged" makes it impossible to disable underline,
bold, etc. We have fg=default and bg=default to disable colors but
there is no equivalent syntax for disabling underline.

I think there is one change to the current algorithm that would be an
improvement and is relatively safe to do -- make fg=default and
bg=default work similarly to fg=1 and bg=1 as far as merging goes.
Since fg=1 in a spec causes a complete override (disabling underline,
bold, etc.), fg=default should also do that. Patch attached (only for
zrefresh; singlerefresh should be updated similarly). With this patch,
if a spec has fg or bg, then it completely overrides the previous
spec; otherwise the spec is merged with the previous spec.

From the examples above, only the following works differently with the
provided patch: fg=1 + fg=default,underline used to produce
fg=1,underline but now it gives just underline without foreground
color.

Going forward, we can extend the spec syntax to give users more
flexibility. I can see two extensions.

1. In addition to "underline", one can use "underline=on" and "underline=off".
2. If the first character of the spec is "+", it's merged with the
current spec for the region; if the character is "=", it overrides;
otherwise the current behavior is preserved (specs with fg and bg
override, other specs merge).

Thoughts?

Roman.

P.S.

singlerefresh() doesn't use default_atr_on and thus
zle_highlight=(default:fg=1) has no effect if SINGLE_LINE_ZLE is set.
Is this intended?

[-- Attachment #2: highlight-patch.txt --]
[-- Type: text/plain, Size: 1500 bytes --]

diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index d9d9503e2..1b246191f 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -1278,27 +1278,22 @@ zrefresh(void)
 		offset = predisplaylen; /* increment over it */
 	    if (rhp->start + offset <= tmppos &&
 		tmppos < rhp->end + offset) {
-		if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
-		    /* override colour with later entry */
-		    base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) |
-			rhp->atr;
-		} else {
-		    /* no colour set yet */
+		if (rhp->atr & (TXTFGCOLOUR|TXTNOFGCOLOUR|TXTBGCOLOUR|TXTNOBGCOLOUR))
+		    base_atr_on = rhp->atr;
+		else
 		    base_atr_on |= rhp->atr;
-		}
 		if (tmppos == rhp->end + offset - 1 ||
 		    tmppos == tmpll - 1)
 		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
 	    }
 	}
-	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
-	    /* keep colours from special attributes */
-	    all_atr_on = special_atr_on |
-		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
-	} else {
-	    /* keep colours from standard attributes */
-	    all_atr_on = special_atr_on | base_atr_on;
-	}
+
+	all_atr_on = base_atr_on;
+	if (special_atr_on & (TXTFGCOLOUR|TXTNOFGCOLOUR))
+	    all_atr_on &= ~(TXT_ATTR_FG_ON_MASK|TXTNOFGCOLOUR);
+	if (special_atr_on & (TXTBGCOLOUR|TXTNOBGCOLOUR))
+	    all_atr_on &= ~(TXT_ATTR_BG_ON_MASK|TXTNOBGCOLOUR);
+	all_atr_on |= special_atr_on;
 	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
 
 	if (t == scs)			/* if cursor is here, remember it */

  reply	other threads:[~2020-10-15  7:38 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-12  9:22 Marlon Richert
2020-10-13 11:05 ` Roman Perepelitsa
2020-10-14  5:12   ` Marlon Richert
2020-10-14 20:46   ` Daniel Shahaf
2020-10-15  7:37     ` Roman Perepelitsa [this message]
2020-10-15 16:58       ` Marlon Richert
2020-10-15 17:09         ` Roman Perepelitsa
2020-10-16 13:36           ` Daniel Shahaf
2020-10-24  1:34           ` Oliver Kiddle
2020-10-24  6:42             ` Roman Perepelitsa
2020-10-16 13:28       ` Daniel Shahaf
2020-10-16 15:50         ` Bart Schaefer
2020-10-22 19:58           ` Marlon Richert
2020-10-22 23:28             ` Daniel Shahaf
2020-10-23  8:08             ` Roman Perepelitsa
2020-10-23  9:24               ` Marlon Richert
2020-10-23  9:35                 ` Roman Perepelitsa
2020-10-23 10:40                   ` Marlon Richert
2020-10-23 11:38                     ` Roman Perepelitsa
2020-10-24  0:50                       ` Functions/Misc/colors vs. region_highlight Bart Schaefer
2020-11-03 18:54                         ` Marlon Richert
2020-11-03 23:45                           ` Bart Schaefer
2020-11-04 15:47                             ` Marlon Richert
2021-04-18 21:40                         ` Bart Schaefer
2020-10-23 23:57                   ` Threading across year boundaries (was: Re: region_highlight converts `fg=default` to `none`, which is not the same) Daniel Shahaf
2021-03-31  8:26       ` region_highlight converts `fg=default` to `none`, which is not the same Marlon Richert
2021-04-10 20:33         ` Lawrence Velázquez
2021-04-13 15:20           ` Daniel Shahaf
2021-04-13 20:33             ` Roman Perepelitsa
2021-04-14 11:04               ` Daniel Shahaf
2021-05-09 20:49                 ` Lawrence Velázquez
2021-05-31  1:16                   ` Lawrence Velázquez
2021-03-31  8:24   ` Marlon Richert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAN=4vMqencidrpknTheyjAYQRT9BHN8ZLXRFk2Z38Gfwx9mxyQ@mail.gmail.com' \
    --to=roman.perepelitsa@gmail.com \
    --cc=d.s@daniel.shahaf.name \
    --cc=marlon.richert@gmail.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).