diff options
author | Eli Zaretskii <eliz@gnu.org> | 2017-06-17 17:42:44 +0300 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2017-06-17 17:42:44 +0300 |
commit | 7277c0fca7dab9f1b311c3eba5c42fd17acc3593 (patch) | |
tree | 6426512a85cc8a16bd274bb93eaf857f440e40db | |
parent | daf78963ee96484df1ecb0c10e7c0040d7b544a5 (diff) | |
download | emacs-7277c0fca7dab9f1b311c3eba5c42fd17acc3593.tar.gz |
Finish up native display of line numbers
* 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.
-rw-r--r-- | etc/NEWS | 21 | ||||
-rw-r--r-- | lisp/cus-start.el | 16 | ||||
-rw-r--r-- | lisp/faces.el | 8 | ||||
-rw-r--r-- | lisp/menu-bar.el | 19 | ||||
-rw-r--r-- | src/dispextern.h | 3 | ||||
-rw-r--r-- | src/xdisp.c | 126 |
6 files changed, 162 insertions, 31 deletions
@@ -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. + * Editing Changes in Emacs 26.1 diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 744fe7f69ee..0fe41d7c3ea 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -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 diff --git a/lisp/faces.el b/lisp/faces.el index 9a8a1344caf..ac2d210a32f 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -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 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/src/dispextern.h b/src/dispextern.h index 050c68b8e08..08e5caa893b 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -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; diff --git a/src/xdisp.c b/src/xdisp.c index dcef242966e..ebf5edc4d05 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -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. */); |