GNU bug report logs - #31717
26.1; display-line-numbers-mode enlarges indicator without need

Previous Next

Package: emacs;

Reported by: Carlos Pita <carlosjosepita <at> gmail.com>

Date: Tue, 5 Jun 2018 02:36:01 UTC

Severity: normal

Tags: notabug

Found in version 26.1

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 31717 in the body.
You can then email your comments to 31717 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#31717; Package emacs. (Tue, 05 Jun 2018 02:36:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Carlos Pita <carlosjosepita <at> gmail.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Tue, 05 Jun 2018 02:36:02 GMT) Full text and rfc822 format available.

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

From: Carlos Pita <carlosjosepita <at> gmail.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 26.1; display-line-numbers-mode enlarges indicator without need
Date: Mon, 04 Jun 2018 23:34:52 -0300
When transitioning from line 69 to line 70 a new column is added to the
left of the indicator as if there was a need to fit three digits despite
that the file has less than 100 lines and despite that if it were larger
line 100 would fall out of the window.

I have tested the transition from 969 to 970 and it shows the same
effect.

It's as if the mode is anticipating an extra digit soon and playing
conservative. I understand that there could be performance reasons for
doing this (thus avoiding the need to compute the remaining lines in a
window or file) but:

1. The mode is already adding two whitespaces around the line number
indicator. Wasting one more char is on the verge of infuriating ;)

2. AFAICS computing the file size doesn't seem too demanding a task,
neither the remaining lines in a window  (although the window could be
resized).

---

In GNU Emacs 26.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.30)
 of 2018-05-29 built on juergen
Windowing system distributor 'The X.Org Foundation', version 11.0.11906000
System Description:	Manjaro Linux

Recent messages:
Quit [2 times]
C-h C-g is undefined
nil
Quit
Display-Line-Numbers mode disabled in current buffer
x is undefined
Display-Line-Numbers mode enabled in current buffer
0 (#o0, #x0, ?\C-@)
scroll-down-command: Beginning of buffer [3 times]
Making completion list...

Configured using:
 'configure --prefix=/usr --sysconfdir=/etc --libexecdir=/usr/lib
 --localstatedir=/var --with-x-toolkit=gtk3 --with-xft --with-modules
 'CFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fstack-protector-strong
 -fno-plt' CPPFLAGS=-D_FORTIFY_SOURCE=2
 LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now'

Configured features:
XPM JPEG TIFF GIF PNG RSVG IMAGEMAGICK SOUND GPM DBUS GSETTINGS NOTIFY
ACL GNUTLS LIBXML2 FREETYPE M17N_FLT LIBOTF XFT ZLIB TOOLKIT_SCROLL_BARS
GTK3 X11 MODULES THREADS LIBSYSTEMD LCMS2

Important settings:
  value of $LC_MONETARY: en_US.UTF-8
  value of $LC_NUMERIC: en_US.UTF-8
  value of $LC_TIME: en_US.UTF-8
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Info

Minor modes in effect:
  display-line-numbers-mode: t
  diff-auto-refine-mode: t
  pyvenv-mode: t
  shell-dirtrack-mode: t
  ido-everywhere: t
  winner-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  buffer-read-only: t
  column-number-mode: t
  transient-mark-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message rmc puny dired dired-loaddefs
rfc822 mml mml-sec epa derived epg gnus-util rmail rmail-loaddefs
mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils
mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr
mail-utils pp cl-print jka-compr vc-git display-line-numbers
doom-tomorrow-night-theme doom-themes doom-themes-common cl-extra
yasnippet elec-pair highlight-indentation flymake-proc flymake warnings
company edmacro kmacro pcase help-fns radix-tree help-mode elpy
find-file-in-project ivy delsel ivy-overlay ffap thingatpt diff-mode
easy-mmode elpy-shell pyvenv esh-var esh-io esh-cmd esh-opt esh-ext
esh-proc esh-arg esh-groups eshell esh-module esh-mode esh-util
elpy-profile elpy-django s elpy-refactor subr-x python tramp-sh tramp
tramp-compat tramp-loaddefs trampver ucs-normalize shell pcomplete
parse-time format-spec advice json map ido grep compile comint
ansi-color files-x etags xref project cus-edit cus-start cus-load
wid-edit winner ring windmove finder-inf info package easymenu
epg-config url-handlers url-parse auth-source cl-seq eieio eieio-core
cl-macs eieio-loaddefs password-cache url-vars seq byte-opt gv bytecomp
byte-compile cconv cl-loaddefs cl-lib time-date mule-util tooltip eldoc
electric uniquify ediff-hook vc-hooks lisp-float-type mwheel term/x-win
x-win term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode elisp-mode lisp-mode
prog-mode register page menu-bar rfn-eshadow isearch timer select
scroll-bar mouse jit-lock font-lock syntax facemenu font-core
term/tty-colors frame cl-generic cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932
hebrew greek romanian slovak czech european ethiopic indian cyrillic
chinese composite charscript charprop case-table epa-hook jka-cmpr-hook
help simple abbrev obarray minibuffer cl-preloaded nadvice loaddefs
button faces cus-face macroexp files text-properties overlay sha1 md5
base64 format env code-pages mule custom widget hashtable-print-readable
backquote dbusbind inotify lcms2 dynamic-setting system-font-setting
font-render-setting move-toolbar gtk x-toolkit x multi-tty
make-network-process emacs)

