]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix vertical-motion across the place where line-number width changes
authorEli Zaretskii <eliz@gnu.org>
Fri, 7 Jul 2017 09:21:10 +0000 (12:21 +0300)
committerEli Zaretskii <eliz@gnu.org>
Fri, 7 Jul 2017 09:21:10 +0000 (12:21 +0300)
* src/indent.c (line_number_display_width): New function,
refactored from line-number width calculations in vertical-motion.
(Fvertical_motion): Call line_number_display_width when the width
of line-number display is needed.
(Fline_number_display_width): New defun.
(syms_of_indent): Defsubr it.

* doc/lispref/display.texi (Size of Displayed Text): Document
line-number-display-width.

* etc/NEWS: Mention line-number-display-width.

* lisp/simple.el (last--line-number-width): New internal variable.
(line-move-visual): Use it to adjust temporary-goal-column when
line-number display changes its width.

doc/lispref/display.texi
etc/NEWS
lisp/simple.el
src/indent.c

index 4de55fd3fb2b48e588466c1667f510c089a837c2..08b2b4671de9a8c0887872148465bae0e5b8c706 100644 (file)
@@ -1980,6 +1980,23 @@ selected window.  The value includes the line spacing of the line
 (@pxref{Line Height}).
 @end defun
 
+When a buffer is displayed with line numbers (@pxref{Display Custom,,,
+emacs, The GNU Emacs Manual}), it is sometimes useful to know the
+width taken for displaying the line numbers.  The following function
+is for Lisp programs which need this information for layout
+calculations.
+
+@defun line-number-display-width &optional pixelwise
+This function returns the width used for displaying the line numbers
+in the selected window.  Optional argument @var{pixelwise}, if
+non-@code{nil}, means return the value in pixels; otherwise the value
+is returned in column units of the font defined for the
+@code{line-number} face.  If line numbers are not displayed in the
+selected window, the value is zero.  Use @code{with-selected-window}
+(@pxref{Selecting Windows}) if you need this information about another
+window.
+@end defun
+
 
 @node Line Height
 @section Line Height
index b50c770c5050b98924788e08cf79b49fa8ea2a85..79eb3919e6d5959b6e76af0a8113adc8263ce0fb 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -420,6 +420,10 @@ line by putting the 'display-line-numbers-disable' text property or
 overlay property on the first character of that screen line.  This is
 intended for add-on packages that need a finer control of the display.
 
+Lisp programs that need to know how much screen estate is used up for
+line-number display in a window can use the new function
+'line-number-display-width'.
+
 Linum mode and all similar packages are henceforth becoming obsolete.
 Users and developers are encouraged to switch to this new feature
 instead.
index df664fc0503ad2df3e92ad01bfb547f9c5caaa78..5d1a6dbfb84c1b150d4c3c33ddb039ec65a11c21 100644 (file)
@@ -5931,6 +5931,10 @@ columns by which window is scrolled from left margin.
 When the `track-eol' feature is doing its job, the value is
 `most-positive-fixnum'.")
 
+(defvar last--line-number-width 0
+  "Last value of width used for displaying line numbers.
+Used internally by `line-move-visual'.")
+
 (defcustom line-move-ignore-invisible t
   "Non-nil means commands that move by lines ignore invisible newlines.
 When this option is non-nil, \\[next-line], \\[previous-line], \\[move-end-of-line], and \\[move-beginning-of-line] behave
@@ -6201,6 +6205,7 @@ not vscroll."
 If NOERROR, don't signal an error if we can't move that many lines."
   (let ((opoint (point))
        (hscroll (window-hscroll))
+        (lnum-width (line-number-display-width t))
        target-hscroll)
     ;; Check if the previous command was a line-motion command, or if
     ;; we were called from some other command.
@@ -6208,9 +6213,19 @@ If NOERROR, don't signal an error if we can't move that many lines."
             (memq last-command `(next-line previous-line ,this-command)))
        ;; If so, there's no need to reset `temporary-goal-column',
        ;; but we may need to hscroll.
