GNU bug report logs - #41513
`compute-motion' can miscount buffer positions in the presence of 'before-string/'after-string overlays

Previous Next

Package: emacs;

Reported by: Stephen Bach <sjbach <at> sjbach.com>

Date: Sun, 24 May 2020 18:45:01 UTC

Severity: normal

Done: Stefan Kangas <stefan <at> marxist.se>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 41513 in the body.
You can then email your comments to 41513 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Sun, 24 May 2020 18:45:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Stephen Bach <sjbach <at> sjbach.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sun, 24 May 2020 18:45:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Stephen Bach <sjbach <at> sjbach.com>
To: bug-gnu-emacs <at> gnu.org
Subject: `compute-motion' can miscount buffer positions in the presence of
 'before-string/'after-string overlays
Date: Sun, 24 May 2020 14:30:13 -0400
[Message part 1 (text/plain, inline)]
Hi,

Please see the attached demonstration code, runnable with `emacs -Q' and
`eval-buffer'.

When `compute-motion' scans along a buffer range that covers an overlay
with a 'buffer-string or 'after-string property [*] having as its content a
sort-of tricky but not too tricky propertized string (described below),
`compute-motion' can/will return an incorrect POS value.

E.g. an overlay with this plist:

  '(before-string #("AAA" 0 3 (display ""))

This overlay is effectively a no-op for redisplay - the "AAA" string that
would have been printed at the start of the overlay interval is not printed
because its presentation is suppressed by the string's 'display property.
This is right and proper.

However, `compute-motion' appears to interpret this overlay as having an
effective visible length of -3 rather than 0. Ditto if the overlay had
instead had an 'after-string property with the same content. [*] The
effective length being negative suggests to me an overcorrection rather
than missing logic.

([*] Aside: I believe `compute-motion' sometimes also miscalculates
'display as an overlay property for cases where one might reasonably expect
it to work- but I limit this report to 'before-string and 'after-string.)

Popular packages such as flycheck and git-gutter use this pattern of
overlay to annotate the margin and the fringe. The pattern is increasingly
common which in turn increasingly limits the application of
`compute-motion' as a reliable exposed function.

The miscounting appears to exist at least as far back as Emacs 24. Might
there be a mitigation/workaround? I like `compute-motion' despite its
complicated signature and complicated return value (and despite its
apparently rare use in the extended ecosystem) - it's fast and it involves
no cursor movement to perform its measurements.

Thanks for reading.


Testcase output:
=======================================

-------------------------------------
Without overlay:
- ‘point’ at col 30: <31>
- buffer content on and after col 30: <30   35   40   45   50>
- posn coord at col 30: <(30 . 0)>
- buffer pos of col 30 according to ‘compute-motion’: <31>     [ok]
- coord of col 30 according to ‘compute-motion’: <(30 . 0)>
-------------------------------------
With ’before-string overlay:
- ‘point’ at col 30: <31>
- buffer content on and after col 30: <30   35   40   45   50>
- posn coord at col 30: <(30 . 0)>
- buffer pos of col 30 according to ‘compute-motion’: <28>     <<<
INCONSISTENT
- coord of col 30 according to ‘compute-motion’: <(30 . 0)>
-------------------------------------
With ’after-string overlay:
- ‘point’ at col 30: <31>
- buffer content on and after col 30: <30   35   40   45   50>
- posn coord at col 30: <(30 . 0)>
- buffer pos of col 30 according to ‘compute-motion’: <28>     <<<
INCONSISTENT
- coord of col 30 according to ‘compute-motion’: <(30 . 0)>
[Message part 2 (text/html, inline)]
[compute-motion-testcase.el (application/octet-stream, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Sun, 24 May 2020 19:10:01 GMT) Full text and rfc822 format available.

Message #8 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Stephen Bach <sjbach <at> sjbach.com>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Sun, 24 May 2020 22:09:19 +0300
> From: Stephen Bach <sjbach <at> sjbach.com>
> Date: Sun, 24 May 2020 14:30:13 -0400
> 
> The miscounting appears to exist at least as far back as Emacs 24. Might there be a mitigation/workaround?
> I like `compute-motion' despite its complicated signature and complicated return value (and despite its
> apparently rare use in the extended ecosystem) - it's fast and it involves no cursor movement to perform its
> measurements.

Don't use compute-motion.  It is a remnant of the old pre-Emacs 21
display engine, and almost certainly doesn't support tricky display
features.

There are other functions that can do the same job.  If you describe
what you need to do, I could try suggesting some alternatives that
will work.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Sun, 24 May 2020 19:56:01 GMT) Full text and rfc822 format available.

Message #11 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Stephen Bach <sjbach <at> sjbach.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Sun, 24 May 2020 15:55:09 -0400
On Sun, May 24, 2020 at 3:09 PM Eli Zaretskii <eliz <at> gnu.org> wrote:
> Don't use compute-motion.  It is a remnant of the old pre-Emacs 21
> display engine, and almost certainly doesn't support tricky display
> features.
>
> There are other functions that can do the same job.  If you describe
> what you need to do, I could try suggesting some alternatives that
> will work.

Thanks Eli. Specifically, I'm measuring the jagged contour of a buffer's
layout within a particular window, i.e. relating the final column of
each visual row (as displayed within the window) to concrete buffer
positions. This is notionally straightforward but complicated in
practice, as you will know -- e.g. line continuation/truncation,
`word-wrap', `visual-line-mode', `adaptive-wrap-prefix-mode',
multi-columnar glyphs. Text properties or overlays using 'display, and
so on.

In my initial go at this I used `end-of-visual-line' calls within a
`save-excursion' but I found that to be a little slow given the
frequency I was making the calls and I was also uncomfortable about all
the cursor movement, perhaps irrationally. But maybe this is the correct
approach?

Re: `compute-motion', perhaps the manual or the docstring could include
a short note to dissuade developers from using it? Its complicated
interface may be sufficient to keep most people away, but on the other
hand a complicated interface can suggest efficiency and intricate
functionality.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Mon, 25 May 2020 15:24:02 GMT) Full text and rfc822 format available.