Memory information:
((conses 16 395972 45995)
 (symbols 48 33197 1)
 (miscs 40 181 236)
 (strings 32 77765 6307)
 (string-bytes 1 2252569)
 (vectors 16 53016)
 (vector-slots 8 923948 18050)
 (floats 8 186 268)
 (intervals 56 4758 0)
 (buffers 992 16))




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#31717; Package emacs. (Tue, 05 Jun 2018 02:56:01 GMT) Full text and rfc822 format available.

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

From: Carlos Pita <carlosjosepita <at> gmail.com>
To: 31717 <at> debbugs.gnu.org
Date: Mon, 4 Jun 2018 23:54:44 -0300
Also, when the mode is visual or relative this is happening too.

In these cases, given that I assume you are assuming <= 69 is a safe
guess, it will be *always* safe to limit the indicator width to that
required by the current line.

So this would be an easy optimization: in visual and relative modes
never anticipate an additional digit.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#31717; Package emacs. (Tue, 05 Jun 2018 03:31:02 GMT) Full text and rfc822 format available.

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

From: Carlos Pita <carlosjosepita <at> gmail.com>
To: 31717 <at> debbugs.gnu.org
Date: Tue, 5 Jun 2018 00:30:10 -0300
A little more experimentation with different windows sizes made me
realize that the <70 hard and fast rule doesn't exist. Now I see the
threshold indeed depends on the window size. But it is still too
conservative, leading to premature enlargement of the line number
space.

I suggest three optimizations in order of assumed (wild guessed)
ascending difficulty:

1. For relative/visual modes never reserve extra space.
2. If the lines in the file are < N never reserve extra space for >= N.
3. Take also into account the remaining window lines (I assume this is
not as easy as it sounds).




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#31717; Package emacs. (Tue, 05 Jun 2018 14:23:01 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Carlos Pita <carlosjosepita <at> gmail.com>
Cc: 31717 <at> debbugs.gnu.org
Subject: Re: bug#31717: 26.1;
 display-line-numbers-mode enlarges indicator without need
Date: Tue, 05 Jun 2018 17:22:50 +0300
tags 31717 notabug
thanks

> From: Carlos Pita <carlosjosepita <at> gmail.com>
> Date: Tue, 5 Jun 2018 00:30:10 -0300

