branch: elpa/logview
commit 749255d1600c966187a195ef05a4608c2853ec49
Author: Paul Pogonyshev <pogonys...@gmail.com>
Commit: Paul Pogonyshev <pogonys...@gmail.com>

    Add command to replace timestamps with differences to corresponding section 
header.
---
 logview.el      | 165 +++++++++++++++++++++++++++++++++++++++++---------------
 test/logview.el |  31 +++++++++++
 2 files changed, 154 insertions(+), 42 deletions(-)

diff --git a/logview.el b/logview.el
index 58c1ab9379..e8ec624e90 100644
--- a/logview.el
+++ b/logview.el
@@ -706,6 +706,7 @@ ENTRY will have its timestamp parsed.")
 (defvar-local logview--timestamp-difference-per-thread-bases nil
   "Either nil or a hash-table of strings to cons cells.
 See `logview--timestamp-difference-base' for details.")
+(defvar-local logview--timestamp-difference-to-section-headers nil)
 
 (defvar-local logview--buffer-target-gap-length nil)
 (defvar-local logview--last-found-large-gap     nil)
@@ -877,9 +878,11 @@ works for \\[logview-set-navigation-view] and 
\\[logview-highlight-view-entries]
     ("Display timestamp differences"
      (logview-difference-to-current-entry                                    
"Show difference to the current entry for all other entries")
      (logview-thread-difference-to-current-entry                             
"Show difference only for the entries of the same thread")
+     (logview-difference-to-section-headers                                  
"Show timestamp differences within each section")
      (logview-go-to-difference-base-entry                                    
"Go to the entry difference to which timestamp is shown")
      (logview-forget-difference-base-entries                                 
"Don’t show timestamp differences")
-     (logview-forget-thread-difference-base-entry                            
"Don’t show timestamp differences for this thread"))
+     (logview-forget-thread-difference-base-entry                            
"Don’t show timestamp differences for this thread")
+     (logview-cancel-difference-to-section-headers                           
"Don’t show timestamp differences to section headers"))
     ("Change options for current buffer"
      (logview-change-target-gap-length                                       
"Set gap length for ‘\\[logview-next-timestamp-gap]’ and similar commands")
      (auto-revert-mode                                                       
"Toggle Auto-Revert mode")
@@ -1161,9 +1164,11 @@ that the line is not the first in the buffer."
                        ;; Entry timestamp commands.
                        ("z a" logview-difference-to-current-entry)
                        ("z t" logview-thread-difference-to-current-entry)
+                       ("z c" logview-difference-to-section-headers)
                        ("z z" logview-go-to-difference-base-entry)
                        ("z A" logview-forget-difference-base-entries)
                        ("z T" logview-forget-thread-difference-base-entry)
+                       ("z C" logview-cancel-difference-to-section-headers)
                        ("z n" logview-next-timestamp-gap)
                        ("z p" logview-previous-timestamp-gap)
                        ("z N" logview-next-timestamp-gap-in-this-thread)
@@ -2133,18 +2138,21 @@ If invoked with prefix argument, make sections 
thread-bound if
 the argument is positive, non-bound otherwise."
   (interactive (list (or current-prefix-arg 'toggle)))
   (logview--assert 'thread)
-  (logview--toggle-option-locally 'logview--sections-thread-bound arg 
(called-interactively-p 'interactive)
-                                  "Sections are now thread-bound and can 
overlap"
-                                  "Sections are not thread-bound anymore and 
are always sequential"))
+  (when (and (logview--toggle-option-locally 'logview--sections-thread-bound 
arg (called-interactively-p 'interactive)
+                                             "Sections are now thread-bound 
and can overlap"
+                                             "Sections are not thread-bound 
anymore and are always sequential")
+             logview--timestamp-difference-to-section-headers)
+    (logview--refontify-buffer)))
 
 (defun logview-sections-thread-bound-p ()
   (and logview--sections-thread-bound (memq 'thread 
logview--submode-features)))
 
 (defun logview--ensure-section-view (set-view-if-needed)
-  (when set-view-if-needed
-    (unless (logview--find-view logview--section-view-name t)
-      (logview--do-set-section-view (logview--choose-view 
(substitute-command-keys
-                                                           "View that defines 
sections (change with `\\[logview-set-section-view]'): "))))))
+  (unless (logview--find-view logview--section-view-name t)
+    (if set-view-if-needed
+        (logview--do-set-section-view (logview--choose-view 
(substitute-command-keys
+                                                             "View that 
defines sections (change with `\\[logview-set-section-view]'): ")))
+      (error "There is no active section view"))))
 
 (defun logview--do-forward-section-as-command (set-view-if-needed n &optional 
go-to-end)
   (logview--assert)
@@ -2402,7 +2410,8 @@ which haven't been manually hidden are visible."
 (defun logview-difference-to-current-entry ()
   "Display difference to current entry's timestamp.
 Difference is shown for all other entries.  Any thread-specific
-difference bases (appointed with 
`\\<logview-mode-map>\\[logview-thread-difference-to-current-entry]') are 
removed."
+difference bases (appointed with 
`\\<logview-mode-map>\\[logview-thread-difference-to-current-entry]') are 
removed and displaying
+of differences to section headers (see 
`\\<logview-mode-map>\\[logview-difference-to-section-headers]') is canceled."
   (interactive)
   (logview--assert 'timestamp)
   (logview--std-temporarily-widening
@@ -2410,15 +2419,20 @@ difference bases (appointed with 
`\\<logview-mode-map>\\[logview-thread-differen
       ;; Make sure that it is parsed.
       (logview--entry-timestamp entry start)
       (let ((base (cons entry start)))
-        (unless (and (equal logview--timestamp-difference-base base) (null 
logview--timestamp-difference-per-thread-bases))
-          (setq logview--timestamp-difference-base             base
-                logview--timestamp-difference-per-thread-bases nil)
+        (unless (and (equal logview--timestamp-difference-base base)
+                     (null logview--timestamp-difference-per-thread-bases)
+                     (not logview--timestamp-difference-to-section-headers))
+          (setf logview--timestamp-difference-base               base
+                logview--timestamp-difference-per-thread-bases   nil
+                logview--timestamp-difference-to-section-headers nil)
           (logview--refontify-buffer))))))
 
 (defun logview-thread-difference-to-current-entry ()
   "Display difference to current entry's timestamp in its thread.
 In case there is a global difference base (appointed with 
`\\<logview-mode-map>\\[logview-difference-to-current-entry]'),
-it stays in effect for other threads."
+it stays in effect for other threads.  Similarly, activated display
+of differences to section headers (see 
`\\<logview-mode-map>\\[logview-difference-to-section-headers]') continues for 
other
+threads."
   (interactive)
   (logview--assert 'timestamp 'thread)
   (logview--std-temporarily-widening
@@ -2427,12 +2441,29 @@ it stays in effect for other threads."
       (logview--entry-timestamp entry start)
       (let ((base   (cons entry start))
             (thread (logview--entry-group entry start logview--thread-group)))
-        (unless (and logview--timestamp-difference-per-thread-bases (equal 
(gethash thread logview--timestamp-difference-per-thread-bases) base))
+        (unless (and logview--timestamp-difference-per-thread-bases
+                     (equal (gethash thread 
logview--timestamp-difference-per-thread-bases) base))
           (unless logview--timestamp-difference-per-thread-bases
             (setq logview--timestamp-difference-per-thread-bases 
(make-hash-table :test #'equal)))
           (puthash thread base logview--timestamp-difference-per-thread-bases)
           (logview--refontify-buffer))))))
 
+(defun logview-difference-to-section-headers (&optional set-view-if-needed)
+  "Display difference to section header timestamp everywhere.
+All global (see `\\<logview-mode-map>\\[logview-difference-to-current-entry]') 
and thread-specific difference bases 
(`\\<logview-mode-map>\\[logview-thread-difference-to-current-entry]')
+are removed."
+  (interactive "p")
+  (logview--assert 'timestamp)
+  (logview--ensure-section-view set-view-if-needed)
+  (logview--std-temporarily-widening
+    (unless (and (null logview--timestamp-difference-base)
+                 (null logview--timestamp-difference-per-thread-bases)
+                 logview--timestamp-difference-to-section-headers)
+          (setf logview--timestamp-difference-base               nil
+                logview--timestamp-difference-per-thread-bases   nil
+                logview--timestamp-difference-to-section-headers t)
+          (logview--refontify-buffer))))
+
 (defun logview-go-to-difference-base-entry ()
   (interactive)
   (logview--assert 'timestamp)
@@ -2442,11 +2473,17 @@ it stays in effect for other threads."
                                 (logview--entry-group entry start 
logview--thread-group)))
              (difference-base (or (when 
logview--timestamp-difference-per-thread-bases
                                     (gethash thread 
logview--timestamp-difference-per-thread-bases))
-                                  logview--timestamp-difference-base)))
+                                  (if 
logview--timestamp-difference-to-section-headers
+                                      (save-excursion
+                                        (logview--forward-section 0)
+                                        (logview--locate-current-entry entry 
start
+                                          (when (funcall (cdr 
logview--section-header-filter) entry start)
+                                            `(,entry . ,start))))
+                                    logview--timestamp-difference-base))))
         (unless difference-base
           (user-error "There is no timestamp difference base for the current 
entry"))
         (when (invisible-p (cdr difference-base))
-          (user-error "Timestamp difference base is either hidden or not in 
the current file contents anymore (e.g. due to log rolling)"))
+          (user-error "Timestamp difference base is either hidden or not in 
the current file contents anymore (e.g. due to log rotation)"))
         (let ((entry (car difference-base))
               (start (cdr difference-base)))
           (unless (and (< start (logview--point-max)) (> (logview--entry-end 
entry start) (logview--point-min)))
@@ -2455,14 +2492,23 @@ it stays in effect for other threads."
           (logview--maybe-pulse-current-entry 'movement))))))
 
 (defun logview-forget-difference-base-entries ()
+  "Don't replace entry timestamps with differences anywhere.
+This cancels effects of commands 
`\\<logview-mode-map>\\[logview-difference-to-current-entry]', 
`\\<logview-mode-map>\\[logview-thread-difference-to-current-entry]' and 
`\\<logview-mode-map>\\[logview-difference-to-section-headers]'."
   (interactive)
   (logview--assert 'timestamp)
-  (unless (and (null logview--timestamp-difference-base) (null 
logview--timestamp-difference-per-thread-bases))
-    (setq logview--timestamp-difference-base             nil
-          logview--timestamp-difference-per-thread-bases nil)
+  (unless (and (null logview--timestamp-difference-base)
+               (null logview--timestamp-difference-per-thread-bases)
+               (not  logview--timestamp-difference-to-section-headers))
+    (setf logview--timestamp-difference-base               nil
+          logview--timestamp-difference-per-thread-bases   nil
+          logview--timestamp-difference-to-section-headers nil)
     (logview--refontify-buffer)))
 
 (defun logview-forget-thread-difference-base-entry ()
+  "Don't replace entry timestamps with differences in this thread.
+However, if there is a global replacement in effect (i.e. not
+specific to this thread: see commands 
`\\<logview-mode-map>\\[logview-difference-to-current-entry]' and 
`\\<logview-mode-map>\\[logview-difference-to-section-headers]'), it will
+get activated instead."
   (interactive)
   (logview--assert 'timestamp 'thread)
   (logview--std-temporarily-widening
@@ -2475,6 +2521,17 @@ it stays in effect for other threads."
         (remhash thread logview--timestamp-difference-per-thread-bases)
         (logview--refontify-buffer)))))
 
+(defun logview-cancel-difference-to-section-headers ()
+  "Don't replace entry timestamps with differences to section headers.
+If section headers are not used as timestamp bases (see command 
`\\<logview-mode-map>\\[logview-difference-to-section-headers]'),
+this command doesn't do anything."
+  (interactive)
+  (logview--assert 'timestamp)
+  (if logview--timestamp-difference-to-section-headers
+      (progn (setf logview--timestamp-difference-to-section-headers nil)
+             (logview--refontify-buffer))
+    (user-error "Not showing timestamp differences to section headers, nothing 
to cancel")))
+
 
 
 ;;; Timestamp gap commands.
@@ -2684,12 +2741,14 @@ These are:
                                      "*Customize Logview Submodes*"))
 
 (defun logview--toggle-option-locally (variable arg &optional show-message 
message-if-true message-if-false)
-  (set (make-local-variable variable)
-       (if (eq arg 'toggle)
-           (not (symbol-value variable))
-         (> (prefix-numeric-value arg) 0)))
-  (when show-message
-    (message (if (symbol-value variable) message-if-true message-if-false))))
+  (let ((new-value (if (eq arg 'toggle)
+                       (not (symbol-value variable))
+                     (> (prefix-numeric-value arg) 0))))
+    (unless (eq new-value (symbol-value variable))
+      (set (make-local-variable variable) new-value)
+      (when show-message
+        (message (if (symbol-value variable) message-if-true 
message-if-false)))
+      t)))
 
 
 
@@ -3895,25 +3954,29 @@ This list is preserved across Emacs session in
           (save-match-data
             (let ((region-start (cdr (logview--do-locate-current-entry 
region-start))))
               (when region-start
-                (let* ((have-timestamp              (memq 'timestamp 
logview--submode-features))
-                       (have-level                  (memq 'level     
logview--submode-features))
-                       (have-name                   (memq 'name      
logview--submode-features))
-                       (have-thread                 (memq 'thread    
logview--submode-features))
-                       (validator                   (cdr 
logview--effective-filter))
-                       (common-difference-base      
logview--timestamp-difference-base)
-                       (difference-bases-per-thread 
logview--timestamp-difference-per-thread-bases)
-                       (displaying-differences      (or common-difference-base 
difference-bases-per-thread))
-                       (difference-format-string    
logview--timestamp-difference-format-string)
-                       (header-filter               (cdr 
logview--section-header-filter))
-                       (highlighter                 (cdr 
logview--highlighted-filter))
-                       (highlighted-part            
logview-highlighted-entry-part))
+                (let* ((have-timestamp                (memq 'timestamp 
logview--submode-features))
+                       (have-level                    (memq 'level     
logview--submode-features))
+                       (have-name                     (memq 'name      
logview--submode-features))
+                       (have-thread                   (memq 'thread    
logview--submode-features))
+                       (validator                     (cdr 
logview--effective-filter))
+                       (difference-to-section-headers 
logview--timestamp-difference-to-section-headers)
+                       (sections-thread-bound         (when 
difference-to-section-headers (logview-sections-thread-bound-p)))
+                       (common-difference-base        (or 
logview--timestamp-difference-base
+                                                          (when (and 
difference-to-section-headers (not sections-thread-bound)) :unknown)))
+                       (difference-bases-per-thread   (or 
logview--timestamp-difference-per-thread-bases
+                                                          (when (and 
difference-to-section-headers sections-thread-bound)       (make-hash-table 
:test #'equal))))
+                       (displaying-differences        (or 
common-difference-base difference-bases-per-thread))
+                       (difference-format-string      
logview--timestamp-difference-format-string)
+                       (header-filter                 (cdr 
logview--section-header-filter))
+                       (highlighter                   (cdr 
logview--highlighted-filter))
+                       (highlighted-part              
logview-highlighted-entry-part))
                   (logview--iterate-entries-forward
                    region-start
                    (lambda (entry start)
                      (let ((end (logview--entry-end entry start))
                            filtered)
                        (if (or (null validator) (funcall validator entry 
start))
-                           (progn
+                           (let ((header-entry (and header-filter (funcall 
header-filter entry start))))
                              (when have-level
                                (let ((entry-faces (aref 
logview--submode-level-faces (logview--entry-level entry))))
                                  (put-text-property start end 'face (car 
entry-faces))
@@ -3921,14 +3984,32 @@ This list is preserved across Emacs session in
                                                          
(logview--entry-group-end   entry start logview--level-group)
                                                          (cdr entry-faces))))
                              (when have-timestamp
-                               (let ((from (logview--entry-group-start entry 
start logview--timestamp-group))
-                                     (to   (logview--entry-group-end   entry 
start logview--timestamp-group))
+                               (let ((from   (logview--entry-group-start entry 
start logview--timestamp-group))
+                                     (to     (logview--entry-group-end   entry 
start logview--timestamp-group))
+                                     (thread (when difference-bases-per-thread 
(logview--entry-group entry start logview--thread-group)))
                                      timestamp-replaced)
                                  (add-face-text-property from to 
'logview-timestamp)
                                  (when displaying-differences
+                                   (when (and header-entry 
difference-to-section-headers)
+                                     (let ((base `(,entry . ,start)))
+                                       (if difference-bases-per-thread
+                                           (puthash thread base 
difference-bases-per-thread)
+                                         (setf common-difference-base base))))
                                    (let ((difference-base (or (when 
difference-bases-per-thread
-                                                                (gethash 
(logview--entry-group entry start logview--thread-group) 
difference-bases-per-thread))
+                                                                (gethash 
thread difference-bases-per-thread (when difference-bases-per-thread :unknown)))
                                                               
common-difference-base)))
+                                     ;; This is possible only when displaying 
differences to section header.
+                                     ;; Means that we don't yet know where the 
header is.
+                                     (when (eq difference-base :unknown)
+                                       ;; FIXME: Might need to improve 
performance here, e.g. using cache.
+                                       (save-excursion
+                                         (goto-char start)
+                                         (logview--forward-section 0)
+                                         (logview--locate-current-entry entry 
start
+                                           (setf difference-base (when 
(funcall (cdr logview--section-header-filter) entry start) `(,entry . ,start)))
+                                           (if difference-bases-per-thread
+                                               (puthash thread difference-base 
difference-bases-per-thread)
+                                             (setf common-difference-base 
difference-base)))))
                                      ;; Hide timestamp with time difference if 
there is a difference
                                      ;; base entry and we are not positioned 
over it right now.
                                      (when (and difference-base (not (and (= 
(cdr difference-base) start)
@@ -3957,7 +4038,7 @@ This list is preserved across Emacs session in
                                (add-face-text-property 
(logview--entry-group-start entry start logview--thread-group)
                                                        
(logview--entry-group-end   entry start logview--thread-group)
                                                        'logview-thread))
-                             (when (and header-filter (funcall header-filter 
entry start))
+                             (when header-entry
                                (add-face-text-property start end 
'logview-section))
                              (when (and highlighter (funcall highlighter entry 
start))
                                (add-face-text-property (if (eq 
highlighted-part 'message) (logview--entry-message-start entry start) start)
diff --git a/test/logview.el b/test/logview.el
index 45d4df2d02..c556f8845d 100644
--- a/test/logview.el
+++ b/test/logview.el
@@ -691,6 +691,37 @@ buffer if the test needs that."
                 (logview--subtest t (logview-last-section-any-thread)
                   (should (string= (logview--test-current-message) "serving 
request 4 (in a different thread)")))))))))))
 
+(ert-deftest logview-difference-to-section-headers ()
+  (logview--test-with-file "log4j/sections-1.log"
+    :extra-customizations (logview--test-view-customizations '(:name 
"sections" :filters "lv INFO\na+ my\\.Server\nm+ serving request"))
+    (logview-set-section-view "sections")
+    (logview-difference-to-section-headers)
+    ;; Entries before any sections, timestamps must not be replaced.
+    (should (string-match-p (rx bol "2010-03-10 20:03:44.000 [thread 1] DEBUG 
my.Startup - starting up" eol)
+                            (logview--test-user-visible-buffer-string)))
+    (should (string-match-p (rx bol "2010-03-10 20:03:44.100 [thread 1] DEBUG 
my.Class - before any sections" eol)
+                            (logview--test-user-visible-buffer-string)))
+    ;; Section headers, timestamps must not be replaced.
+    (should (string-match-p (rx bol "2010-03-10 20:03:45.000 [thread 1] INFO 
my.Server - serving request 1" eol)
+                            (logview--test-user-visible-buffer-string)))
+    (should (string-match-p (rx bol "2010-03-10 20:03:46.100 [thread 2] INFO 
my.Server - serving request 3 (in a different thread)" eol)
+                            (logview--test-user-visible-buffer-string)))
+    ;; Entries withing sections, timestamps must be shown as difference to the 
corresponding section start.
+    (should (string-match-p (rx bol "                 +0.100 [thread 1] DEBUG 
my.Class - inside section 1" eol)
+                            (logview--test-user-visible-buffer-string)))
+    (should (string-match-p (rx bol "                 +0.300 [thread 1] DEBUG 
my.Class - inside section 2" eol)
+                            (logview--test-user-visible-buffer-string)))
+    (should (string-match-p (rx bol "                 +0.300 [thread 2] DEBUG 
my.Class - doing stuff (section 3)" eol)
+                            (logview--test-user-visible-buffer-string)))
+    (should (string-match-p (rx bol "                 +0.400 [thread 2] DEBUG 
my.Class - doing more stuff (section 4)" eol)
+                            (logview--test-user-visible-buffer-string)))
+    ;; This must cause immediate difference rebuilding.
+    (logview-toggle-sections-thread-bound 0)
+    ;; This entry must be seen as belonging to a different section now 
("serving request 3
+    ;; (in a different thread)").
+    (should (string-match-p (rx bol "                 +0.200 [thread 1] DEBUG 
my.Class - inside section 2" eol)
+                            (logview--test-user-visible-buffer-string)))))
+
 
 (ert-deftest logview-view-editing-1 ()
   (logview--test-with-file "log4j/en-1.log"

Reply via email to