Message #14 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Stephen Bach <sjbach <at> sjbach.com>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Mon, 25 May 2020 18:23:03 +0300
> From: Stephen Bach <sjbach <at> sjbach.com>
> Date: Sun, 24 May 2020 15:55:09 -0400
> Cc: 41513 <at> debbugs.gnu.org
> 
> > There are other functions that can do the same job.  If you describe
> > what you need to do, I could try suggesting some alternatives that
> > will work.
> 
> Thanks Eli. Specifically, I'm measuring the jagged contour of a buffer's
> layout within a particular window, i.e. relating the final column of
> each visual row (as displayed within the window) to concrete buffer
> positions. This is notionally straightforward but complicated in
> practice, as you will know -- e.g. line continuation/truncation,
> `word-wrap', `visual-line-mode', `adaptive-wrap-prefix-mode',
> multi-columnar glyphs. Text properties or overlays using 'display, and
> so on.

Please tell more.  What exactly would you like to measure?  Is it the
width of each screen line? or just the maximum width of the longest
line among those shown in the window? or something else?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Mon, 25 May 2020 20:17:01 GMT) Full text and rfc822 format available.

Message #17 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Stephen Bach <sjbach <at> sjbach.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Mon, 25 May 2020 16:16:08 -0400
On Mon, May 25, 2020 at 11:22 AM Eli Zaretskii <eliz <at> gnu.org> wrote:
>
> Please tell more.  What exactly would you like to measure?  Is it the
> width of each screen line? or just the maximum width of the longest
> line among those shown in the window? or something else?

The width of each screen line, kind-of. I'm writing a
terminal-compatible drawing library for displaying low-res
visualizations in the buffer/window:

   - https://sjbach.com/canvas-emacs-logo.mp4
   - https://sjbach.com/canvas-mario.mp4

To draw an image I apply temporary overlays to buffer ranges that
correspond to window coordinates which themselves correspond to the
"pixels" of the image.

A performance-sensitive part of this turns out to be when I walk between
`window-start' and `window-end' to figure out (1) the buffer positions
of the final characters of visible screen lines/visual rows, and (2) the
buffer positions underlying particular window coordinates that happen to
fall within the buffer text -- both of which I've been using
`compute-motion' to calculate.

Is `vertical-motion' a more reliable analogue to `compute-motion'? I see
it can take a (COLS . LINES) argument, so it appears similar.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Tue, 26 May 2020 16:22:02 GMT) Full text and rfc822 format available.

Message #20 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Stephen Bach <sjbach <at> sjbach.com>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Tue, 26 May 2020 19:21:05 +0300
> From: Stephen Bach <sjbach <at> sjbach.com>
> Date: Mon, 25 May 2020 16:16:08 -0400
> Cc: 41513 <at> debbugs.gnu.org
> 
> Is `vertical-motion' a more reliable analogue to `compute-motion'? I see
> it can take a (COLS . LINES) argument, so it appears similar.

