GNU bug report logs - #73600
31.0.50; Visual wrap prefix mode and image display

Previous Next

Package: emacs;

Reported by: Gautier Ponsinet <gautier <at> gautierponsinet.xyz>

Date: Wed, 2 Oct 2024 15:21:02 UTC

Severity: normal

Found in version 31.0.50

Done: Jim Porter <jporterbugs <at> gmail.com>

Full log


View this message in rfc822 format

From: Jim Porter <jporterbugs <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 73600 <at> debbugs.gnu.org, gautier <at> gautierponsinet.xyz
Subject: bug#73600: 31.0.50; Visual wrap prefix mode and image display
Date: Sat, 5 Oct 2024 11:07:39 -0700
[Message part 1 (text/plain, inline)]
On 10/4/2024 11:41 PM, Eli Zaretskii wrote:
>> Date: Fri, 4 Oct 2024 12:45:10 -0700
>> Cc: 73600 <at> debbugs.gnu.org, gautier <at> gautierponsinet.xyz
>> From: Jim Porter <jporterbugs <at> gmail.com>
>>
>>     * Here is some text. [IMG]
>>
>> That should get the wrap prefix.
> 
> I think you will find that we always clip images at window edges, so
> the above might be trickier.  Did you actually try that?  How do you
> get at the width of the image actually displayed in that case?

Yes, see attached. "image.html" is a simple page testing both cases, 
which you can try out with something like this:

  (progn
    (setq ;; Show our image inline.
          shr-max-inline-image-size (cons 30 30)
          ;; Don't hard-wrap text; use 'visual-line-mode' and
          ;; 'visual-wrap-prefix-mode'.
          shr-fill-text nil)
    (eww "file:///path/to/image.html"))

I've also attached a screenshot showing this in action.

For this first case, I don't think the width of the image matters to 
'visual-wrap-prefix-mode'; the width we want to get here is the width of 
the prefix string "* ".

>> The reverse is also true, though it's more complex:
>>
>>     [IMG] Here is some text.
> 
> What do you expect to happen here, and how does the code handle this,
> especially the visual width of the image?

Assuming the mode sets 'adaptive-fill-function' correctly, I'd expect to 
see something like the second block of the attached screenshot, or for a 
textual approximation, something like this:

  [IMG] Here is some text, and
        here is some more.

In the attached HTML page, we get the above behavior automatically 
because SHR inserts the string "*" into the buffer to add the image 
display spec to. As a result, the fill prefix is "* " (with the "*" 
having the display spec). Once 'visual-wrap-prefix-mode' passes that to 
'string-pixel-width', we get the actual pixel width we expect.

As you note, this doesn't quite work if Emacs clips the image; in this 
example, 'string-pixel-width' just returns the width of the space after 
the "*" with the image spec. However, the only "problem" this causes is 
that the wrap prefix is smaller than otherwise expected. In practice, 
this might even be a *good* thing since it ensures that the wrap prefix 
isn't wider than the window width.

However, note that this case only comes up if the image is *also* on the 
same logical line as some other text without an image spec. For SHR/EWW, 
this shouldn't happen unless a user sets 'shr-max-inline-image-size' to 
some large value (it's nil by default) *and* 'shr-max-image-proportion' 
to some value >1 (it's 0.9 by default).

For 'image-mode', this doesn't apply at all since the entire buffer text 
has the image spec. Are there other test cases I should look at?

>> +(defun visual-wrap--display-property-safe-p (position offset)
>> +  "Return non-nil if the display property at POSITION is \"safe\".
>> +A \"safe\" display property is one where all the display specs are
>> +members of `visual-wrap--safe-display-specs', and won't interfere with
>> +the additional text properties that `visual-wrap-prefix-mode' uses.
> 
> This should mention the fact that some 'display' properties replace
> the text and some don't, otherwise the above doesn't really explain
> the underlying issue.  The text reads as if it says '"Safe" display
> properties are those which are safe and don't interfere with adding
> more properties'.  Without more details about the cause this is not
> helpful.

Ok, done. (I moved this explanation to the defvar since that's the place 
where people might change the setting.)

> 
>> +        (when (or (vectorp display) (listp display))
>> +          (unless (listp (car display)) (setq display (list display)))
>> +          (not (catch 'unsafe
>> +                 (mapc (lambda (spec)
>> +                         (unless (memq spec visual-wrap--safe-display-specs)
>> +                           (throw 'unsafe t)))
>> +                       display)))))))
> 
> I'm not sure I understand this.  DISPLAY here could also be a single
> value, not a list or a vector, but in that case you don't test it
> against the "safe" specs?  Also, (car display) will signal an error if
> DISPLAY is a vector, but in any case, what is the significance of the
> first element of DISPLAY being a list?  I'm probably missing
> something.

In the case where DISPLAY is a single value (e.g. a string), we just 
treat it as unsafe. This code is an attempt to adapt the logic from 
find_display_property. I've added some comments explaining the logic and 
fixed the issue you mentioned.

If this way looks reasonable, I could add some regression tests for it. 
Alternately, maybe there should be some function that "normalizes" the 
display property so that it's always a list of specs (or something like 
that). Then it would be easier to write a function like I did here that 
just wants to ask, "Are all the specs on my safe list?"

>> -    (when-let ((first-line-prefix (fill-match-adaptive-prefix))
>> +    (when-let ((eol (pos-eol))
>> +               ;; Don't add wrapping properties if we're in the middle
>> +               ;; of an unsafe multi-line display spec.  (For example,
>> +               ;; this could be an image containing a newline-byte being
>> +               ;; displayed in `image-mode'; see bug#73600.)
>> +               ((visual-wrap--display-property-safe-p (point) -1))
>> +               ((visual-wrap--display-property-safe-p eol 1))
>> +               (first-line-prefix (fill-match-adaptive-prefix))
> 
> I'm not sure I can reason about this logic.  I'd prefer the code to
> completely skip text covered by "unsafe" display properties, because
> reasoning about that text makes no sense: it will not show on display.
> Specifically, for the "text" that is the bytes of the image file, I
> don't know how to begin reasoning about newlines embedded in those
> bytes.  Thus, it is hard for me to tell if I agree with the above
> logic or not.

The idea here is this:

1. If we have some display property that spans multiple lines (either
   from the previous line onto our current line, or from the current
   line onto the next), then we need to be careful and check the specs
   of this multi-line display property.

  a. If the multi-line display prop is something safe (e.g. 'raise'), we
     can proceed with adding our visual-wrap props.

  b. Otherwise, we can't safely add visual-wrap props, so we bail out.

2. If no display property spans multiple lines, then any display props
   on our line are *only* on our line, so we can add the visual-wrap
   props without breaking the 'eq'-ness of any existing display props.
   That's there to allow the behavior as shown in "image.html".

For the image-mode case, if we have one newline-byte, then we hit 
condition (1): we have the same display property on both the first and 
second lines. Then we examine each display spec, and find that there's 
one that's not on the safe list (b), so we bail out.

I agree that this logic is fairly complex, and I'm happy to rearrange it 
or add comments as needed once we're on the same page. I'm also ok with 
just saying "any multi-line display properties are assumed to be unsafe" 
for now. That's simpler, and we could add this extra logic later if 
someone notices a specific issue.
[0001-Don-t-add-visual-wrap-prefix-properties-to-multi-lin.patch (text/plain, attachment)]
[image.html (text/html, attachment)]
[rendered.png (image/png, attachment)]

This bug report was last modified 20 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.