(Sorry for a longish response, but you raise design and implementation
issues which cannot be explained without some non-trivial background.)

> When transitioning from line 69 to line 70 a new column is added to the
> left of the indicator as if there was a need to fit three digits despite
> that the file has less than 100 lines and despite that if it were larger
> line 100 would fall out of the window.

Right, this is the intended behavior, and there are good reasons for
it.  See below.

> It's as if the mode is anticipating an extra digit soon and playing
> conservative. I understand that there could be performance reasons for
> doing this (thus avoiding the need to compute the remaining lines in a
> window or file)

The main reason is performance, but it's not the only one.  You must
keep in mind that the code which produces line numbers is part of the
display engine -- it produces the line number as part of that line's
layout -- and that imposes very specific restrictions and conditions
on what we can do, how, and when.  I try to explain the issues below
in more detail, but the main point is that you simply _cannot_ think
about stuff done in redisplay as if it were some Lisp program running
off post-command-hook or somesuch, it's a very different runtime
environment with peculiar requirements.

> but:
> 
> 1. The mode is already adding two whitespaces around the line number
> indicator. Wasting one more char is on the verge of infuriating ;)

If you don't want to "waste" one more column, you can use one of the
optional features, e.g., set display-line-numbers-width-start non-nil,
with or without display-line-numbers-grow-only non-nil.

> 2. AFAICS computing the file size doesn't seem too demanding a task,
> neither the remaining lines in a window  (although the window could be
> resized).

Doing this as part of a redisplay cycle is precisely what made linum
mode so slow.  In a nutshell, it would require displaying each window
twice, because the exact number of lines in a window is known only
after it was completely redrawn (remember that Emacs supports
variable-size fonts and non-character display elements such as images,
which all affect the number of lines in a window).

As for computing the number of lines in a buffer, I guess you never
have to deal with very large buffers, if you think it's scalable.
With today's 64-bit architecture, the maximum buffer size supported by
Emacs is too huge to allow us to count lines all the time; plus, as I
explain below, the display code which produces line numbers is called
much more than you evidently assume.

> Also, when the mode is visual or relative this is happening too.

By default, visual and relative line numbers still display the
absolute line number for the current line, so we still need enough
space for that.  And the current line could be the last line of the
window.  (And if you think the fact that only the current line needs
this makes the life of the implementation easier, read on, and you
will understand why not.)

> In these cases, given that I assume you are assuming <= 69 is a safe
> guess, it will be *always* safe to limit the indicator width to that
> required by the current line.

As you later discovered, there's no 69 value anywhere in the code, the
space needed for the line-number display is estimated dynamically and
depends on the window size and the font size used by the window's
frame.  For a very large window, the switch could be much sooner than
line 69; for a very small window, it could be very close to 100.

> A little more experimentation with different windows sizes made me
> realize that the <70 hard and fast rule doesn't exist. Now I see the
> threshold indeed depends on the window size. But it is still too
> conservative, leading to premature enlargement of the line number
> space.

It _must_ be conservative, because it needs to decide on the space for
the line numbers _before_ it displays the first line of the window.

Here's some background information regarding the requirements imposed
by the display engine on the line-number implementation:

The Emacs display engine works by "screen lines".  (A screen line is a
single horizontal line of the display "canvas"; continuation lines
count as additional screen lines.)  The workhorse of the display
engine is a function that lays out a single screen line.  In some
cases Emacs invokes that function to redraw all the lines of a window,
one by one; in other cases, the function is invoked to produce only a
few screen lines, or even just one, when Emacs is able to decide up
front that none of the other screen lines need to change on display --
being able to do this is an important part of Emacs redisplay
optimizations.

In addition, Emacs needs to take text layout on display into
consideration in many situations that have little to do with actual
redisplay.  For example, vertical-motion (which is the basis of any
vertical cursor movement and scrolling commands) needs to determine
the character directly below or above the character at point,
something that obviously depends on the stuff displayed between point
and that other place, the faces involved, etc.  As another example,
consider a mouse click inside the window, where Emacs needs to know
the buffer position at the click coordinates.

