From 50e4b47d0144511757a274c85f77813f15c3c966 Mon Sep 17 00:00:00 2001 From: Miranda Marquez Date: Thu, 18 Jul 2024 12:11:29 -0800 Subject: [PATCH] destroy history to get the thing pushed --- .gitignore | 3 + nlinum.el | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 .gitignore create mode 100644 nlinum.el diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d057a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/nlinum-pkg.el +/nlinum-autoloads.el +*.elc diff --git a/nlinum.el b/nlinum.el new file mode 100644 index 0000000..f487432 --- /dev/null +++ b/nlinum.el @@ -0,0 +1,394 @@ +;;; nlinum.el --- Show line numbers in the margin -*- lexical-binding: t -*- + +;; Copyright (C) 2012, 2014-2019 Free Software Foundation, Inc. + +;; Author: Stefan Monnier +;; Keywords: convenience +;; Version: 1.9 + +;; This program 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. + +;; This program 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 this program. If not, see . + +;;; Commentary: + +;; This is like linum-mode, but uses jit-lock to be (hopefully) +;; more efficient. + +;;;; News: + +;; v1.9: +;; - Add `nlinu-widen'. + +;; v1.8: +;; - Add `nlinum-use-right-margin'. + +;; v1.7: +;; - Add ability to highlight current line number. +;; - New custom variable `nlinum-highlight-current-line' and +;; face `nlinum-current-line'. +;; - New `nlinum' group in Custom. + +;; v1.3: +;; - New custom variable `nlinum-format'. +;; - Change in calling convention of `nlinum-format-function'. + +;; v1.2: +;; - New global mode `global-nlinum-mode'. +;; - New config var `nlinum-format-function'. + +;;;; Known bugs: +;; - When narrowed, there can be a "bogus" line number that appears at the +;; very end of the buffer. +;; - After widening, the current line's number may stay stale until the +;; next command. + +;;; Code: + +(defgroup nlinum nil + "Show line numbers in the margin, (hopefully) more efficiently." + :group 'convenience + :group 'linum + :prefix "nlinum") + +(defcustom nlinum-highlight-current-line nil + ;; FIXME: It would be good to enable it by default, but only once we make it + ;; work right with multiple-windows. + "Whether the current line number should be highlighted. +When non-nil, the current line number is highlighted in `nlinum-current-line' +face." + :type 'boolean) + +(defcustom nlinum-widen nil + "If nil, count lines within the current narrowing only." + :type 'boolean) + +(defface nlinum-current-line + '((t :inherit (shadow default))) + "Face for displaying current line.") + +(defvar nlinum--width 2) +(make-variable-buffer-local 'nlinum--width) + +(defvar nlinum--current-line 0 + "Store current line number.") +(make-variable-buffer-local 'nlinum--current-line) + +(defcustom nlinum-use-right-margin nil + "If non-nil, put line numbers in the right margin instead of the left one." + :type 'boolean) + +;; (defvar nlinum--desc "") + +(defvar nlinum--using-right-margin nil) +(make-variable-buffer-local 'nlinum--using-right-margin) + +;;;###autoload +(define-minor-mode nlinum-mode + "Toggle display of line numbers in the left margin (Linum mode). +With a prefix argument ARG, enable Linum mode if ARG is positive, +and disable it otherwise. If called from Lisp, enable the mode +if ARG is omitted or nil. + +Linum mode is a buffer-local minor mode." + :lighter nil ;; (" NLinum" nlinum--desc) + (jit-lock-unregister #'nlinum--region) + (remove-hook 'window-configuration-change-hook #'nlinum--setup-window :local) + (remove-hook 'text-scale-mode-hook #'nlinum--setup-window :local) + (remove-hook 'after-change-functions #'nlinum--after-change :local) + (remove-hook 'post-command-hook #'nlinum--current-line-update :local) + (remove-hook 'pre-redisplay-functions #'nlinum--check-narrowing :local) + (kill-local-variable 'nlinum--line-number-cache) + (kill-local-variable 'nlinum--last-point-min) + (remove-overlays (point-min) (point-max) 'nlinum t) + ;; (kill-local-variable 'nlinum--ol-counter) + (kill-local-variable 'nlinum--width) + (when (and (local-variable-p 'nlinum--using-right-margin) + (not (eq nlinum--using-right-margin nlinum-use-right-margin))) + ;; Remove outdated margins as well as margin annotations. + (let ((nlinum-mode nil)) (nlinum--flush)) + (kill-local-variable 'nlinum--using-right-margin)) + (setq nlinum--using-right-margin nlinum-use-right-margin) + (when nlinum-mode + ;; FIXME: Another approach would be to make the mode permanent-local, + ;; which might indeed be preferable. + (add-hook 'change-major-mode-hook (lambda () (nlinum-mode -1))) + (add-hook 'text-scale-mode-hook #'nlinum--setup-window nil :local) + (add-hook 'window-configuration-change-hook #'nlinum--setup-window nil t) + (add-hook 'after-change-functions #'nlinum--after-change nil :local) + (add-hook 'pre-redisplay-functions #'nlinum--check-narrowing nil :local) + (if nlinum-highlight-current-line + (add-hook 'post-command-hook #'nlinum--current-line-update nil :local)) + (jit-lock-register #'nlinum--region :contextual)) + (nlinum--setup-windows)) + +(defun nlinum--face-height (face) + (aref (font-info (face-font face)) 3)) + +(defun nlinum--face-width (face) ;New info only in Emacs>=25. + (let ((fi (font-info (face-font face)))) + (when (> (length fi) 11) + (let ((width (aref fi 11))) + (if (<= width 0) + (aref fi 10) + width))))) + +(defun nlinum--setup-window () + ;; FIXME: The interaction between different uses of the margin is + ;; problematic. We should have a way for different packages to indicate (and + ;; change) their preference independently. + (let* ((width (if (display-graphic-p) + (ceiling + (let ((width (nlinum--face-width 'linum))) + (if width + (/ (* nlinum--width 1.0 width) + (frame-char-width)) + (/ (* nlinum--width 1.0 + (nlinum--face-height 'linum)) + (frame-char-height))))) + nlinum--width)) + (cur-margins (window-margins)) + (cur-margin (if nlinum--using-right-margin + (cdr cur-margins) (car cur-margins))) + ;; (EXT . OURS) keeps track of the size of the margin, where EXT is the + ;; size chosen by external code and OURS is the size we last set. + ;; OURS is used to detect when someone else modifies the margin. + (margin-settings (window-parameter nil 'linum--margin))) + (if margin-settings + (unless (eq (cdr margin-settings) cur-margin) + ;; Damn! The margin is not what it used to be! => Update EXT! + (setcar margin-settings cur-margin)) + (set-window-parameter nil 'linum--margin + (setq margin-settings (list cur-margin)))) + (and (car margin-settings) width + (setq width (max width (car margin-settings)))) + (setcdr margin-settings width) + (apply #'set-window-margins nil + (let ((new-margin (if nlinum-mode width (car margin-settings)))) + (if nlinum--using-right-margin + (list (car cur-margins) new-margin) + (list new-margin (cdr cur-margins))))))) + +(defun nlinum--setup-windows () + (dolist (win (get-buffer-window-list nil nil t)) + (with-selected-window win (nlinum--setup-window)))) + +(defun nlinum--flush () + (nlinum--setup-windows) + (save-excursion + (save-restriction + (widen) + ;; (kill-local-variable 'nlinum--ol-counter) + (remove-overlays (point-min) (point-max) 'nlinum t) + (run-with-timer 0 nil + (lambda (buf) + (with-current-buffer buf + (with-silent-modifications + ;; FIXME: only remove `fontified' on those parts of + ;; the buffer that had an nlinum overlay! + (save-excursion + (save-restriction + (widen) + (remove-text-properties + (point-min) (point-max) '(fontified))))))) + (current-buffer))))) + +(defun nlinum--current-line-update () + "Update current line number." + (let ((last-line nlinum--current-line)) + (setq nlinum--current-line (save-excursion + (forward-line 0) + (nlinum--line-number-at-pos))) + + (let ((line-diff (- last-line nlinum--current-line)) + beg end) + ;; Remove the text properties only if the current line has changed. + (when (not (zerop line-diff)) + (if (natnump line-diff) + ;; Point is moving upward. + (progn + (setq beg (line-beginning-position)) + (setq end (line-end-position (1+ line-diff)))) + ;; Point is moving downward. + (setq beg (line-beginning-position (1+ line-diff))) + (setq end (line-end-position))) + + ;; (message "curr-line:%d [beg/end:%d/%d] -- last-line:%d" + ;; nlinum--current-line beg end last-line) + (with-silent-modifications + (remove-text-properties beg + ;; Handle the case of blank lines too. + (min (point-max) (1+ end)) + '(fontified))))))) + +;; (defun nlinum--ol-count () +;; (let ((i 0)) +;; (dolist (ol (overlays-in (point-min) (point-max))) +;; (when (overlay-get ol 'nlinum) (incf i))) +;; i)) + +;; (defvar nlinum--ol-counter 100) +;; (make-variable-buffer-local 'nlinum--ol-counter) + +;; (defun nlinum--flush-overlays (buffer) +;; (with-current-buffer buffer +;; (kill-local-variable 'nlinum--ol-counter) +;; ;; We've created many overlays in this buffer, which can slow +;; ;; down operations significantly. Let's flush them. +;; ;; An easy way to flush them is +;; ;; (remove-overlays min max 'nlinum t) +;; ;; (put-text-property min max 'fontified nil) +;; ;; but if the visible part of the buffer requires more than +;; ;; nlinum-overlay-threshold overlays, then we'll inf-loop. +;; ;; So let's be more careful about removing overlays. +;; (let ((windows (get-buffer-window-list nil nil t)) +;; (start (point-min)) +;; (debug-count (nlinum--ol-count))) +;; (with-silent-modifications +;; (while (< start (point-max)) +;; (let ((end (point-max))) +;; (dolist (window windows) +;; (cond +;; ((< start (1- (window-start window))) +;; (setq end (min (1- (window-start window)) end))) +;; ((< start (1+ (window-end window))) +;; (setq start (1+ (window-end window)))))) +;; (when (< start end) +;; (remove-overlays start end 'nlinum t) +;; ;; Warn jit-lock that this part of the buffer is not done any +;; ;; more. This has the downside that font-lock will be re-applied +;; ;; as well. But jit-lock doesn't know how to (and doesn't want +;; ;; to) keep track of the status of its various +;; ;; clients independently. +;; (put-text-property start end 'fontified nil) +;; (setq start (+ end 1)))))) +;; (let ((debug-new-count (nlinum--ol-count))) +;; (message "Flushed %d overlays, %d remaining" +;; (- debug-count debug-new-count) debug-new-count))))) + + +(defvar nlinum--line-number-cache nil) +(make-variable-buffer-local 'nlinum--line-number-cache) + +;; We could try and avoid flushing the cache at every change, e.g. with: +;; (defun nlinum--before-change (start _end) +;; (if (and nlinum--line-number-cache +;; (< start (car nlinum--line-number-cache))) +;; (save-excursion (goto-char start) (nlinum--line-number-at-pos)))) +;; But it's far from clear that it's worth the trouble. The current simplistic +;; approach seems to be good enough in practice. + +(defun nlinum--after-change (&rest _args) + (setq nlinum--line-number-cache nil)) + +(defvar nlinum--last-point-min nil) +(make-variable-buffer-local 'nlinum--last-point-min) + +(defun nlinum--check-narrowing (&optional _win) + ;; FIXME: We should also flush if nlinum-widen was changed. + ;; Note: if nlinum-widen is t the flush is still needed when + ;; point-min is/was in the middle of a line. + (unless (eql nlinum--last-point-min (point-min)) + (unless nlinum-widen (setq nlinum--line-number-cache nil)) + (nlinum--current-line-update) + (setq nlinum--last-point-min (point-min)) + (nlinum--flush))) + +(defun nlinum--line-number-at-pos () + "Like `line-number-at-pos' but sped up with a cache. +Only works right if point is at BOL." + ;; (cl-assert (bolp)) + (if nlinum-widen + (save-excursion + (save-restriction + (widen) + (forward-line 0) ;In case (point-min) was not at BOL. + (let ((nlinum-widen nil)) + (nlinum--line-number-at-pos)))) + (let ((pos + (if (and nlinum--line-number-cache + (> (- (point) (point-min)) + (abs (- (point) (car nlinum--line-number-cache))))) + (funcall (if (> (point) (car nlinum--line-number-cache)) + #'+ #'-) + (cdr nlinum--line-number-cache) + (count-lines (point) (car nlinum--line-number-cache))) + (line-number-at-pos)))) + ;;(assert (= pos (line-number-at-pos))) + (setq nlinum--line-number-cache (cons (point) pos)) + pos))) + +(defcustom nlinum-format "%d" + "Format of the line numbers. +Used by the default `nlinum-format-function'." + :type 'string + :group 'linum) + +(defvar nlinum-format-function + (lambda (line width) + (let* ((is-current-line (= line nlinum--current-line)) + (str (format nlinum-format line))) + (when (< (length str) width) + ;; Left pad to try and right-align the line-numbers. + (setq str (concat (make-string (- width (length str)) ?\ ) str))) + (put-text-property 0 width 'face + (if (and nlinum-highlight-current-line + is-current-line) + 'nlinum-current-line + 'linum) + str) + str)) + "Function to build the string representing the line number. +Takes 2 arguments LINE and WIDTH, both of them numbers, and should return +a string. WIDTH is the ideal width of the result. If the result is larger, +it may cause the margin to be resized and line numbers to be recomputed.") + +(defun nlinum--region (start limit) + (save-excursion + ;; Text may contain those nasty intangible properties, but + ;; that shouldn't prevent us from counting those lines. + (let ((inhibit-point-motion-hooks t)) + (goto-char start) + (unless (bolp) (forward-line 1)) + (remove-overlays (point) limit 'nlinum t) + (let ((line (nlinum--line-number-at-pos))) + (while + (and (not (eobp)) (< (point) limit) + (let* ((ol (make-overlay (point) (1+ (point)))) + (str (funcall nlinum-format-function + line nlinum--width)) + (width (string-width str)) + (margin (if nlinum--using-right-margin + 'right-margin 'left-margin))) + (when (< nlinum--width width) + (setq nlinum--width width) + (nlinum--flush)) + (overlay-put ol 'nlinum t) + (overlay-put ol 'evaporate t) + (overlay-put ol 'before-string + (propertize " " 'display + `((margin ,margin) ,str))) + ;; (setq nlinum--ol-counter (1- nlinum--ol-counter)) + ;; (when (= nlinum--ol-counter 0) + ;; (run-with-idle-timer 0.5 nil #'nlinum--flush-overlays + ;; (current-buffer))) + (setq line (1+ line)) + (zerop (forward-line 1)))))))) + ;; (setq nlinum--desc (format "-%d" (nlinum--ol-count))) + nil) + +;;;###autoload +(define-globalized-minor-mode global-nlinum-mode nlinum-mode + (lambda () (unless (minibufferp) (nlinum-mode)))) + +(provide 'nlinum) +;;; nlinum.el ends here