More CL cleanups and reduction of use of cl.el.
[emacs.git] / lisp / linum.el
blob162dc19f43742b6829c218ca75efede09c0cbd6f
1 ;;; linum.el --- display line numbers in the left margin -*- lexical-binding: t -*-
3 ;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
5 ;; Author: Markus Triska <markus.triska@gmx.at>
6 ;; Maintainer: FSF
7 ;; Keywords: convenience
8 ;; Version: 0.9x
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
25 ;;; Commentary:
27 ;; Display line numbers for the current buffer.
29 ;; Toggle display of line numbers with M-x linum-mode. To enable
30 ;; line numbering in all buffers, use M-x global-linum-mode.
32 ;;; Code:
34 (defconst linum-version "0.9x")
36 (defvar linum-overlays nil "Overlays used in this buffer.")
37 (defvar linum-available nil "Overlays available for reuse.")
38 (defvar linum-before-numbering-hook nil
39 "Functions run in each buffer before line numbering starts.")
41 (mapc #'make-variable-buffer-local '(linum-overlays linum-available))
43 (defgroup linum nil
44 "Show line numbers in the left margin."
45 :group 'convenience)
47 ;;;###autoload
48 (defcustom linum-format 'dynamic
49 "Format used to display line numbers.
50 Either a format string like \"%7d\", `dynamic' to adapt the width
51 as needed, or a function that is called with a line number as its
52 argument and should evaluate to a string to be shown on that line.
53 See also `linum-before-numbering-hook'."
54 :group 'linum
55 :type 'sexp)
57 (defface linum
58 '((t :inherit (shadow default)))
59 "Face for displaying line numbers in the display margin."
60 :group 'linum)
62 (defcustom linum-eager t
63 "Whether line numbers should be updated after each command.
64 The conservative setting `nil' might miss some buffer changes,
65 and you have to scroll or press \\[recenter-top-bottom] to update the numbers."
66 :group 'linum
67 :type 'boolean)
69 (defcustom linum-delay nil
70 "Delay updates to give Emacs a chance for other changes."
71 :group 'linum
72 :type 'boolean)
74 ;;;###autoload
75 (define-minor-mode linum-mode
76 "Toggle display of line numbers in the left margin (Linum mode).
77 With a prefix argument ARG, enable Linum mode if ARG is positive,
78 and disable it otherwise. If called from Lisp, enable the mode
79 if ARG is omitted or nil.
81 Linum mode is a buffer-local minor mode."
82 :lighter "" ; for desktop.el
83 (if linum-mode
84 (progn
85 (if linum-eager
86 (add-hook 'post-command-hook (if linum-delay
87 'linum-schedule
88 'linum-update-current) nil t)
89 (add-hook 'after-change-functions 'linum-after-change nil t))
90 (add-hook 'window-scroll-functions 'linum-after-scroll nil t)
91 ;; Using both window-size-change-functions and
92 ;; window-configuration-change-hook seems redundant. --Stef
93 ;; (add-hook 'window-size-change-functions 'linum-after-size nil t)
94 (add-hook 'change-major-mode-hook 'linum-delete-overlays nil t)
95 (add-hook 'window-configuration-change-hook
96 ;; FIXME: If the buffer is shown in N windows, this
97 ;; will be called N times rather than once. We should use
98 ;; something like linum-update-window instead.
99 'linum-update-current nil t)
100 (linum-update-current))
101 (remove-hook 'post-command-hook 'linum-update-current t)
102 (remove-hook 'post-command-hook 'linum-schedule t)
103 ;; (remove-hook 'window-size-change-functions 'linum-after-size t)
104 (remove-hook 'window-scroll-functions 'linum-after-scroll t)
105 (remove-hook 'after-change-functions 'linum-after-change t)
106 (remove-hook 'window-configuration-change-hook 'linum-update-current t)
107 (remove-hook 'change-major-mode-hook 'linum-delete-overlays t)
108 (linum-delete-overlays)))
110 ;;;###autoload
111 (define-globalized-minor-mode global-linum-mode linum-mode linum-on)
113 (defun linum-on ()
114 (unless (minibufferp)
115 (linum-mode 1)))
117 (defun linum-delete-overlays ()
118 "Delete all overlays displaying line numbers for this buffer."
119 (mapc #'delete-overlay linum-overlays)
120 (setq linum-overlays nil)
121 (dolist (w (get-buffer-window-list (current-buffer) nil t))
122 (set-window-margins w 0 (cdr (window-margins w)))))
124 (defun linum-update-current ()
125 "Update line numbers for the current buffer."
126 (linum-update (current-buffer)))
128 (defun linum-update (buffer)
129 "Update line numbers for all windows displaying BUFFER."
130 (with-current-buffer buffer
131 (when linum-mode
132 (setq linum-available linum-overlays)
133 (setq linum-overlays nil)
134 (save-excursion
135 (mapc #'linum-update-window
136 (get-buffer-window-list buffer nil 'visible)))
137 (mapc #'delete-overlay linum-available)
138 (setq linum-available nil))))
140 (defun linum-update-window (win)
141 "Update line numbers for the portion visible in window WIN."
142 (goto-char (window-start win))
143 (let ((line (line-number-at-pos))
144 (limit (window-end win t))
145 (fmt (cond ((stringp linum-format) linum-format)
146 ((eq linum-format 'dynamic)
147 (let ((w (length (number-to-string
148 (count-lines (point-min) (point-max))))))
149 (concat "%" (number-to-string w) "d")))))
150 (width 0))
151 (run-hooks 'linum-before-numbering-hook)
152 ;; Create an overlay (or reuse an existing one) for each
153 ;; line visible in this window, if necessary.
154 (while (and (not (eobp)) (<= (point) limit))
155 (let* ((str (if fmt
156 (propertize (format fmt line) 'face 'linum)
157 (funcall linum-format line)))
158 (visited (catch 'visited
159 (dolist (o (overlays-in (point) (point)))
160 (when (equal-including-properties
161 (overlay-get o 'linum-str) str)
162 (unless (memq o linum-overlays)
163 (push o linum-overlays))
164 (setq linum-available (delq o linum-available))
165 (throw 'visited t))))))
166 (setq width (max width (length str)))
167 (unless visited
168 (let ((ov (if (null linum-available)
169 (make-overlay (point) (point))
170 (move-overlay (pop linum-available) (point) (point)))))
171 (push ov linum-overlays)
172 (overlay-put ov 'before-string
173 (propertize " " 'display `((margin left-margin) ,str)))
174 (overlay-put ov 'linum-str str))))
175 ;; Text may contain those nasty intangible properties, but that
176 ;; shouldn't prevent us from counting those lines.
177 (let ((inhibit-point-motion-hooks t))
178 (forward-line))
179 (setq line (1+ line)))
180 (set-window-margins win width (cdr (window-margins win)))))
182 (defun linum-after-change (beg end _len)
183 ;; update overlays on deletions, and after newlines are inserted
184 (when (or (= beg end)
185 (= end (point-max))
186 (string-match-p "\n" (buffer-substring-no-properties beg end)))
187 (linum-update-current)))
189 (defun linum-after-scroll (win _start)
190 (linum-update (window-buffer win)))
192 ;; (defun linum-after-size (frame)
193 ;; (linum-after-config))
195 (defun linum-schedule ()
196 ;; schedule an update; the delay gives Emacs a chance for display changes
197 (run-with-idle-timer 0 nil #'linum-update-current))
199 ;; (defun linum-after-config ()
200 ;; (walk-windows (lambda (w) (linum-update (window-buffer w))) nil 'visible))
202 (defun linum-unload-function ()
203 "Unload the Linum library."
204 (global-linum-mode -1)
205 ;; continue standard unloading
206 nil)
208 (provide 'linum)
210 ;;; linum.el ends here