For these reasons, the display routines that perform layout
calculations are used _a_lot_ in Emacs, and must (a) be very
efficient, and (b) produce consistent results no matter whether they
are called to redraw the entire window or just a small part thereof.
In particular, the functions that lay out a single screen line have no
idea about the context in which they were called -- they don't know
whether they are called as part of a complete window redisplay, or
just to draw a single screen line.  They must produce exactly the same
results regardless, and without depending on any such context.

I hope you are now beginning to understand why the feature works as it
does.  The code must calculate the space required for line-number
display before it displays the first line of a window, and the result
must be guaranteed to fit for all the lines in that window, because
otherwise some window line near the end might need more space, and you
will either have an ugly "jagged" display, or will need to abandon the
redisplay cycle and trigger another cycle -- which will be slow
(that's basically what linum.el does, and we already know it's slow).
How do we estimate the largest line number to be displayed in the
window in a way that is never too small?  Well, the display engine
keeps an estimation of the maximum number of screen lines in the
window, based on the fonts defined by the frame -- it needs to know
that because that defines the dimensions of the canvas used for
displaying the window.  So we simply reuse that estimation for this
purpose.  That estimation is always a conservative one, and it cannot
be any other way.  (Btw, it is much less conservative on TTY frames,
for obvious reasons, so there you will see much less "waste".)

> I suggest three optimizations in order of assumed (wild guessed)
> ascending difficulty:
> 
> 1. For relative/visual modes never reserve extra space.

Cannot be done, because when the space for line numbers is computed,
Emacs doesn't yet know what will be the current line, nor where in the
window it will be.  The current line, which is the line that displays
the cursor, is computed only near the end of the redisplay cycle of a
window.  And the code must use the same conservative estimate to be
able to display the largest line number possible.  If you don't need
to see the absolute line number in relative mode, customize
display-line-numbers-current-absolute to nil, and the "waste" will
indeed be no more.

> 2. If the lines in the file are < N never reserve extra space for >= N.

Can't be done by default, because counting lines in a very large
buffer will slow down redisplay and any command that uses display code
(see above).  It would also cause unpleasant redrawing and horizontal
movement of text when you add lines at the end of the buffer.

However, you can have this as an optional feature, if you customize
display-line-numbers-width-start to a non-nil value.

> 3. Take also into account the remaining window lines (I assume this is
> not as easy as it sounds).

Can't be done, because it would require a second redisplay cycle,
which will get us back to the same performance problem as we have in
linum.el.  I didn't write this feature just to have it work as slow as
linum.el, only in C ;-)

(Of course, if someone has clever ideas how to improve line-number
display, please speak up, but you should make yourself familiar with
the related code in the display engine first.)

I hope the above explains why we have the implementation we have.  All
in all, I find the price entirely reasonable, given the performance
gains (the default configuration works with line numbers almost as
fast as without them).  And we have a few customizable options to
optionally enable features which alleviate some of these issues, for
those who are willing to pay the price.

P.S.  Please make a point of keeping the full title of the bug in your
responses and followups, because removing the title and leaving just
the bug number makes it much harder to find all the messages that
pertain to the same bug, at least with Rmail.

Thanks.