Depending on what exactly do you need to do, I can suggest the
following APIs:

  vertical-motion
  posn-at-point
  pos-visible-in-window-p
  window-text-pixel-size

Let me know if these don't cover the functionalities that you need.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Tue, 26 May 2020 18:13:02 GMT) Full text and rfc822 format available.

Message #23 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Stephen Bach <sjbach <at> sjbach.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Tue, 26 May 2020 14:12:10 -0400
On Tue, May 26, 2020 at 12:21 PM Eli Zaretskii <eliz <at> gnu.org> wrote:
>
> Depending on what exactly do you need to do, I can suggest the
> following APIs:
>
>   vertical-motion
>   posn-at-point
>   pos-visible-in-window-p
>   window-text-pixel-size
>
> Let me know if these don't cover the functionalities that you need.

I overlooked pos-visible-in-window-p before, thanks for the reference.

Do you have any points of advice for avoiding side-effects when moving
the cursor around the buffer programmatically?

What I have so far:
- Wrap in save-excursion
- Bind to t:
  - inhibit-point-motion-hooks
  - cursor-sensor-inhibit
  - inhibit-field-text-motion (for precision, might be superfluous)
- Bind to nil:
  - goal-column (probably superfluous)

Thanks Eli, I appreciate the help and also the work you do for Emacs.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#41513; Package emacs. (Tue, 26 May 2020 19:10:02 GMT) Full text and rfc822 format available.

Message #26 received at 41513 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Stephen Bach <sjbach <at> sjbach.com>
Cc: 41513 <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Tue, 26 May 2020 22:09:23 +0300
> From: Stephen Bach <sjbach <at> sjbach.com>
> Date: Tue, 26 May 2020 14:12:10 -0400
> Cc: 41513 <at> debbugs.gnu.org
> 
> Do you have any points of advice for avoiding side-effects when moving
> the cursor around the buffer programmatically?
> 
> What I have so far:
> - Wrap in save-excursion
> - Bind to t:
>   - inhibit-point-motion-hooks
>   - cursor-sensor-inhibit
>   - inhibit-field-text-motion (for precision, might be superfluous)
> - Bind to nil:
>   - goal-column (probably superfluous)

save-excursion should do, usually.

> Thanks Eli, I appreciate the help and also the work you do for Emacs.

You are welcome.




Reply sent to Stefan Kangas <stefan <at> marxist.se>:
You have taken responsibility. (Mon, 24 Aug 2020 00:27:02 GMT) Full text and rfc822 format available.

Notification sent to Stephen Bach <sjbach <at> sjbach.com>:
bug acknowledged by developer. (Mon, 24 Aug 2020 00:27:02 GMT) Full text and rfc822 format available.

Message #31 received at 41513-done <at> debbugs.gnu.org (full text, mbox):

From: Stefan Kangas <stefan <at> marxist.se>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Stephen Bach <sjbach <at> sjbach.com>, 41513-done <at> debbugs.gnu.org
Subject: Re: bug#41513: `compute-motion' can miscount buffer positions in the
 presence of 'before-string/'after-string overlays
Date: Sun, 23 Aug 2020 20:26:44 -0400
Eli Zaretskii <eliz <at> gnu.org> writes:

>> From: Stephen Bach <sjbach <at> sjbach.com>
>> Date: Tue, 26 May 2020 14:12:10 -0400
>> Cc: 41513 <at> debbugs.gnu.org
>>
>> Do you have any points of advice for avoiding side-effects when moving
>> the cursor around the buffer programmatically?
>>
>> What I have so far:
>> - Wrap in save-excursion
>> - Bind to t:
>>   - inhibit-point-motion-hooks
>>   - cursor-sensor-inhibit
>>   - inhibit-field-text-motion (for precision, might be superfluous)
>> - Bind to nil:
>>   - goal-column (probably superfluous)
>
> save-excursion should do, usually.
>
>> Thanks Eli, I appreciate the help and also the work you do for Emacs.
>
> You are welcome.

From skimming this thread, it looks like all issues here were
resolved.  I'm therefore closing this bug report.

If this conclusion is incorrect, please reply to this email (use "Reply
to all" in your email client) and we can reopen the bug report.

Best regards,
Stefan Kangas




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Mon, 21 Sep 2020 11:24:03 GMT) Full text and rfc822 format available.

This bug report was last modified 3 years and 218 days ago.

Previous Next


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