]> git.eshelyaron.com Git - emacs.git/commitdiff
Finish up native display of line numbers
authorEli Zaretskii <eliz@gnu.org>
Sat, 17 Jun 2017 14:42:44 +0000 (17:42 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 17 Jun 2017 14:42:44 +0000 (17:42 +0300)
* src/xdisp.c (maybe_produce_line_number): Produce a blank before
the number, for R2L rows.  Increment 'g' in the loop even if
glyph_row is NULL.  Accept 2nd argument FORCE and produce the
line-number glyphs if it is non-zero.
(move_it_in_display_line_to): Account for the space taken by the
line-number glyphs.  Call maybe_produce_line_number with 2nd
argument non-zero.
(set_cursor_from_row): Fix calculation of cursor X coordinate in
R2L rows with display-produced glyphs at the beginning.
(syms_of_xdisp) <line-number>: New face symbol.
<relative, display-line-width>: New symbols.
(maybe_produce_line_number): Use the line-number face for
displaying line numbers.  Support relative line-number display.
Support user-defined width for displaying line numbers.
(try_cursor_movement, try_window_id): Disable these optimizations
when displaying relative line numbers.
* src/dispextern.h (struct it): New member 'pt_lnum'.

* lisp/faces.el (line-number): New face.
* lisp/cus-start.el (standard): Provide customization forms for
display-line-numbers and display-line-width.
* lisp/menu-bar.el (menu-bar-showhide-menu): Add menu-bar item to
turn display-line-numbers on and off.

* etc/NEWS: Document the new feature.

etc/NEWS
lisp/cus-start.el
lisp/faces.el
lisp/menu-bar.el
src/dispextern.h
src/xdisp.c

index 2fb8daab1011ed64b4fce634c22b9a0fa2ac1b1b..856ebfe35e7de280df881bb3eb35241166eef527 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -377,6 +377,27 @@ display of raw bytes from octal to hex.
 ** You can now provide explicit field numbers in format specifiers.
 For example, '(format "%2$s %1$s" "X" "Y")' produces "Y X".
 
+** Emacs now supports optional display of line numbers in the buffer.
+This is similar to what linum-mode provides, but much faster and
+doesn't usurp the display margin for the line numbers.  Customize the
+buffer-local variable 'display-line-numbers' to activate this optional
+display.  If set to t, Emacs will display the number of each line
+before the line.  If set to 'relative', Emacs will display the line
+number relative to the line showing point.  The default is nil, which
+doesn't display the line numbers.
+
+You can also customize the new variable 'display-lines-width' to
+specify a fixed minimal with of the area allocated to line-number
+display.  The default is nil, meaning that Emacs will dynamically
+calculate the area width, enlarging it as needed.  Setting it to a
+non-negative integer specifies that as the minimal width; selecting a
+value that is large enough to display all line numbers in a buffer
+will keep the line-number display area of constant width.
+
+Linum mode and all similar packages are henceforth becoming obsolete.
+Users and developers are encouraged to switch to this new feature
+instead.
+
 \f
 * Editing Changes in Emacs 26.1
 
index 744fe7f69eec66cb391d788a9bfce139b4d1fb4b..0fe41d7c3ea053721f02cc732ed26b4314a53150 100644 (file)
@@ -584,6 +584,22 @@ since it could result in memory overflow and make Emacs crash."
                       (const :tag "Grow only" :value grow-only))
              "25.1")
             (display-raw-bytes-as-hex display boolean "26.1")
+             (display-line-numbers display
+                                   (choice
+                                    (const :tag "Off (nil)" :value nil)
+                                    (const :tag "Absolute line numbers"
+                                           :value t)
+                                    (const :tag "Relative line numbers"
+                                           :value relative))
+                                   "26.1")
+             (display-line-width display
+                                 (choice
+                                  (const :tag "Dynamically computed"
+                                         :value nil)
+                                  (integer :menu-tag "Fixed number of columns"
+                                           :value 2
+                                           :format "%v"))
+                                 "26.1")
             ;; xfaces.c
             (scalable-fonts-allowed display boolean "22.1")
             ;; xfns.c
index 9a8a1344cafaf2f93c69e30681cba81a0c29537e..ac2d210a32fd73202bfb4bb43cc53a515e5b81f1 100644 (file)
@@ -2465,6 +2465,14 @@ If you set `term-file-prefix' to nil, this function does nothing."
   :version "21.1"
   :group 'basic-faces)
 
+;; Definition stolen from linum.el.
+(defface line-number
+  '((t :inherit (shadow default)))
+  "Face for displaying line numbers.
+This face is used when `display-line-numbers' is non-nil."
+  :version "26.1"
+  :group 'basic-faces)
+
 (defface escape-glyph
   '((((background dark)) :foreground "cyan")
     ;; See the comment in minibuffer-prompt for
index 9c7bcffbaab87b6bfbe8305bf93d356ff235f286..06f8c7872b79083ba0645fe32700e12e9a82196d 100644 (file)
@@ -1101,17 +1101,32 @@ The selected font will be the default on both the existing and future frames."
                     :button (:radio . (eq tool-bar-mode nil))))
       menu)))
 
