diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi index 6b66c18016b..5878e7da256 100644 --- a/doc/emacs/basic.texi +++ b/doc/emacs/basic.texi @@ -630,7 +630,8 @@ Display the line number of point. @item M-x line-number-mode @itemx M-x column-number-mode Toggle automatic display of the current line number or column number. -@xref{Optional Mode Line}. +@xref{Optional Mode Line}. If you want to have a line number +displayed before each line, see @ref{Display Custom}. @item M-= Display the number of lines, words, and characters that are present in diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index c84f4a975d8..a756a89e3f8 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -1701,7 +1701,6 @@ and mouse events: (global-set-key (kbd "C-c y") 'clipboard-yank) (global-set-key (kbd "C-M-q") 'query-replace) (global-set-key (kbd "") 'flyspell-mode) -(global-set-key (kbd "C-") 'linum-mode) (global-set-key (kbd "C-") 'forward-sentence) (global-set-key (kbd "") 'mouse-save-then-kill) @end example diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index c4554eb3187..083fcdf97a6 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -1333,7 +1333,7 @@ characters in the buffer, which means that @samp{k} for 10^3, @samp{M} for 10^6, @samp{G} for 10^9, etc., are used to abbreviate. @cindex line number display -@cindex display of line number +@cindex display of current line number @findex line-number-mode The current line number of point appears in the mode line when Line Number mode is enabled. Use the command @kbd{M-x line-number-mode} to @@ -1710,6 +1710,66 @@ variable @code{visual-line-fringe-indicators}. This section describes variables that control miscellaneous aspects of the appearance of the Emacs screen. Beginning users can skip it. +@vindex display-line-numbers +@cindex number lines in a buffer +@cindex display line numbers + If you want to have Emacs display line numbers for every line in the +buffer, customize the buffer-local variable +@code{display-line-numbers}; it is @code{nil} by default. This +variable can have several different values to support various modes of +line-number display: + +@table @asis +@item @code{t} +Display (an absolute) line number before each non-continuation screen +line that displays buffer text. If the line is a continuation line, +or if the entire screen line displays a display or an overlay string, +that line will not be numbered. + +@item @code{relative} +Display relative line numbers before non-continuation lines which show +buffer text. The line numbers are relative to the line showing point, +so the numbers grow both up and down as lines become farther from the +current line. + +@item @code{visual} +This value causes Emacs to count lines visually: only lines actually +shown on the display will be counted (disregarding any lines in +invisible parts of text), and lines which wrap to consume more than +one screen line will be numbered that many times. The displayed +numbers are relative, as with @code{relative} value above. This is +handy in modes that fold text, such as Outline mode (@pxref{Outline +Mode}), and need to move by exact number of screen lines. + +@item anything else +Any other non-@code{nil} value is treated as @code{t}. +@end table + +@vindex display-line-numbers-current-absolute +When Emacs displays relative line numbers, you can control the number +displayed before the current line, the line showing point. By +default, Emacs displays the absolute number of the current line there, +even though all the other line numbers are relative. If you customize +the variable @code{display-line-numbers-current-absolute} to a +@code{nil} value, the number displayed for the current line will be +zero. This is handy if you don't care about the number of the current +line, and want to leave more horizontal space for text in large +buffers. + +@vindex display-line-numbers-widen +In a narrowed buffer (@pxref{Narrowing}) lines are normally numbered +starting at the beginning of the narrowing. However, if you customize +the variable @code{display-line-numbers-widen} to a non-@code{nil} +value, line numbers will disregard any narrowing and will start at the +first character of the buffer. + +@cindex line-number face +The line numbers are displayed in a special face @code{line-number}. +The current line number is displayed in a different face, +@code{line-number-current-line}, so you can make the current line's +number have a distinct appearance, which will help locating the line +showing point. + @vindex visible-bell If the variable @code{visible-bell} is non-@code{nil}, Emacs attempts to make the whole screen blink when it would normally make an audible bell diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi index be893403012..eb0c88b2901 100644 --- a/doc/emacs/modes.texi +++ b/doc/emacs/modes.texi @@ -225,11 +225,6 @@ Font-Lock mode automatically highlights certain textual units found in programs. It is enabled globally by default, but you can disable it in individual buffers. @xref{Faces}. -@findex linum-mode -@cindex Linum mode -@item -Linum mode displays each line's line number in the window's left margin. - @item Outline minor mode provides similar facilities to the major mode called Outline mode. @xref{Outline Mode}. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 2ebe872c362..005d31af05a 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2045,6 +2045,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 diff --git a/etc/NEWS b/etc/NEWS index 13805ce0da7..15c3009ae10 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -405,10 +405,60 @@ 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". -+++ ** 'comment-indent-function' values may now return a cons to specify a range of indentation. ++++ +** 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, with that line's number +displayed as absolute. If set to 'visual', Emacs will display a +relative number for every screen line, i.e. it will count screen lines +rather than buffer lines. The default is nil, which doesn't display +the line numbers. + +In 'relative' and 'visual' modes, the variable +'display-line-numbers-current-absolute' controls what number is +displayed for the line showing point. By default, this variable's +value is t, which means display the absolute line number for the line +showing point. Customizing this variable to a nil value will cause +Emacs to show zero instead, which preserves horizontal space of the +window in large buffers. + +Line numbers are not displayed at all in minibuffer windows and in +tooltips, as they are not useful there. + +The new face 'line-number' is used to display the line numbers. The +new face 'line-number-current-line' can be customized to display the +current line's number differently from all the other line numbers; by +default these two faces are identical. + +You can also customize the new variable 'display-line-number-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 or shrinking 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 then keep the line-number display area of +constant width at all times, if that is desired. + +Lisp programs can disable line-number display for a particular screen +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. + * Editing Changes in Emacs 26.1 diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 744fe7f69ee..017e7f9fa55 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -584,6 +584,38 @@ 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) + (const :tag "Visually relative line numbers" + :value visual)) + "26.1") + (display-line-number-width display + (choice + (const :tag "Dynamically computed" + :value nil) + (integer :menu-tag "Fixed number of columns" + :value 2 + :format "%v")) + "26.1") + (display-line-numbers-current-absolute display + (choice + (const :tag "Display actual number of current line" + :value t) + (const :tag "Display zero as number of current line" + :value nil)) + "26.1") + (display-line-numbers-widen display + (choice + (const :tag "Disregard narrowing when calculating line numbers" + :value t) + (const :tag "Count lines from beinning of narrowed region" + :value nil)) + "26.1") ;; xfaces.c (scalable-fonts-allowed display boolean "22.1") ;; xfns.c diff --git a/lisp/faces.el b/lisp/faces.el index 9a8a1344caf..c3693d16631 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -2465,6 +2465,33 @@ 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. + +If you customize the font of this face, make sure it is a +monospaced font, otherwise line numbers will not line up, +and text lines might move horizontally as you move through +the buffer." + :version "26.1" + :group 'basic-faces) + +(defface line-number-current-line + '((t :inherit line-number)) + "Face for displaying the current line number. +This face is used when `display-line-numbers' is non-nil. + +If you customize the font of this face, make sure it is a +monospaced font, otherwise line numbers will not line up, +and text lines might move horizontally as you move through +the buffer. Similarly, making this face's font different +from that of the `line-number' face could produce such +unwanted effects." + :version "26.1" + :group 'basic-faces) + (defface escape-glyph '((((background dark)) :foreground "cyan") ;; See the comment in minibuffer-prompt for diff --git a/lisp/frame.el b/lisp/frame.el index b54df6fa160..c629a9497c0 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -2466,6 +2466,10 @@ See also `toggle-frame-maximized'." line-prefix wrap-prefix truncate-lines + display-line-numbers + display-line-number-width + display-line-numbers-current-absolute + display-line-numbers-widen bidi-paragraph-direction bidi-display-reordering)) diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 9c7bcffbaab..06f8c7872b7 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -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] diff --git a/lisp/simple.el b/lisp/simple.el index 1db14a859d6..3d23fc35596 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -5942,6 +5942,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 @@ -6212,6 +6216,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. @@ -6219,9 +6224,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) diff --git a/src/buffer.c b/src/buffer.c index 80dbd3318dc..780e4d7a7d6 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3054,6 +3054,33 @@ mouse_face_overlay_overlaps (Lisp_Object overlay) return i < n; } +/* Return the value of the 'display-line-numbers-disable' property at + EOB, if there's an overlay at ZV with a non-nil value of that property. */ +Lisp_Object +disable_line_numbers_overlay_at_eob (void) +{ + ptrdiff_t n, i, size; + Lisp_Object *v, tem = Qnil; + Lisp_Object vbuf[10]; + USE_SAFE_ALLOCA; + + size = ARRAYELTS (vbuf); + v = vbuf; + n = overlays_in (ZV, ZV, 0, &v, &size, NULL, NULL); + if (n > size) + { + SAFE_NALLOCA (v, 1, n); + overlays_in (ZV, ZV, 0, &v, &n, NULL, NULL); + } + + for (i = 0; i < n; ++i) + if ((tem = Foverlay_get (v[i], Qdisplay_line_numbers_disable), + !NILP (tem))) + break; + + SAFE_FREE (); + return tem; +} /* Fast function to just test if we're at an overlay boundary. */ diff --git a/src/dispextern.h b/src/dispextern.h index 8644ce26d13..1df769a8f99 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -384,6 +384,7 @@ struct glyph glyph standing for newline at end of line 0 empty space after the end of the line -1 overlay arrow on a TTY -1 + glyph displaying line number -1 glyph at EOB that ends in a newline -1 left truncation glyphs: -1 right truncation/continuation glyphs next buffer position @@ -2537,7 +2538,12 @@ struct it Do NOT use !BUFFERP (it.object) as a test whether we are iterating over a string; use STRINGP (it.string) instead. - Position is the current iterator position in object. */ + Position is the current iterator position in object. + + The 'position's CHARPOS is copied to glyph->charpos of the glyph + produced by PRODUCE_GLYPHS, so any artificial value documented + under 'struct glyph's 'charpos' member can also be found in the + 'position' member here. */ Lisp_Object object; struct text_pos position; @@ -2621,6 +2627,20 @@ struct it coordinate is past first_visible_x. */ int hpos; + /* Current line number, zero-based. */ + ptrdiff_t lnum; + + /* The byte position corresponding to lnum. */ + ptrdiff_t lnum_bytepos; + + /* The width, in columns and in pixels, needed for display of the + line numbers, or zero if not computed. */ + int lnum_width; + int lnum_pixel_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; diff --git a/src/indent.c b/src/indent.c index adecc3622a8..4c6dacd2042 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1947,6 +1947,57 @@ 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); + return make_number (width); +} + /* In window W (derived from WINDOW), return x coordinate for column COL (derived from COLUMN). */ static int @@ -2068,9 +2119,19 @@ whether or not it is currently displayed in some window. */) start_x = window_column_x (w, window, start_col, cur_col); } - itdata = bidi_shelve_cache (); + /* When displaying line numbers, we need to prime IT's + lnum_width with the value calculated at window's start, since + 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)) + 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); + it.lnum_width = lnum_width; first_x = it.first_visible_x; it_start = IT_CHARPOS (it); @@ -2247,6 +2308,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, @@ -2292,6 +2359,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); } diff --git a/src/lisp.h b/src/lisp.h index ff8dde2b825..f5cb6c75706 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3965,6 +3965,7 @@ extern void syms_of_editfns (void); /* Defined in buffer.c. */ extern bool mouse_face_overlay_overlaps (Lisp_Object); +extern Lisp_Object disable_line_numbers_overlay_at_eob (void); extern _Noreturn void nsberror (Lisp_Object); extern void adjust_overlays_for_insert (ptrdiff_t, ptrdiff_t); extern void adjust_overlays_for_delete (ptrdiff_t, ptrdiff_t); diff --git a/src/term.c b/src/term.c index 3d7f4ada0b9..87a412666d0 100644 --- a/src/term.c +++ b/src/term.c @@ -1585,10 +1585,16 @@ produce_glyphs (struct it *it) { int absolute_x = (it->current_x + it->continuation_lines_width); + int x0 = absolute_x; + /* Adjust for line numbers. */ + if (!NILP (Vdisplay_line_numbers)) + absolute_x -= it->lnum_pixel_width; int next_tab_x = (((1 + absolute_x + it->tab_width - 1) / it->tab_width) * it->tab_width); + if (!NILP (Vdisplay_line_numbers)) + next_tab_x += it->lnum_pixel_width; int nspaces; /* If part of the TAB has been displayed on the previous line @@ -1596,7 +1602,7 @@ produce_glyphs (struct it *it) been incremented already by the part that fitted on the continued line. So, we will get the right number of spaces here. */ - nspaces = next_tab_x - absolute_x; + nspaces = next_tab_x - x0; if (it->glyph_row) { diff --git a/src/xdisp.c b/src/xdisp.c index 1c316fa4932..fad23bfdc9d 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -290,6 +290,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include "lisp.h" #include "atimer.h" @@ -324,7 +325,7 @@ along with GNU Emacs. If not, see . */ #define FRAME_X_OUTPUT(f) ((f)->output_data.x) #endif -#define INFINITY 10000000 +#define DISP_INFINITY 10000000 /* Holds the list (error). */ static Lisp_Object list_of_error; @@ -832,6 +833,8 @@ 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 *); +static bool should_produce_line_number (struct it *); 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); @@ -843,6 +846,8 @@ static const char *decode_mode_spec (struct window *, int, int, Lisp_Object *); static void display_menu_bar (struct window *); static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t *); +static void pint2str (register char *, register int, register ptrdiff_t); + static int display_string (const char *, Lisp_Object, Lisp_Object, ptrdiff_t, ptrdiff_t, struct it *, int, int, int, int); static void compute_line_metrics (struct it *); @@ -6764,7 +6769,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string, FIELD_WIDTH < 0 means infinite field width. This is useful for padding with `-' at the end of a mode line. */ if (field_width < 0) - field_width = INFINITY; + field_width = DISP_INFINITY; /* Implementation note: We deliberately don't enlarge it->bidi_it.string.schars here to fit it->end_charpos, because the bidi iterator cannot produce characters out of thin air. */ @@ -8661,9 +8666,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 (should_produce_line_number (it) + && it->current_x == it->first_visible_x) + maybe_produce_line_number (it); + /* 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)); @@ -13069,6 +13081,43 @@ hscroll_window_tree (Lisp_Object window) } bool row_r2l_p = cursor_row->reversed_p; bool hscl = hscrolling_current_line_p (w); + int x_offset = 0; + /* When line numbers are displayed, we need to account for + the horizontal space they consume. */ + if (!NILP (Vdisplay_line_numbers)) + { + struct glyph *g; + if (!row_r2l_p) + { + for (g = cursor_row->glyphs[TEXT_AREA]; + g < cursor_row->glyphs[TEXT_AREA] + + cursor_row->used[TEXT_AREA]; + g++) + { + if (!(NILP (g->object) && g->charpos < 0)) + break; + x_offset += g->pixel_width; + } + } + else + { + for (g = cursor_row->glyphs[TEXT_AREA] + + cursor_row->used[TEXT_AREA]; + g > cursor_row->glyphs[TEXT_AREA]; + g--) + { + if (!(NILP ((g - 1)->object) && (g - 1)->charpos < 0)) + break; + x_offset += (g - 1)->pixel_width; + } + } + } + if (cursor_row->truncated_on_left_p) + { + /* On TTY frames, don't count the left truncation glyph. */ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + x_offset -= (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)); + } text_area_width = window_box_width (w, TEXT_AREA); @@ -13101,7 +13150,7 @@ hscroll_window_tree (Lisp_Object window) inside the left margin and the window is already hscrolled. */ && ((!row_r2l_p - && ((w->hscroll && w->cursor.x <= h_margin) + && ((w->hscroll && w->cursor.x <= h_margin + x_offset) || (cursor_row->enabled_p && cursor_row->truncated_on_right_p && (w->cursor.x >= text_area_width - h_margin)))) @@ -13119,7 +13168,8 @@ hscroll_window_tree (Lisp_Object window) && cursor_row->truncated_on_right_p && w->cursor.x <= h_margin) || (w->hscroll - && (w->cursor.x >= text_area_width - h_margin)))) + && (w->cursor.x >= (text_area_width - h_margin + - x_offset))))) /* This last condition is needed when moving vertically from an hscrolled line to a short line that doesn't need to be hscrolled. If we omit @@ -13150,7 +13200,7 @@ hscroll_window_tree (Lisp_Object window) if (hscl) it.first_visible_x = window_hscroll_limited (w, it.f) * FRAME_COLUMN_WIDTH (it.f); - it.last_visible_x = INFINITY; + it.last_visible_x = DISP_INFINITY; move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS); /* If the line ends in an overlay string with a newline, we might infloop, because displaying the window will @@ -14796,15 +14846,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) @@ -15835,7 +15882,7 @@ compute_window_start_on_continuation_line (struct window *w) So, we're looking for the display line start with the minimum distance from the old window start. */ pos_before_pt = pos = it.current.pos; - min_distance = INFINITY; + min_distance = DISP_INFINITY; while ((distance = eabs (CHARPOS (start_pos) - IT_CHARPOS (it))), distance < min_distance) { @@ -15941,6 +15988,17 @@ 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) + && !EQ (Vdisplay_line_numbers, Qvisual) + /* When the current line number should be displayed in a + distinct face, moving point cannot be handled in optimized + way as below. */ + && !(!NILP (Vdisplay_line_numbers) + && NILP (Finternal_lisp_face_equal_p (Qline_number, + Qline_number_current_line, + w->frame))) /* 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 @@ -16788,10 +16846,15 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) XBUFFER (w->contents)->text->redisplay = false; safe__call1 (true, Vpre_redisplay_function, Fcons (window, Qnil)); - if (w->redisplay || XBUFFER (w->contents)->text->redisplay) + if (w->redisplay || XBUFFER (w->contents)->text->redisplay + || ((EQ (Vdisplay_line_numbers, Qrelative) + || EQ (Vdisplay_line_numbers, Qvisual)) + && row != MATRIX_FIRST_TEXT_ROW (w->desired_matrix))) { - /* pre-redisplay-function made changes (e.g. move the region) - that require another round of redisplay. */ + /* Either pre-redisplay-function made changes (e.g. move + the region), or we moved point in a window that is + under display-line-numbers = relative mode. We need + another round of redisplay. */ clear_glyph_matrix (w->desired_matrix); if (!try_window (window, startp, 0)) goto need_larger_matrices; @@ -17592,6 +17655,12 @@ try_window_reusing_current_matrix (struct window *w) if (w->vscroll || MATRIX_ROW_PARTIALLY_VISIBLE_P (w, start_row)) return false; + /* Give up if line numbers are being displayed, because reusing the + current matrix might use the wrong width for line-number + display. */ + if (!NILP (Vdisplay_line_numbers)) + return false; + /* The variable new_start now holds the new window start. The old start `start' can be determined from the current matrix. */ SET_TEXT_POS_FROM_MARKER (new_start, w->start); @@ -18423,6 +18492,16 @@ 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, or when the + current line's number needs to be displayed in a distinct face. */ + if (EQ (Vdisplay_line_numbers, Qrelative) + || EQ (Vdisplay_line_numbers, Qvisual) + || (!NILP (Vdisplay_line_numbers) + && NILP (Finternal_lisp_face_equal_p (Qline_number, + Qline_number_current_line, + w->frame)))) + 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 @@ -20669,6 +20748,352 @@ find_row_edges (struct it *it, struct glyph_row *row, row->maxpos = it->current.pos; } +/* Like display_count_lines, but capable of counting outside of the + current narrowed region. */ +static ptrdiff_t +display_count_lines_logically (ptrdiff_t start_byte, ptrdiff_t limit_byte, + ptrdiff_t count, ptrdiff_t *byte_pos_ptr) +{ + if (!display_line_numbers_widen || (BEGV == BEG && ZV == Z)) + return display_count_lines (start_byte, limit_byte, count, byte_pos_ptr); + + ptrdiff_t val; + ptrdiff_t pdl_count = SPECPDL_INDEX (); + record_unwind_protect (save_restriction_restore, save_restriction_save ()); + Fwiden (); + val = display_count_lines (start_byte, limit_byte, count, byte_pos_ptr); + unbind_to (pdl_count, Qnil); + return val; +} + +/* Count the number of screen lines in window IT->w between character + position IT_CHARPOS(*IT) and the line showing that window's point. */ +static ptrdiff_t +display_count_lines_visually (struct it *it) +{ + struct it tem_it; + ptrdiff_t to; + struct text_pos from; + + /* If we already calculated a relative line number, use that. This + trick relies on the fact that visual lines (a.k.a. "glyph rows") + are laid out sequentially, one by one, for each sequence of calls + to display_line or other similar function that follows a call to + init_iterator. */ + if (it->lnum_bytepos > 0) + return it->lnum + 1; + else + { + ptrdiff_t count = SPECPDL_INDEX (); + + if (IT_CHARPOS (*it) <= PT) + { + from = it->current.pos; + to = PT; + } + else + { + SET_TEXT_POS (from, PT, PT_BYTE); + to = IT_CHARPOS (*it); + } + start_display (&tem_it, it->w, from); + /* Need to disable visual mode temporarily, since otherwise the + call to move_it_to will cause infinite recursion. */ + specbind (Qdisplay_line_numbers, Qrelative); + /* Some redisplay optimizations could invoke us very far from + PT, which will make the caller painfully slow. There should + be no need to go too far beyond the window's bottom, as any + such optimization will fail to show point anyway. */ + move_it_to (&tem_it, to, -1, + tem_it.last_visible_y + + (SCROLL_LIMIT + 10) * FRAME_LINE_HEIGHT (tem_it.f), + -1, MOVE_TO_POS | MOVE_TO_Y); + unbind_to (count, Qnil); + return IT_CHARPOS (*it) <= PT ? -tem_it.vpos : tem_it.vpos; + } +} + +/* Produce the line-number glyphs for the current glyph_row. If + IT->glyph_row is non-NULL, populate the row with the produced + glyphs. */ +static void +maybe_produce_line_number (struct it *it) +{ + ptrdiff_t last_line = it->lnum; + ptrdiff_t start_from, bytepos; + ptrdiff_t this_line; + bool first_time = false; + ptrdiff_t beg = display_line_numbers_widen ? BEG : BEGV; + ptrdiff_t beg_byte = display_line_numbers_widen ? BEG_BYTE : BEGV_BYTE; + ptrdiff_t z_byte = display_line_numbers_widen ? Z_BYTE : ZV_BYTE; + void *itdata = bidi_shelve_cache (); + + if (EQ (Vdisplay_line_numbers, Qvisual)) + this_line = display_count_lines_visually (it); + else + { + if (!last_line) + { + /* FIXME: Maybe reuse the data in it->w->base_line_number. */ + start_from = beg; + if (!it->lnum_bytepos) + first_time = true; + } + else + start_from = it->lnum_bytepos; + + /* Paranoia: what if someone changes the narrowing since the + last time display_line was called? Shouldn't really happen, + but who knows what some crazy Lisp invoked by :eval could do? */ + if (!(beg_byte <= start_from && start_from < z_byte)) + { + last_line = 0; + start_from = beg_byte; + } + + this_line = + last_line + display_count_lines_logically (start_from, + IT_BYTEPOS (*it), + IT_CHARPOS (*it), &bytepos); + eassert (this_line > 0 || (this_line == 0 && start_from == beg_byte)); + eassert (bytepos == IT_BYTEPOS (*it)); + } + + /* Record the line number information. */ + if (this_line != last_line || !it->lnum_bytepos) + { + it->lnum = this_line; + it->lnum_bytepos = IT_BYTEPOS (*it); + } + + /* Produce the glyphs for the line number. */ + 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 */ + int lnum_face_id = merge_faces (it->f, Qline_number, 0, DEFAULT_FACE_ID); + int current_lnum_face_id + = merge_faces (it->f, Qline_number_current_line, 0, DEFAULT_FACE_ID); + /* Compute point's line number if needed. */ + if ((EQ (Vdisplay_line_numbers, Qrelative) + || EQ (Vdisplay_line_numbers, Qvisual) + || lnum_face_id != current_lnum_face_id) + && !it->pt_lnum) + { + ptrdiff_t ignored; + if (PT_BYTE > it->lnum_bytepos && !EQ (Vdisplay_line_numbers, Qvisual)) + it->pt_lnum = + this_line + display_count_lines_logically (it->lnum_bytepos, PT_BYTE, + PT, &ignored); + else + it->pt_lnum = display_count_lines_logically (beg_byte, PT_BYTE, PT, + &ignored); + } + /* Compute the required width if needed. */ + if (!it->lnum_width) + { + if (NATNUMP (Vdisplay_line_number_width)) + it->lnum_width = XFASTINT (Vdisplay_line_number_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; + + if (NILP (Vdisplay_line_numbers_current_absolute) + && (EQ (Vdisplay_line_numbers, Qrelative) + || EQ (Vdisplay_line_numbers, Qvisual))) + /* We subtract one more because the current line is always + zero in this mode. */ + max_lnum = it->w->desired_matrix->nrows - 2; + else if (EQ (Vdisplay_line_numbers, Qvisual)) + max_lnum = it->pt_lnum + it->w->desired_matrix->nrows - 1; + else + max_lnum = this_line + it->w->desired_matrix->nrows - 1 - it->vpos; + max_lnum = max (1, max_lnum); + it->lnum_width = max (it->lnum_width, log10 (max_lnum) + 1); + eassert (it->lnum_width > 0); + } + if (EQ (Vdisplay_line_numbers, Qrelative)) + lnum_offset = it->pt_lnum; + else if (EQ (Vdisplay_line_numbers, Qvisual)) + lnum_offset = 0; + + /* Under 'relative', display the absolute line number for the + current line, unless the user requests otherwise. */ + ptrdiff_t lnum_to_display = eabs (this_line - lnum_offset); + if ((EQ (Vdisplay_line_numbers, Qrelative) + || EQ (Vdisplay_line_numbers, Qvisual)) + && lnum_to_display == 0 + && !NILP (Vdisplay_line_numbers_current_absolute)) + lnum_to_display = it->pt_lnum + 1; + /* 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, lnum_to_display); + strcat (lnum_buf, " "); + + /* Setup for producing the glyphs. */ + init_iterator (&tem_it, it->w, -1, -1, &scratch_glyph_row, + /* FIXME: Use specialized face. */ + DEFAULT_FACE_ID); + scratch_glyph_row.reversed_p = false; + scratch_glyph_row.used[TEXT_AREA] = 0; + SET_TEXT_POS (tem_it.position, 0, 0); + tem_it.avoid_cursor_p = true; + tem_it.bidi_p = true; + 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, assuming we are in + an L2R paragraph. */ + tem_it.bidi_it.resolved_level = 2; + + /* Produce glyphs for the line number in a scratch glyph_row. */ + int n_glyphs_before; + for (const char *p = lnum_buf; *p; p++) + { + /* For continuation lines and lines after ZV, instead of a line + number, produce a blank prefix of the same width. Use the + default face for the blank field beyond ZV. */ + if (beyond_zv) + tem_it.face_id = it->base_face_id; + else if (lnum_face_id != current_lnum_face_id + && (EQ (Vdisplay_line_numbers, Qvisual) + ? this_line == 0 + : this_line == it->pt_lnum)) + tem_it.face_id = current_lnum_face_id; + else + tem_it.face_id = lnum_face_id; + if (beyond_zv + /* Don't display the same line number more than once. */ + || (!EQ (Vdisplay_line_numbers, Qvisual) + && (it->continuation_lines_width > 0 + || (this_line == last_line && !first_time)))) + tem_it.c = tem_it.char_to_display = ' '; + else + tem_it.c = tem_it.char_to_display = *p; + tem_it.len = 1; + n_glyphs_before = scratch_glyph_row.used[TEXT_AREA]; + /* Make sure these glyphs will have a "position" of -1. */ + SET_TEXT_POS (tem_it.position, -1, -1); + PRODUCE_GLYPHS (&tem_it); + + /* Stop producing glyphs if we don't have enough space on + this line. FIXME: should we refrain from producing the + line number at all in that case? */ + if (tem_it.current_x > tem_it.last_visible_x) + { + scratch_glyph_row.used[TEXT_AREA] = n_glyphs_before; + break; + } + } + + /* Record the width in pixels we need for the line number display. */ + it->lnum_pixel_width = tem_it.current_x; + /* Copy the produced glyphs into IT's glyph_row. */ + struct glyph *g = scratch_glyph_row.glyphs[TEXT_AREA]; + struct glyph *e = g + scratch_glyph_row.used[TEXT_AREA]; + 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; + + for ( ; g < e; g++) + { + it->current_x += g->pixel_width; + /* 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; + (*u)++; + } + } + + /* Update IT's metrics due to glyphs produced for line numbers. */ + if (it->glyph_row) + { + struct glyph_row *row = it->glyph_row; + + it->max_ascent = max (row->ascent, tem_it.max_ascent); + it->max_descent = max (row->height - row->ascent, tem_it.max_descent); + it->max_phys_ascent = max (row->phys_ascent, tem_it.max_phys_ascent); + it->max_phys_descent = max (row->phys_height - row->phys_ascent, + tem_it.max_phys_descent); + } + else + { + it->max_ascent = max (it->max_ascent, tem_it.max_ascent); + it->max_descent = max (it->max_descent, tem_it.max_descent); + it->max_phys_ascent = max (it->max_phys_ascent, tem_it.max_phys_ascent); + it->max_phys_descent = max (it->max_phys_descent, tem_it.max_phys_descent); + } + + bidi_unshelve_cache (itdata, false); +} + +/* Return true if this glyph row needs a line number to be produced + for it. */ +static bool +should_produce_line_number (struct it *it) +{ + if (NILP (Vdisplay_line_numbers)) + return false; + + /* Don't display line numbers in minibuffer windows. */ + if (MINI_WINDOW_P (it->w)) + return false; + +#ifdef HAVE_WINDOW_SYSTEM + /* Don't display line number in tooltip frames. */ + if (FRAMEP (tip_frame) && EQ (WINDOW_FRAME (it->w), tip_frame)) + return false; +#endif + + /* If the character at current position has a non-nil special + property, disable line numbers for this row. This is for + packages such as company-mode, which need this for their tricky + layout, where line numbers get in the way. */ + Lisp_Object val = Fget_char_property (make_number (IT_CHARPOS (*it)), + Qdisplay_line_numbers_disable, + it->window); + /* For ZV, we need to also look in empty overlays at that point, + because get-char-property always returns nil for ZV, except if + the property is in 'default-text-properties'. */ + if (NILP (val) && IT_CHARPOS (*it) >= ZV) + val = disable_line_numbers_overlay_at_eob (); + return NILP (val) ? true : false; +} + +/* Return true if ROW has no glyphs except those inserted by the + display engine. This is needed for indicate-empty-lines and + similar features when the glyph row starts with glyphs which didn't + come from buffer or string. */ +static bool +row_text_area_empty (struct glyph_row *row) +{ + if (!row->reversed_p) + { + for (struct glyph *g = row->glyphs[TEXT_AREA]; + g < row->glyphs[TEXT_AREA] + row->used[TEXT_AREA]; + g++) + if (!NILP (g->object) || g->charpos > 0) + return false; + } + else + { + for (struct glyph *g = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA] - 1; + g > row->glyphs[TEXT_AREA]; + g--) + if (!NILP ((g - 1)->object) || (g - 1)->charpos > 0) + return false; + } + + return true; +} + /* Construct the glyph row IT->glyph_row in the desired matrix of IT->w from text at the current position of IT. See dispextern.h for an overview of struct it. Value is true if @@ -20739,6 +21164,8 @@ display_line (struct it *it, int cursor_vpos) (window_hscroll_limited (it->w, it->f) - it->w->min_hscroll) * FRAME_COLUMN_WIDTH (it->f); + bool line_number_needed = should_produce_line_number (it); + /* Move over display elements that are not visible because we are hscrolled. This may stop at an x-position < first_visible_x if the first glyph is partially visible or if we hit a line end. */ @@ -20774,9 +21201,17 @@ display_line (struct it *it, int cursor_vpos) are hscrolled to the left of the left edge of the window. */ min_pos = CHARPOS (this_line_min_pos); min_bpos = BYTEPOS (this_line_min_pos); + + /* Produce line number, if needed. */ + if (line_number_needed) + maybe_produce_line_number (it); } else if (it->area == TEXT_AREA) { + /* Line numbers should precede the line-prefix or wrap-prefix. */ + if (line_number_needed) + maybe_produce_line_number (it); + /* We only do this when not calling move_it_in_display_line_to above, because that function calls itself handle_line_prefix. */ handle_line_prefix (it); @@ -20838,6 +21273,7 @@ display_line (struct it *it, int cursor_vpos) buffer reached. */ if (!get_next_display_element (it)) { + bool row_has_glyphs = false; /* Maybe add a space at the end of this line that is used to display the cursor there under X. Set the charpos of the first glyph of blank lines not corresponding to any text @@ -20846,14 +21282,17 @@ display_line (struct it *it, int cursor_vpos) row->exact_window_width_line_p = true; else if ((append_space_for_newline (it, true) && row->used[TEXT_AREA] == 1) - || row->used[TEXT_AREA] == 0) + || row->used[TEXT_AREA] == 0 + || (row_has_glyphs = row_text_area_empty (row))) { row->glyphs[TEXT_AREA]->charpos = -1; - row->displays_text_p = false; + /* Don't reset the displays_text_p flag if we are + displaying line numbers or line-prefix. */ + if (!row_has_glyphs) + row->displays_text_p = false; if (!NILP (BVAR (XBUFFER (it->w->contents), indicate_empty_lines)) - && (!MINI_WINDOW_P (it->w) - || (minibuf_level && EQ (it->window, minibuf_window)))) + && (!MINI_WINDOW_P (it->w))) row->indicate_empty_line_p = true; } @@ -20935,6 +21374,10 @@ display_line (struct it *it, int cursor_vpos) process the prefix now. */ if (it->area == TEXT_AREA && pending_handle_line_prefix) { + /* Line numbers should precede the line-prefix or wrap-prefix. */ + if (line_number_needed) + maybe_produce_line_number (it); + pending_handle_line_prefix = false; handle_line_prefix (it); } @@ -22006,7 +22449,7 @@ Value is the new character position of point. */) reach point, in order to start from its X coordinate. So we need to disregard the window's horizontal extent in that case. */ if (it.line_wrap == TRUNCATE) - it.last_visible_x = INFINITY; + it.last_visible_x = DISP_INFINITY; if (it.cmp_it.id < 0 && it.method == GET_FROM_STRING @@ -22099,7 +22542,7 @@ Value is the new character position of point. */) { start_display (&it, w, pt); if (it.line_wrap == TRUNCATE) - it.last_visible_x = INFINITY; + it.last_visible_x = DISP_INFINITY; reseat_at_previous_visible_line_start (&it); it.current_x = it.current_y = it.hpos = 0; if (pt_vpos != 0) @@ -27616,15 +28059,23 @@ x_produce_glyphs (struct it *it) { int tab_width = it->tab_width * font->space_width; int x = it->current_x + it->continuation_lines_width; + int x0 = x; + /* Adjust for line numbers, if needed. */ + if (!NILP (Vdisplay_line_numbers) && x0 >= it->lnum_pixel_width) + x -= it->lnum_pixel_width; int next_tab_x = ((1 + x + tab_width - 1) / tab_width) * tab_width; /* If the distance from the current position to the next tab stop is less than a space character width, use the tab stop after that. */ - if (next_tab_x - x < font->space_width) + if (next_tab_x - x0 < font->space_width) next_tab_x += tab_width; + if (!NILP (Vdisplay_line_numbers) && x0 >= it->lnum_pixel_width) + next_tab_x += (it->lnum_pixel_width + - ((it->w->hscroll * font->space_width) + % tab_width)); - it->pixel_width = next_tab_x - x; + it->pixel_width = next_tab_x - x0; it->nglyphs = 1; if (FONT_TOO_HIGH (font)) { @@ -31708,6 +32159,12 @@ They are still logged to the *Messages* buffer. */); /* Name of the face used to highlight trailing whitespace. */ DEFSYM (Qtrailing_whitespace, "trailing-whitespace"); + /* Names of the faces used to display line numbers. */ + DEFSYM (Qline_number, "line-number"); + DEFSYM (Qline_number_current_line, "line-number-current-line"); + /* Name of a text property which disables line-number display. */ + DEFSYM (Qdisplay_line_numbers_disable, "display-line-numbers-disable"); + /* Name and number of the face used to highlight escape glyphs. */ DEFSYM (Qescape_glyph, "escape-glyph"); @@ -32215,6 +32672,46 @@ To add a prefix to continuation lines, use `wrap-prefix'. */); DEFSYM (Qline_prefix, "line-prefix"); Fmake_variable_buffer_local (Qline_prefix); + DEFVAR_LISP ("display-line-numbers", Vdisplay_line_numbers, + doc: /* Non-nil means display line numbers. +By default, line numbers are displayed before each non-continuation +line that displays buffer text, i.e. after each newline that came +from buffer text. However, if the value is `visual', every screen +line will have a number. + +Lisp programs can disable display of a line number of a particular +screen line by putting the `display-line-numbers-disable' text +property or overlay property on the first visible character of +that line. */); + Vdisplay_line_numbers = Qnil; + DEFSYM (Qdisplay_line_numbers, "display-line-numbers"); + Fmake_variable_buffer_local (Qdisplay_line_numbers); + DEFSYM (Qrelative, "relative"); + DEFSYM (Qvisual, "visual"); + + DEFVAR_LISP ("display-line-number-width", Vdisplay_line_number_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_number_width = Qnil; + DEFSYM (Qdisplay_line_number_width, "display-line-number-width"); + Fmake_variable_buffer_local (Qdisplay_line_number_width); + + DEFVAR_LISP ("display-line-numbers-current-absolute", + Vdisplay_line_numbers_current_absolute, + doc: /* Non-nil means display absolute number of current line. +This variable has effect only when `display-line-numbers' is +either `relative' or `visual'. */); + Vdisplay_line_numbers_current_absolute = Qt; + + DEFVAR_BOOL ("display-line-numbers-widen", display_line_numbers_widen, + doc: /* Non-nil means display line numbers disregarding any narrowing. */); + display_line_numbers_widen = false; + DEFSYM (Qdisplay_line_numbers_widen, "display-line-numbers-widen"); + Fmake_variable_buffer_local (Qdisplay_line_numbers_widen); + DEFVAR_BOOL ("inhibit-eval-during-redisplay", inhibit_eval_during_redisplay, doc: /* Non-nil means don't eval Lisp during redisplay. */); inhibit_eval_during_redisplay = false;