-       (if (or (/= (cdr temporary-goal-column) hscroll)
-               (>  (cdr temporary-goal-column) 0))
-           (setq target-hscroll (cdr temporary-goal-column)))
+        (progn
+          (if (or (/= (cdr temporary-goal-column) hscroll)
+                  (>  (cdr temporary-goal-column) 0))
+              (setq target-hscroll (cdr temporary-goal-column)))
+          ;; Update the COLUMN part of temporary-goal-column if the
+          ;; line-number display changed its width since the last
+          ;; time.
+          (setq temporary-goal-column
+                (cons (+ (car temporary-goal-column)
+                         (/ (float (- lnum-width last--line-number-width))
+                            (frame-char-width)))
+                      (cdr temporary-goal-column)))
+          (setq last--line-number-width lnum-width))
       ;; Otherwise, we should reset `temporary-goal-column'.
       (let ((posn (posn-at-point))
            x-pos)
index 70351f90466a38469f99b1e32daa281df2829661..ba93650993442aa66b20aa15be2a3d5ea6446964 100644 (file)
@@ -1947,6 +1947,59 @@ vmotion (register ptrdiff_t from, register ptrdiff_t from_byte,
                         -1, hscroll, 0, w);
 }
 
+/* Return the width taken by line-number display in window W.  */
+static void
+line_number_display_width (struct window *w, int *width, int *pixel_width)
+{
+  if (NILP (Vdisplay_line_numbers))
+    {
+      *width = 0;
+      *pixel_width = 0;
+    }
+  else
+    {
+      struct it it;
+      struct text_pos wstart;
+      bool saved_restriction = false;
+      ptrdiff_t count = SPECPDL_INDEX ();
+      SET_TEXT_POS_FROM_MARKER (wstart, w->start);
+      void *itdata = bidi_shelve_cache ();
+      /* We must start from window's start point, but it could be
+        outside the accessible region.  */
+      if (wstart.charpos < BEGV || wstart.charpos > ZV)
+       {
+         record_unwind_protect (save_restriction_restore,
+                                save_restriction_save ());
+         Fwiden ();
+         saved_restriction = true;
+       }
+      start_display (&it, w, wstart);
+      move_it_by_lines (&it, 1);
+      *width = it.lnum_width;
+      *pixel_width = it.lnum_pixel_width;
+      if (saved_restriction)
+       unbind_to (count, Qnil);
+      bidi_unshelve_cache (itdata, 0);
+    }
+}
+
+DEFUN ("line-number-display-width", Fline_number_display_width,
+       Sline_number_display_width, 0, 1, 0,
+       doc: /* Return the width used for displaying line numbers in the selected window.
+If optional argument PIXELWISE is non-nil, return the width in pixels,
+otherwise return the width in columns of the face used to display
+line numbers, `line-number'.  */)
+  (Lisp_Object pixelwise)
+{
+  int width, pixel_width;
+  line_number_display_width (XWINDOW (selected_window), &width, &pixel_width);
+  if (!NILP (pixelwise))
+    return make_number (pixel_width);
+  /* FIXME: The "+ 2" part knows that we add a blank on each side of
+     the line number when producing glyphs for display.  */
+  return make_number (width + 2);
+}
+
 /* In window W (derived from WINDOW), return x coordinate for column
    COL (derived from COLUMN).  */
 static int
@@ -2073,30 +2126,10 @@ whether or not it is currently displayed in some window.  */)
         that's what normal window redisplay does.  Otherwise C-n/C-p
         will sometimes err by one column.  */
       int lnum_width = 0;
+      int lnum_pixel_width = 0;
       if (!NILP (Vdisplay_line_numbers)
          && !EQ (Vdisplay_line_numbers, Qvisual))
-       {
-         struct text_pos wstart;
-         bool saved_restriction = false;
-         ptrdiff_t count1 = SPECPDL_INDEX ();
-         SET_TEXT_POS_FROM_MARKER (wstart, w->start);
-         itdata = bidi_shelve_cache ();
-         /* We must start from window's start point, but it could be
-            outside the accessible region.  */
-         if (wstart.charpos < BEGV || wstart.charpos > ZV)
-           {
-             record_unwind_protect (save_restriction_restore,
-                                    save_restriction_save ());
-             Fwiden ();
-             saved_restriction = true;
-           }
-         start_display (&it, w, wstart);
-         move_it_by_lines (&it, 1);
-         lnum_width = it.lnum_width;
-         if (saved_restriction)
-           unbind_to (count1, Qnil);
-         bidi_unshelve_cache (itdata, 0);
-       }
+       line_number_display_width (w, &lnum_width, &lnum_pixel_width);
       SET_TEXT_POS (pt, PT, PT_BYTE);
       itdata = bidi_shelve_cache ();
       start_display (&it, w, pt);
@@ -2277,6 +2310,12 @@ whether or not it is currently displayed in some window.  */)
         an addition to the hscroll amount.  */
       if (lcols_given)
        {
+         /* If we are displaying line numbers, we could cross the
+            line where the width of the line-number display changes,
+            in which case we need to fix up the pixel coordinate
+            accordingly.  */
+         if (lnum_pixel_width > 0)
+           to_x += it.lnum_pixel_width - lnum_pixel_width;
          move_it_in_display_line (&it, ZV, first_x + to_x, MOVE_TO_X);
          /* If we find ourselves in the middle of an overlay string
             which includes a newline after current string position,
@@ -2322,6 +2361,7 @@ syms_of_indent (void)
   defsubr (&Sindent_to);
   defsubr (&Scurrent_column);
   defsubr (&Smove_to_column);
+  defsubr (&Sline_number_display_width);
   defsubr (&Svertical_motion);
   defsubr (&Scompute_motion);
 }