bug-gnu-emacs
[Top][All Lists]
Advanced

[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;
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]