--- linum.el.orig 2011-01-09 02:45:14.000000000 +0900 +++ linum.el 2011-05-07 23:06:51.062956400 +0900 @@ -58,6 +58,39 @@ "Face for displaying line numbers in the display margin." :group 'linum) +(defface linum-partial-text-invis + '((t (:inherit linum :strike-through t))) + "Face for displaying line numbers in the display margin. +It is used only for a line which has the partial (not entire) invisible text." + :group 'linum) + +(defface linum-following-line-invis + '((t (:inherit linum :underline t))) + "Face for displaying line numbers in the display margin. +It is used only for a line which has the following invisible line(s)." + :group 'linum) + +(defface linum-partial-text-and-following-line-invis + ;;'((t (:inherit (linum-partial-text-invis linum-following-line-invis)))) ; NG + '((t (:inherit linum :strike-through t :underline t))) + "Face for displaying line numbers in the display margin. +It is used only for a line which has the partial (not entire) invisible text +and the following invisible line(s)." + :group 'linum) + +(defcustom linum-indicate-invis nil + "Whether to indicate invisibility by line number's face. + +A value of nil means don't indicate. +A value of `partial-text' means do only for the partial text. +A value of `following-line' means do only for the following line. +A value of t means do both for the partial text and for the following line." + :type '(choice (const :tag "Don't indicate" nil) + (const :tag "Only for partial text" partial-text) + (const :tag "Only for the following line" following-line) + (const :tag "Both for partial text and for the following line" t)) + :group 'linum) + (defcustom linum-eager t "Whether line numbers should be updated after each command. The conservative setting `nil' might miss some buffer changes, @@ -133,51 +166,200 @@ (defun linum-update-window (win) "Update line numbers for the portion visible in window WIN." - (goto-char (window-start win)) + (goto-char (1- (window-end win t))) + (let ((inhibit-point-motion-hooks t)) + (forward-line 0)) (let ((line (line-number-at-pos)) - (limit (window-end win t)) + (limit (window-start win)) (fmt (cond ((stringp linum-format) linum-format) ((eq linum-format 'dynamic) (let ((w (length (number-to-string (count-lines (point-min) (point-max)))))) (concat "%" (number-to-string w) "d"))))) - (width 0)) + (width 0) + (shortage 0) + bol-invis eol eol-invis invis orig-invis old-invis + (invis-list '((nil . 0) + (partial-text . 1) + (following-line . 2) + (partial-text-and-following-line . 3) ; (+ 1 2) + (whole-line . 6))) ; (+ 2 4), it may hide `following-line' + (face-list `((nil . linum) + (partial-text + . ,(if (memq linum-indicate-invis '(t partial-text)) + 'linum-partial-text-invis + 'linum)) + (following-line + . ,(if (memq linum-indicate-invis '(t following-line)) + 'linum-following-line-invis + 'linum)) + (partial-text-and-following-line + . ,(or (cdr (assq linum-indicate-invis + '((nil . linum) + (t . linum-partial-text-and-following-line-invis) + (partial-text . linum-partial-text-invis) + (following-line . linum-following-line-invis)))) + 'linum)) + ;;(whole-line . nil) + ))) (run-hooks 'linum-before-numbering-hook) ;; Create an overlay (or reuse an existing one) for each ;; line visible in this window, if necessary. - (while (and (not (eobp)) (<= (point) limit)) - (let* ((str (if fmt - (propertize (format fmt line) 'face 'linum) - (funcall linum-format line))) - (visited (catch 'visited - (dolist (o (overlays-in (point) (point))) - (when (equal-including-properties - (overlay-get o 'linum-str) str) - (unless (memq o linum-overlays) - (push o linum-overlays)) - (setq linum-available (delq o linum-available)) - (throw 'visited t)))))) - (setq width (max width (length str))) - (unless visited - (let ((ov (if (null linum-available) - (make-overlay (point) (point)) - (move-overlay (pop linum-available) (point) (point))))) - (push ov linum-overlays) - (overlay-put ov 'before-string - (propertize " " 'display `((margin left-margin) ,str))) - (overlay-put ov 'linum-str str)))) + (while (and (zerop shortage) (<= limit (point))) + (let ((inhibit-point-motion-hooks t)) + (setq bol-invis (invisible-p (point)) + eol (line-end-position) + eol-invis (invisible-p eol) + + ;; | bol-invis | (mol-invis) | eol-invis | -> | invis | + ;; |-----------+-------------+-----------+----+---------------------------------| + ;; | t | t | t | | whole-line | + ;; | t | t | nil | | partial-text | + ;; | t | nil | t | | partial-text-and-following-line | + ;; | t | nil | nil | | partial-text | + ;; | nil | t | t | | partial-text-and-following-line | + ;; | nil | t | nil | | partial-text | + ;; | nil | nil | t | | following-line | + ;; | nil | nil | nil | | nil | + + invis (cond (bol-invis + ;; middle of line is invisible ? + (cond ((catch 'invisible-p + (while t + (goto-char + (next-single-char-property-change + (point) 'invisible nil eol)) + (cond ((eolp) + (throw 'invisible-p t)) + ((not (invisible-p (point))) + (throw 'invisible-p nil))))) + (cond (eol-invis + 'whole-line) + (t + 'partial-text))) + (t + (cond (eol-invis + 'partial-text-and-following-line) + (t + 'partial-text))))) + (t + ;; middle of line is invisible ? + (cond ((catch 'invisible-p + (while t + (goto-char + (next-single-char-property-change + (point) 'invisible nil eol)) + (cond ((eolp) + (throw 'invisible-p nil)) + ((invisible-p (point)) + (throw 'invisible-p t))))) + (cond (eol-invis + 'partial-text-and-following-line) + (t + 'partial-text))) + (t + (cond (eol-invis + 'following-line) + (t + nil)))))) + orig-invis invis + ;; consider the following line invisibility + invis (car (rassq (logior (cdr (assq invis invis-list)) + (cdr (assq old-invis invis-list))) + invis-list))) + (forward-line 0)) + (unless (eq invis 'whole-line) + (let* ((str (if fmt + (propertize (format fmt line) + 'face (cdr (assq invis face-list))) + (funcall linum-format line))) + (visited (catch 'visited + (dolist (o (overlays-in (point) (point))) + (when (equal-including-properties + (overlay-get o 'linum-str) str) + (unless (memq o linum-overlays) + (push o linum-overlays)) + (setq linum-available (delq o linum-available)) + (throw 'visited t)))))) + (setq width (max width (length str))) + (unless visited + (let ((ov (if (null linum-available) + (make-overlay (point) (point)) + (move-overlay (pop linum-available) (point) (point))))) + (push ov linum-overlays) + (overlay-put ov 'before-string + (propertize " " 'display `((margin left-margin) ,str))) + (overlay-put ov 'linum-str str) + (overlay-put ov 'linum-invis orig-invis))))) + (setq old-invis (if (eq invis 'whole-line) 'following-line nil)) ;; Text may contain those nasty intangible properties, but that ;; shouldn't prevent us from counting those lines. (let ((inhibit-point-motion-hooks t)) - (forward-line)) - (setq line (1+ line))) + (setq shortage (forward-line -1))) + (setq line (1- line))) (set-window-margins win width (cdr (window-margins win))))) (defun linum-after-change (beg end len) ;; update overlays on deletions, and after newlines are inserted (when (or (= beg end) (= end (point-max)) - (string-match-p "\n" (buffer-substring-no-properties beg end))) + (string-match-p "\n" (buffer-substring-no-properties beg end)) + (if linum-indicate-invis + ;; overlay property `linum-invis' value and current `invis' value are different ? + (save-excursion + (let ((inhibit-point-motion-hooks t) + bol-invis eol eol-invis invis orig-invis) + (goto-char beg) + (forward-line 0) + (setq orig-invis (catch 'linum-invis + (dolist (o (overlays-in (point) (point))) + (if (plist-member (overlay-properties o) 'linum-invis) + (throw 'linum-invis (overlay-get o 'linum-invis)))) + 'whole-line) + bol-invis (invisible-p (point)) + eol (line-end-position) + eol-invis (invisible-p eol) + invis (cond (bol-invis + ;; middle of line is invisible ? + (cond ((catch 'invisible-p + (while t + (goto-char + (next-single-char-property-change + (point) 'invisible nil eol)) + (cond ((eolp) + (throw 'invisible-p t)) + ((not (invisible-p (point))) + (throw 'invisible-p nil))))) + (cond (eol-invis + 'whole-line) + (t + 'partial-text))) + (t + (cond (eol-invis + 'partial-text-and-following-line) + (t + 'partial-text))))) + (t + ;; middle of line is invisible ? + (cond ((catch 'invisible-p + (while t + (goto-char + (next-single-char-property-change + (point) 'invisible nil eol)) + (cond ((eolp) + (throw 'invisible-p nil)) + ((invisible-p (point)) + (throw 'invisible-p t))))) + (cond (eol-invis + 'partial-text-and-following-line) + (t + 'partial-text))) + (t + (cond (eol-invis + 'following-line) + (t + nil))))))) + (not (eq orig-invis invis)))))) (linum-update-current))) (defun linum-after-scroll (win start)