[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#28246: display line number width != length of line number at eob
From: |
Keith David Bershatsky |
Subject: |
bug#28246: display line number width != length of line number at eob |
Date: |
Sun, 27 Aug 2017 18:46:42 -0700 |
Thank you, Eli, for reviewing #28246 and for explaining that automatic
adjustment of the width for line-number display is *estimated* based upon
certain criteria.
I do not know whether any other Emacs users would appreciate *precision* width
based upon the length of the last line in the buffer.
Inasmuch as I was hoping to achieve *precision* width every command loop,
display-line-numbers-width-start and/or display-line-numbers-grow-only would
not suffice in that regard. I am uncertain why it->lnum_width is *not* always
equal to
(save-excursion
(goto-char (point-max))
(length (format-mode-line "%l"))
The difference between it->lnum_width and the Lisp code above can be seen with
messages in the echo area after evaluating the code in the opening bug report
for #28246, and thereafter manually adding/removing lines from the buffer.
linum.el is slow in large buffers primarily because it uses count-lines. In an
emacs.stackexchange.com thread entitled "A faster method to obtain
`line-number-at-pos` in large buffers" --
https://emacs.stackexchange.com/q/3821/2287 -- I learned that format-mode-line
with the "%l" argument can be used to greatly enhance speed versus using
count-lines. However, the window must be visible and some other limitations
apply as discussed in the comments of the SE thread.
After reading your comments (a few times to let it all sink in), I understand
your concern about unnecessarily calculating the width each time that
maybe_produce_line_number is called. In the example that was used at the
outset of #28246, the width is calculated only once per command loop and stored
in display_line_numbers_width.
The following example function is a condensed version of what the mode-line
code uses to determine the line number, which will be substantially faster than
count-lines and/or line-number-at-pos. In addition, I found a little snippet
on stackoverflow.com to get the length of an integer. Inasmuch as
maybe_produce_line_number may be called more than one time each command loop, a
different location needs to be found for internal_line_number_at_position -- so
that it gets called just for each window per command loop -- and that value
would need to be stored and subsequently used. I understand that
internal_line_number_at_position may not be well written, but it is a working
proof concept to demonstrate where I am coming from.
/* Length of an Integer: https://stackoverflow.com/a/3068420/2112489
The log10, abs, and floor functions are provided by math.h */
TARGET_WIDTH = floor (log10 (abs (internal_line_number_at_position (XWINDOW
(selected_window), ZV)))) + 1;
static ptrdiff_t
internal_line_number_at_position (struct window *w, ptrdiff_t pos)
{
Lisp_Object buf, value;
struct buffer *b;
struct buffer *old_buffer = NULL;
buf = w->contents;
CHECK_BUFFER (buf);
b = XBUFFER (buf);
ptrdiff_t line_number;
if (b != current_buffer)
{
old_buffer = current_buffer;
set_buffer_internal (b);
}
ptrdiff_t startpos, startpos_byte, line, linepos, linepos_byte,
topline, nlines, height, junk, opoint;
opoint = PT;
if (opoint != pos)
SET_PT (pos);
startpos = marker_position (w->start);
startpos_byte = marker_byte_position (w->start);
/* If we decided that this buffer isn't suitable for line numbers, don't
forget that too fast. */
if (MINI_WINDOW_P (w))
{
line_number = 0;
goto done;
}
if (w->base_line_pos == -1)
{
line_number = 0;
goto done;
}
/* If the buffer is very big, don't waste time. */
if (INTEGERP (Vline_number_display_limit)
&& BUF_ZV (b) - BUF_BEGV (b) > XINT (Vline_number_display_limit))
{
w->base_line_pos = 0;
w->base_line_number = 0;
line_number = 0;
goto done;
}
if (w->base_line_number > 0
&& w->base_line_pos > 0
&& w->base_line_pos <= startpos)
{
line = w->base_line_number;
linepos = w->base_line_pos;
linepos_byte = buf_charpos_to_bytepos (b, linepos);
}
else
{
line = 1;
linepos = BUF_BEGV (b);
linepos_byte = BUF_BEGV_BYTE (b);
}
/* Count lines from base line to window start position. */
nlines = display_count_lines (linepos_byte, startpos_byte, startpos, &junk);
topline = nlines + line;
/* Determine a new base line, if the old one is too close
or too far away, or if we did not have one.
"Too close" means it's plausible a scroll-down would go back past it. */
if (startpos == BUF_BEGV (b))
{
w->base_line_number = topline;
w->base_line_pos = BUF_BEGV (b);
}
else if (nlines < height + 25 || nlines > height * 3 + 50 || linepos ==
BUF_BEGV (b))
{
ptrdiff_t limit = BUF_BEGV (b);
ptrdiff_t limit_byte = BUF_BEGV_BYTE (b);
ptrdiff_t position;
ptrdiff_t distance = (height * 2 + 30) *
line_number_display_limit_width;
if (startpos - distance > limit)
{
limit = startpos - distance;
limit_byte = CHAR_TO_BYTE (limit);
}
nlines = display_count_lines (startpos_byte, limit_byte, - (height * 2
+ 30), &position);
/* If we couldn't find the lines we wanted within
line_number_display_limit_width chars per line,
give up on line numbers for this window. */
if (position == limit_byte && limit == startpos - distance)
{
w->base_line_pos = -1;
w->base_line_number = 0;
line_number = 0;
goto done;
}
w->base_line_number = topline - nlines;
w->base_line_pos = BYTE_TO_CHAR (position);
}
/* Now count lines from the start pos to point. */
nlines = display_count_lines (startpos_byte, PT_BYTE, PT, &junk);
if (opoint != pos)
SET_PT (opoint);
line_number = topline + nlines;
done:
if (old_buffer)
set_buffer_internal (old_buffer);
return line_number;
}