linum.el 9.9 KB

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