Added tag(s) notabug. Request was from Eli Zaretskii <eliz <at> gnu.org> to control <at> debbugs.gnu.org. (Tue, 05 Jun 2018 14:23:01 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#31717; Package emacs. (Tue, 05 Jun 2018 15:21:01 GMT) Full text and rfc822 format available.

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

From: Carlos Pita <carlosjosepita <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 31717 <at> debbugs.gnu.org
Subject: Re: bug#31717: 26.1; display-line-numbers-mode enlarges indicator
 without need
Date: Tue, 5 Jun 2018 12:19:55 -0300
Thanks Eli for such an informative and detailed answer! It's worth
posting it somewhere more available, I will do something about that in
due time.

> If you don't want to "waste" one more column, you can use one of the
> optional features, e.g., set display-line-numbers-width-start non-nil,
> with or without display-line-numbers-grow-only non-nil.

I'm failing to see how this would prevent the "wasteful" behavior. Are
you just saying that if I reserve all the space in advance I won't be
seeing the widening happening since it have already happened up front?

I understand all your points above, so I'm going to ask for a more
modest alternative: the ability to suppress the left, right or both
whitespaces in the indicator. Do you think this is worth opening a new
issue or will it be rejected instantaneously/ignored for ever?

Again, thanks for taking the time to explain!




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#31717; Package emacs. (Tue, 05 Jun 2018 15:45:02 GMT) Full text and rfc822 format available.

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

From: Eli Zaretskii <eliz <at> gnu.org>
To: Carlos Pita <carlosjosepita <at> gmail.com>
Cc: 31717 <at> debbugs.gnu.org
Subject: Re: bug#31717: 26.1; display-line-numbers-mode enlarges indicator
 without need
Date: Tue, 05 Jun 2018 18:44:26 +0300
> From: Carlos Pita <carlosjosepita <at> gmail.com>
> Date: Tue, 5 Jun 2018 12:19:55 -0300
> Cc: 31717 <at> debbugs.gnu.org
> 
> > If you don't want to "waste" one more column, you can use one of the
> > optional features, e.g., set display-line-numbers-width-start non-nil,
> > with or without display-line-numbers-grow-only non-nil.
> 
> I'm failing to see how this would prevent the "wasteful" behavior. Are
> you just saying that if I reserve all the space in advance I won't be
> seeing the widening happening since it have already happened up front?

AFAIU, it will reserve exactly the space needed by the number of lines
in the file.

> I understand all your points above, so I'm going to ask for a more
> modest alternative: the ability to suppress the left, right or both
> whitespaces in the indicator.

This produces ugly display, so I don't think it's a good idea.  If you
want to reserve less space for the line numbers, just customize the
line-number face to use a smaller font.




Reply sent to Stefan Kangas <stefan <at> marxist.se>:
You have taken responsibility. (Thu, 03 Oct 2019 20:41:02 GMT) Full text and rfc822 format available.

Notification sent to Carlos Pita <carlosjosepita <at> gmail.com>:
bug acknowledged by developer. (Thu, 03 Oct 2019 20:41:02 GMT) Full text and rfc822 format available.

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

From: Stefan Kangas <stefan <at> marxist.se>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: Carlos Pita <carlosjosepita <at> gmail.com>, 31717-done <at> debbugs.gnu.org
Subject: Re: bug#31717: 26.1; display-line-numbers-mode enlarges indicator
 without need
Date: Thu, 3 Oct 2019 22:39:47 +0200
Eli Zaretskii <eliz <at> gnu.org> writes:

> tags 31717 notabug
> thanks
>
>> From: Carlos Pita <carlosjosepita <at> gmail.com>
>> Date: Tue, 5 Jun 2018 00:30:10 -0300
>
> (Sorry for a longish response, but you raise design and implementation
> issues which cannot be explained without some non-trivial background.)
>
>> When transitioning from line 69 to line 70 a new column is added to the
>> left of the indicator as if there was a need to fit three digits despite
>> that the file has less than 100 lines and despite that if it were larger
>> line 100 would fall out of the window.
>
> Right, this is the intended behavior, and there are good reasons for
> it.  See below.

This was tagged notabug since the observed behaviour was intentional.
I'm therefore also closing it now.

Best regards,
Stefan Kangas




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Fri, 01 Nov 2019 11:24:04 GMT) Full text and rfc822 format available.

This bug report was last modified 4 years and 175 days ago.

Previous Next


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