summaryrefslogtreecommitdiff
path: root/lisp/display-line-numbers.el
blob: 4f87ffd0e22afb4292c9a9cc78491441475837ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
;;; display-line-numbers.el --- interface for display-line-numbers -*- lexical-binding: t -*-

;; Copyright (C) 2017-2023 Free Software Foundation, Inc.

;; Maintainer: emacs-devel@gnu.org
;; Keywords: convenience

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Provides a minor mode interface for `display-line-numbers'.
;;
;; Toggle display of line numbers with M-x display-line-numbers-mode.
;; To enable line numbering in all buffers, use M-x
;; global-display-line-numbers-mode.  To change the default type of
;; line numbers displayed, customize display-line-numbers-type.

;; NOTE: Customization variables for `display-line-numbers' itself are
;; defined in cus-start.el.

;;; Code:

(defgroup display-line-numbers nil
  "Display line numbers in the buffer."
  :group 'convenience
  :group 'display)

(defcustom display-line-numbers-type t
  "The default type of line numbers to use in `display-line-numbers-mode'.
See `display-line-numbers' for value options."
  :group 'display-line-numbers
  :type '(choice (const :tag "Relative line numbers" relative)
                 (const :tag "Relative visual line numbers" visual)
                 (other :tag "Absolute line numbers" t))
  :version "26.1")

(defcustom display-line-numbers-grow-only nil
  "If non-nil, do not shrink line number width."
  :group 'display-line-numbers
  :type 'boolean
  :version "26.1")

(defcustom display-line-numbers-width-start nil
  "If non-nil, count number of lines to use for line number width.
When `display-line-numbers-mode' is turned on, if this option is
non-nil, `display-line-numbers-width' is set up front to a width
necessary to display all line numbers in the buffer.  If the value
is a positive number, it is interpreted as extra lines to account
for when computing the required width; this should be set to the
number of lines in the tallest window in which you want to prevent
the line-number width from changing."
  :group 'display-line-numbers
  :type '(choice (boolean :tag "Minimum width for buffer's line count")
                 (integer :tag "Number of extra lines to account for"))
  :version "28.1")

(defun display-line-numbers-update-width ()
  "Prevent the line number width from shrinking."
  (let ((width (line-number-display-width)))
    (when (> width (or display-line-numbers-width 1))
      (setq display-line-numbers-width width))))

;;;###autoload
(define-minor-mode display-line-numbers-mode
  "Toggle display of line numbers in the buffer.
This uses `display-line-numbers' internally.

To change the type of line numbers displayed by default,
customize `display-line-numbers-type'.  To change the type while
the mode is on, set `display-line-numbers' directly."
  :lighter nil
  (if display-line-numbers-mode
      (progn
        (when display-line-numbers-width-start
          (setq display-line-numbers-width
                (length (number-to-string
                         (+ (count-lines (point-min) (point-max))
                            (if (and (numberp display-line-numbers-width-start)
                                     (> display-line-numbers-width-start 0))
                                display-line-numbers-width-start
                              0))))))
        (when display-line-numbers-grow-only
          (add-hook 'pre-command-hook #'display-line-numbers-update-width nil t))
        (setq display-line-numbers display-line-numbers-type))
    (remove-hook 'pre-command-hook #'display-line-numbers-update-width t)
    (setq display-line-numbers nil)))

(defun display-line-numbers--turn-on ()
  "Turn on `display-line-numbers-mode'."
  (unless (minibufferp)
    (display-line-numbers-mode)))

;;;###autoload
(define-globalized-minor-mode global-display-line-numbers-mode
  display-line-numbers-mode display-line-numbers--turn-on)



;;;###autoload
(defvar header-line-indent ""
  "String of spaces to indent the beginning of header-line due to line numbers.
This is intended to be used in `header-line-format', and requires
the `header-line-indent-mode' to be turned on, in order for the width
of this string to be kept updated when the line-number width changes
on display.  An example of a `header-line-format' that uses this
variable might look like this:

  (\"\" header-line-indent THE-REST...)

where THE-REST is the format string which produces the actual text
of the header-line.
Also see `header-line-indent-width'.")

;;;###autoload
(defvar header-line-indent-width 0
  "The width of the current line number display in the window.
This is measured in units of the frame's canonical columns.
This is updated when `header-line-indent-mode' is switched on,
and is intended for use in `:align-to' display specifications
that are part of `header-line-format', when portions of header-line
text should be aligned to respective parts of buffer text.
Also see `header-line-indent'.")

(defun header-line-indent--line-number-width ()
  "Return the width taken by `display-line-numbers' in the current buffer."
  ;; line-number-display-width returns the value for the selected
  ;; window, which might not be the window in which the current buffer
  ;; is displayed.
  (if (not display-line-numbers)
      0
    (let ((cbuf-window (get-buffer-window (current-buffer) t)))
      (if (window-live-p cbuf-window)
          (with-selected-window cbuf-window
            (truncate (line-number-display-width 'columns)))
        4))))

(defun header-line-indent--watch-line-number-width (_window)
  (let ((width (header-line-indent--line-number-width)))
    (setq header-line-indent-width width)
    (unless (= (length header-line-indent) width)
      (setq header-line-indent (make-string width ?\s)))))

(defun header-line-indent--window-scroll-function (window _start)
  (let ((width (with-selected-window window
                 (truncate (line-number-display-width 'columns)))))
    (setq header-line-indent-width width)
    (unless (= (length header-line-indent) width)
      (setq header-line-indent (make-string width ?\s)))))

;;;###autoload
(define-minor-mode header-line-indent-mode
  "Minor mode to help with alignment of header line when line numbers are shown.
This minor mode should be turned on in buffers which display header-line
that needs to be aligned with buffer text when `display-line-numbers-mode'
is turned on in the buffer.

Buffers that have this switched on should have a `header-line-format'
that uses the `header-line-indent' or the `header-line-indent-width'
variables, which this mode will keep up-to-date with the current
display of line numbers.  For example, a `header-line-format' that
looks like this:

  (\"\" header-line-indent THE-REST...)

will make sure the text produced by THE-REST (which should be
a header-line format string) is always indented to be aligned on
display with the first column of buffer text.

The `header-line-indent-width' variable is also kept updated,
and can be used, for instance, in `:align-to' specs as part
of `header-line-format', like this:

  (space :align-to (+ header-line-indent-width 10))

See also `line-number-display-width'."
  :lighter nil
  (if header-line-indent-mode
      (progn
        (setq-local header-line-indent ""
                    header-line-indent-width 0)
        (add-hook 'pre-redisplay-functions
                  #'header-line-indent--watch-line-number-width nil t)
        (add-hook 'window-scroll-functions
                  #'header-line-indent--window-scroll-function nil t))
    (setq-local header-line-indent ""
                header-line-indent-width 0)
    (remove-hook 'pre-redisplay-functions
                 #'header-line-indent--watch-line-number-width t)
    (remove-hook 'window-scroll-functions
                 #'header-line-indent--window-scroll-function t)))

(provide 'display-line-numbers)

;;; display-line-numbers.el ends here