+(defun toggle-display-line-numbers ()
+  (interactive)
+  (if display-line-numbers
+      (setq display-line-numbers nil)
+    (setq display-line-numbers t))
+  (force-mode-line-update))
+
 (defvar menu-bar-showhide-menu
   (let ((menu (make-sparse-keymap "Show/Hide")))
 
+    (bindings--define-key menu [display-line-numbers]
+      `(menu-item "Line Numbers for all lines"
+                  ,(lambda ()
+                     (interactive)
+                     (toggle-display-line-numbers))
+                  :help "Show the line number alongside each line"
+                  :button (:toggle . display-line-numbers)))
+
     (bindings--define-key menu [column-number-mode]
       (menu-bar-make-mm-toggle column-number-mode
-                               "Column Numbers"
+                               "Column Numbers in Mode Line"
                                "Show the current column number in the mode line"))
 
     (bindings--define-key menu [line-number-mode]
       (menu-bar-make-mm-toggle line-number-mode
-                               "Line Numbers"
+                               "Line Numbers in Mode Line"
                                "Show the current line number in the mode line"))
 
     (bindings--define-key menu [size-indication-mode]
index 050c68b8e088838e535f5b646e66acdb8bb8d11b..08e5caa893bfba8b1a6aaacb8fc93493d0802b28 100644 (file)
@@ -2671,6 +2671,9 @@ struct it
      zero if not computed.  */
   int lnum_width;
 
+  /* The line number of point's line, or zero if not computed yet.  */
+  ptrdiff_t pt_lnum;
+
   /* Left fringe bitmap number (enum fringe_bitmap_type).  */
   unsigned left_user_fringe_bitmap : FRINGE_ID_BITS;
 
index dcef242966ee4fa127e01fa4509f69a66e09e55a..ebf5edc4d05eddfaf8efa69e2116d0c60401bec8 100644 (file)
@@ -833,6 +833,7 @@ static bool cursor_row_fully_visible_p (struct window *, bool, bool);
 static bool update_menu_bar (struct frame *, bool, bool);
 static bool try_window_reusing_current_matrix (struct window *);
 static int try_window_id (struct window *);
+static void maybe_produce_line_number (struct it *, bool);
 static bool display_line (struct it *, int);
 static int display_mode_lines (struct window *);
 static int display_mode_line (struct window *, enum face_id, Lisp_Object);
@@ -8652,9 +8653,16 @@ move_it_in_display_line_to (struct it *it,
        || (it->method == GET_FROM_DISPLAY_VECTOR               \
           && it->dpvec + it->current.dpvec_index + 1 >= it->dpend)))
 
-  /* If there's a line-/wrap-prefix, handle it.  */
-  if (it->hpos == 0 && it->method == GET_FROM_BUFFER)
-    handle_line_prefix (it);
+  if (it->hpos == 0)
+    {
+      /* If line numbers are being displayed, produce a line number.  */
+      if (!NILP (Vdisplay_line_numbers)
+         && it->current_x == it->first_visible_x)
+       maybe_produce_line_number (it, true);
+      /* If there's a line-/wrap-prefix, handle it.  */
+      if (it->method == GET_FROM_BUFFER)
+       handle_line_prefix (it);
+    }
 
   if (IT_CHARPOS (*it) < CHARPOS (this_line_min_pos))
     SET_TEXT_POS (this_line_min_pos, IT_CHARPOS (*it), IT_BYTEPOS (*it));
@@ -14787,15 +14795,12 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
          while (glyph > end + 1
                 && NILP (glyph->object)
                 && glyph->charpos < 0)
-           {
-             --glyph;
-             x -= glyph->pixel_width;
-           }
+           --glyph;
          if (NILP (glyph->object) && glyph->charpos < 0)
            --glyph;
          /* By default, in reversed rows we put the cursor on the
             rightmost (first in the reading order) glyph.  */
-         for (g = end + 1; g < glyph; g++)
+         for (x = 0, g = end + 1; g < glyph; g++)
            x += g->pixel_width;
          while (end < glyph
                 && NILP ((end + 1)->object)
@@ -15932,6 +15937,9 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
       && !windows_or_buffers_changed
       && !f->cursor_type_changed
       && NILP (Vshow_trailing_whitespace)
+      /* When display-line-numbers is in relative mode, moving point
+        requires to redraw the entire window.  */
+      && !EQ (Vdisplay_line_numbers, Qrelative)
       /* This code is not used for mini-buffer for the sake of the case
         of redisplaying to replace an echo area message; since in
         that case the mini-buffer contents per se are usually
@@ -18433,6 +18441,10 @@ try_window_id (struct window *w)
   if (!NILP (BVAR (XBUFFER (w->contents), extra_line_spacing)))
     GIVE_UP (23);
 
+  /* Give up if display-line-numbers is in relative mode.  */
+  if (EQ (Vdisplay_line_numbers, Qrelative))
+    GIVE_UP (24);
+
   /* Make sure beg_unchanged and end_unchanged are up to date.  Do it
      only if buffer has really changed.  The reason is that the gap is
      initially at Z for freshly visited files.  The code below would
@@ -20679,8 +20691,13 @@ find_row_edges (struct it *it, struct glyph_row *row,
     row->maxpos = it->current.pos;
 }
 
+/* Produce the line-number glyphs for the current glyph_row.  If
+   IT->glyph_row is non-NULL, populate the row with the produced
+   glyphs.  FORCE non-zero means produce the glyphs even if the line
+   number didn't change since the last time this function was called;
+   this is used by move_it_in_display_line_to.  */
 static void
-maybe_produce_line_number (struct it *it)
+maybe_produce_line_number (struct it *it, bool force)
 {
   ptrdiff_t last_line = it->lnum;
   ptrdiff_t start_from, bytepos;
@@ -20709,9 +20726,12 @@ maybe_produce_line_number (struct it *it)
   eassert (this_line > 0 || (this_line == 0 && start_from == BEGV_BYTE));
   eassert (bytepos == IT_BYTEPOS (*it));
 
-  /* If this is a new logical line, produce the glyphs for the line
-     number.  */
-  if (this_line != last_line || !last_line || it->continuation_lines_width > 0)
+  /* Produce the glyphs for the line number if needed.  */
+  if (force
+      || !last_line
+      || this_line != last_line
+      || it->continuation_lines_width > 0
+      || (EQ (Vdisplay_line_numbers, Qrelative) && PT != it->w->last_point))
     {
       if (this_line != last_line || !last_line)
        {
@@ -20723,19 +20743,51 @@ maybe_produce_line_number (struct it *it)
       struct it tem_it;
       char lnum_buf[INT_STRLEN_BOUND (ptrdiff_t) + 1];
       bool beyond_zv = IT_BYTEPOS (*it) >= ZV_BYTE ? true : false;
+      ptrdiff_t lnum_offset = -1; /* to produce 1-based line numbers */
+      /* Compute point's line number if needed.  */
+      if (EQ (Vdisplay_line_numbers, Qrelative) && !it->pt_lnum)
+       {
+         ptrdiff_t ignored;
+         if (PT_BYTE > it->lnum_bytepos)
+           it->pt_lnum =
+             this_line + display_count_lines (it->lnum_bytepos, PT_BYTE, PT,
+                                              &ignored);
+         else
+           it->pt_lnum = display_count_lines (BEGV_BYTE, PT_BYTE, PT,
+                                              &ignored);
+       }
       /* Compute the required width if needed.  */
       if (!it->lnum_width)
        {
-         /* Max line number to be displayed cannot be more than the
-            one corresponding to the last row of the desired
-            matrix.  */
-         ptrdiff_t max_lnum =
-           this_line + it->w->desired_matrix->nrows - 1 - it->vpos;
-         it->lnum_width = log10 (max_lnum) + 1;
+         if (NATNUMP (Vdisplay_line_width))
+           it->lnum_width = XFASTINT (Vdisplay_line_width);
+         else
+           {
+             /* Max line number to be displayed cannot be more than
+                the one corresponding to the last row of the desired
+                matrix.  */
+             ptrdiff_t max_lnum;
+
+             if (EQ (Vdisplay_line_numbers, Qrelative))
+               /* We subtract one more because the current line is
+                  always zero under relative line-number display.  */
+               max_lnum = it->w->desired_matrix->nrows - 2;
+             else
+               max_lnum =
+                 this_line + it->w->desired_matrix->nrows - 1 - it->vpos;
+             it->lnum_width = log10 (max_lnum) + 1;
+           }
          eassert (it->lnum_width > 0);
        }
-      pint2str (lnum_buf, it->lnum_width, this_line + 1);
-      /* Append a blank.  */
+      if (EQ (Vdisplay_line_numbers, Qrelative))
+       lnum_offset = it->pt_lnum;
+
+      /* In L2R rows we need to append the blank separator, in R2L
+        rows we need to prepend it.  But this function is usually
+        called when no display elements were produced from the
+        following line, so the paragraph direction might be unknown.
+        Therefore we cheat and add 2 blanks, one on either side.  */
+      pint2str (lnum_buf, it->lnum_width + 1, eabs (this_line - lnum_offset));
       strcat (lnum_buf, " ");
 
       /* Setup for producing the glyphs.  */
@@ -20745,12 +20797,12 @@ maybe_produce_line_number (struct it *it)
       scratch_glyph_row.reversed_p = false;
       scratch_glyph_row.used[TEXT_AREA] = 0;
       SET_TEXT_POS (tem_it.position, 0, 0);
+      tem_it.face_id = merge_faces (it->f, Qline_number, 0, DEFAULT_FACE_ID);
       tem_it.bidi_it.type = WEAK_EN;
       /* According to UAX#9, EN goes up 2 levels in L2R paragraph and
-        1 level in R2L paragraphs.  Emulate that.  */
+        1 level in R2L paragraphs.  Emulate that, assuming we are in
+        an L2R paragraph.  */
       tem_it.bidi_it.resolved_level = 2;
-      if (it->glyph_row && it->glyph_row->reversed_p)
-       tem_it.bidi_it.resolved_level = 1;
 
       /* Produce glyphs for the line number in a scratch glyph_row.  */
       int n_glyphs_before;
@@ -20784,13 +20836,17 @@ maybe_produce_line_number (struct it *it)
       struct glyph *p = it->glyph_row ? it->glyph_row->glyphs[TEXT_AREA] : NULL;
       short *u = it->glyph_row ? &it->glyph_row->used[TEXT_AREA] : NULL;
 
-      while (g < e)
+      for ( ; g < e; g++)
        {
          it->current_x += g->pixel_width;
-         it->hpos++;
+         /* The following is important when this function is called
+            from move_it_in_display_line_to: HPOS is incremented only
+            when we are in the visible portion of the glyph row.  */
+         if (it->current_x > it->first_visible_x)
+           it->hpos++;
          if (p)
            {
-             *p++ = *g++;
+             *p++ = *g;
              (*u)++;
            }
        }
@@ -20922,13 +20978,13 @@ display_line (struct it *it, int cursor_vpos)
 
       /* Produce line number, if needed.  */
       if (!NILP (Vdisplay_line_numbers))
-       maybe_produce_line_number (it);
+       maybe_produce_line_number (it, false);
     }
   else if (it->area == TEXT_AREA)
     {
       /* Line numbers should precede the line-prefix or wrap-prefix.  */
       if (!NILP (Vdisplay_line_numbers))
-       maybe_produce_line_number (it);
+       maybe_produce_line_number (it, false);
 
       /* We only do this when not calling move_it_in_display_line_to
         above, because that function calls itself handle_line_prefix.  */
@@ -21090,7 +21146,7 @@ display_line (struct it *it, int cursor_vpos)
            {
              /* Line numbers should precede the line-prefix or wrap-prefix.  */
              if (!NILP (Vdisplay_line_numbers))
-               maybe_produce_line_number (it);
+               maybe_produce_line_number (it, false);
 
              pending_handle_line_prefix = false;
              handle_line_prefix (it);
@@ -31778,6 +31834,9 @@ They are still logged to the *Messages* buffer.  */);
   /* Name of the face used to highlight trailing whitespace.  */
   DEFSYM (Qtrailing_whitespace, "trailing-whitespace");
 
+  /* Name of the face used to display line numbers.  */
+  DEFSYM (Qline_number, "line-number");
+
   /* Name and number of the face used to highlight escape glyphs.  */
   DEFSYM (Qescape_glyph, "escape-glyph");
 
@@ -32297,6 +32356,15 @@ after each newline that comes from buffer text.  */);
   Vdisplay_line_numbers = Qnil;
   DEFSYM (Qdisplay_line_numbers, "display-line-numbers");
   Fmake_variable_buffer_local (Qdisplay_line_numbers);
+  DEFSYM (Qrelative, "relative");
+
+  DEFVAR_LISP ("display-line-width", Vdisplay_line_width,
+    doc: /* Minimum width of space reserved for line number display.
+A positive number means reserve that many columns for line numbers,
+even if the actual number needs less space.
+The default value of nil means compute the space dynamically.
+Any other value is treated as nil.  */);
+  Vdisplay_line_width = Qnil;
 
   DEFVAR_BOOL ("inhibit-eval-during-redisplay", inhibit_eval_during_redisplay,
     doc: /* Non-nil means don't eval Lisp during redisplay.  */);