From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Eli Zaretskii Newsgroups: gmane.emacs.bugs Subject: bug#31717: 26.1; display-line-numbers-mode enlarges indicator without need Date: Tue, 05 Jun 2018 17:22:50 +0300 Message-ID: <83fu21b9dx.fsf@gnu.org> References: <87vaaym04z.fsf@carlos.i-did-not-set--mail-host-address--so-tickle-me> Reply-To: Eli Zaretskii NNTP-Posting-Host: blaine.gmane.org X-Trace: blaine.gmane.org 1528208480 28548 195.159.176.226 (5 Jun 2018 14:21:20 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 5 Jun 2018 14:21:20 +0000 (UTC) Cc: 31717@debbugs.gnu.org To: Carlos Pita Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Tue Jun 05 16:21:15 2018 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1fQCpn-0007Iv-6v for geb-bug-gnu-emacs@m.gmane.org; Tue, 05 Jun 2018 16:21:15 +0200 Original-Received: from localhost ([::1]:47108 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fQCru-0008IB-9b for geb-bug-gnu-emacs@m.gmane.org; Tue, 05 Jun 2018 10:23:26 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:41583) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fQCrc-0008Du-9J for bug-gnu-emacs@gnu.org; Tue, 05 Jun 2018 10:23:12 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fQCrV-0005BD-RL for bug-gnu-emacs@gnu.org; Tue, 05 Jun 2018 10:23:08 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:55568) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fQCrV-0005Au-MG for bug-gnu-emacs@gnu.org; Tue, 05 Jun 2018 10:23:01 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1fQCrV-0005X2-HL for bug-gnu-emacs@gnu.org; Tue, 05 Jun 2018 10:23:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Tue, 05 Jun 2018 14:23:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 31717 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 31717-submit@debbugs.gnu.org id=B31717.152820858021257 (code B ref 31717); Tue, 05 Jun 2018 14:23:01 +0000 Original-Received: (at 31717) by debbugs.gnu.org; 5 Jun 2018 14:23:00 +0000 Original-Received: from localhost ([127.0.0.1]:35232 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1fQCrU-0005Wm-1k for submit@debbugs.gnu.org; Tue, 05 Jun 2018 10:23:00 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:50450) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1fQCrR-0005WZ-Rd for 31717@debbugs.gnu.org; Tue, 05 Jun 2018 10:22:58 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fQCrK-00054z-Lr for 31717@debbugs.gnu.org; Tue, 05 Jun 2018 10:22:52 -0400 Original-Received: from fencepost.gnu.org ([2001:4830:134:3::e]:44612) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fQCrA-00050P-OZ; Tue, 05 Jun 2018 10:22:40 -0400 Original-Received: from [176.228.60.248] (port=3814 helo=home-c4e4a596f7) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1fQCrA-0003QM-7B; Tue, 05 Jun 2018 10:22:40 -0400 In-reply-to: (message from Carlos Pita on Tue, 5 Jun 2018 00:30:10 -0300) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:147010 Archived-At: tags 31717 notabug thanks > From: Carlos Pita > 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.