emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

linum.el: problem (bug ?) fix and improvement


From: Toru TSUNEYOSHI
Subject: linum.el: problem (bug ?) fix and improvement
Date: Sat, 7 May 2011 23:50:28 +0900

Hello.

I sometimes use linum-mode, then I meet a problem.
It is displaying a wrong line number when there is an invisible line.
(I don't know the problem is a bug clearly, but it is trouble to me.)

    Example:
    ====================================================================
      In buffer (linum-mode is enabled).

        1 abc
        2 def
        3 ghi
        4 jkl

      Make the 2nd line ("def\n") invisible (by `facemenu-set-invisible').

        1 abc
        2 ghi                   <= wrong
        4 jkl
    ====================================================================

I fixed the problem.

    the attached file: "linum-mode.el.1.diff"
                       (based on Emacs 23.3.)

(If the following additional improvement is unnecessary, please ignore.)

Additionally, I improved about invisible buffer string.

    the attached file: "linum-mode.el.2.diff"
                       (including the above bug fix.)

  (1) Indicating invisible line(s) by an underlined line number
      when the following line(s) is/are invisible.

    ====================================================================
        _1_ abc                 <= underline
         3  ghi
         4  jkl
    ====================================================================

  (2) Indicating partial invisible string by a `strike-through' line
      number when the line has partial invisible string.

    ====================================================================
        _1_ abc
         3  ghi
         4  jkl

      Make the 3rd line ("hi") invisible partially.

        _1_ abc
        -3- g                   <= strike-through
         4  jkl

      Make the 1st line ("ab") invisible partially.

        -_1_- c                 <= underline and strike-through
         -3-  g
          4   jkl
    ====================================================================

  For the additional improvement,
check the new variable `linum-indicate-invis',
and eval the following, please.

    (setq linum-indicate-invis t)
--- linum.el.orig       2011-01-09 02:45:14.000000000 +0900
+++ linum.el    2011-05-06 21:24:13.338622400 +0900
@@ -146,26 +146,38 @@
     ;; 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))))
+      (unless (and (invisible-p (point))
+                  (catch 'invisible-p
+                    (save-excursion
+                      (let* ((inhibit-field-text-motion t)
+                             (eol (line-end-position)))
+                        (while (not (eolp))
+                          (goto-char
+                           (next-single-char-property-change
+                            (point) 'invisible nil eol))
+                          (unless (invisible-p (point))
+                            (throw 'invisible-p nil)))
+                        t))))    ; current line is invisible entirely ?
+       (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)))))
       ;; Text may contain those nasty intangible properties, but that
       ;; shouldn't prevent us from counting those lines.
       (let ((inhibit-point-motion-hooks t))
--- 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)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]