]> git.eshelyaron.com Git - emacs.git/commitdiff
* lisp/outline.el: Improve new feature of preserving outlines after revert.
authorJuri Linkov <juri@linkov.net>
Mon, 10 Jun 2024 06:34:15 +0000 (09:34 +0300)
committerEshel Yaron <me@eshelyaron.com>
Mon, 10 Jun 2024 07:31:34 +0000 (09:31 +0200)
(outline-minor-mode): Move adding hook 'outline-revert-buffer-rehighlight'
to the same code branch that calls 'outline-minor-mode-highlight-buffer'.
(outline-revert-buffer-rehighlight): Remove same conditions
already existing in 'outline-minor-mode'.
(outline-hidden-headings-regexp): Remove function.
(outline-hidden-headings-paths)
(outline-hidden-headings-restore-paths): New functions
that save and restore complete paths instead of flat regexps.
(outline-revert-buffer-restore-visibility): Use
'outline-hidden-headings-paths' and
'outline-hidden-headings-restore-paths'.

(cherry picked from commit 1a5aa16066bb5180eb92d9c0bfc8cb2c0ce4a4d0)

lisp/outline.el

index 4d74b30dba453db4d98dd18e7ba740c79abe6e5f..c19ee9cf0f6af9d6a7cf6462eb5eb241b702e865 100644 (file)
@@ -574,7 +574,10 @@ See the command `outline-mode' for more information on this mode."
               (progn
                 (font-lock-add-keywords nil outline-font-lock-keywords t)
                 (font-lock-flush))
-            (outline-minor-mode-highlight-buffer)))
+            (progn
+              (outline-minor-mode-highlight-buffer)
+              (add-hook 'revert-buffer-restore-functions
+                        #'outline-revert-buffer-rehighlight nil t))))
         (outline--fix-up-all-buttons)
        ;; Turn off this mode if we change major modes.
        (add-hook 'change-major-mode-hook
@@ -582,8 +585,6 @@ See the command `outline-mode' for more information on this mode."
                  nil t)
         (add-hook 'revert-buffer-restore-functions
                   #'outline-revert-buffer-restore-visibility nil t)
-        (add-hook 'revert-buffer-restore-functions
-                  #'outline-revert-buffer-rehighlight nil t)
         (setq-local line-move-ignore-invisible t)
        ;; Cause use of ellipses for invisible text.
        (add-to-invisibility-spec '(outline . t))
@@ -1696,44 +1697,63 @@ LEVEL, decides of subtree visibility according to
      (point-min) (point-max)))
   (run-hooks 'outline-view-change-hook))
 
-(defun outline-hidden-headings-regexp ()
-  "Return a regexp that matches all currently hidden outlines.
-This is useful to save the hidden outlines and restore them later,
-for example, after reverting the buffer."
-  (let ((headings))
+(defun outline-hidden-headings-paths ()
+  "Return a hash with headings of currently hidden outlines.
+Every hash key is a list whose elements compose a complete path
+of headings descending from the top level down to the bottom level.
+This is useful to save the hidden outlines and restore them later
+after reverting the buffer."
+  (let ((paths (make-hash-table :test #'equal))
+        current-path)
     (outline-map-region
      (lambda ()
-       (when (save-excursion
-               (outline-end-of-heading)
-               (seq-some (lambda (o) (eq (overlay-get o 'invisible)
-                                         'outline))
-                         (overlays-at (point))))
-         (push (buffer-substring (pos-bol) (pos-eol)) headings)))
+       (let* ((level (funcall outline-level))
+              (heading (buffer-substring-no-properties (pos-bol) (pos-eol)))
+              path)
+         (while (and current-path (>= (cdar current-path) level))
+           (pop current-path))
+         (push (cons heading level) current-path)
+         (when (save-excursion
+                 (outline-end-of-heading)
+                 (seq-some (lambda (o) (eq (overlay-get o 'invisible)
+                                           'outline))
+                           (overlays-at (point))))
+           (setf (gethash (mapcar #'car current-path) paths) t))))
      (point-min) (point-max))
-    (when headings
-      (mapconcat (lambda (heading)
-                   (concat "\\`" (regexp-quote heading) "\\'"))
-                 (nreverse headings) "\\|"))))
+    paths))
+
+(defun outline-hidden-headings-restore-paths (paths)
+  "Restore hidden outlines from a hash of hidden headings.
+This is useful after reverting the buffer to restore the outlines
+hidden by `outline-hidden-headings-paths'."
+  (let (current-path outline-view-change-hook)
+    (outline-map-region
+     (lambda ()
+       (let* ((level (funcall outline-level))
+              (heading (buffer-substring (pos-bol) (pos-eol)))
+              path)
+         (while (and current-path (>= (cdar current-path) level))
+           (pop current-path))
+         (push (cons heading level) current-path)
+         (when (gethash (mapcar #'car current-path) paths)
+           (outline-hide-subtree))))
+     (point-min) (point-max))))
 
 (defun outline-revert-buffer-restore-visibility ()
   "Preserve visibility when reverting buffer under `outline-minor-mode'.
 This function restores the visibility of outlines after the buffer
 under `outline-minor-mode' is reverted by `revert-buffer'."
-  (let ((regexp (outline-hidden-headings-regexp)))
-    (when regexp
+  (let ((paths (outline-hidden-headings-paths)))
+    (unless (hash-table-empty-p paths)
       (lambda ()
-        (outline-hide-by-heading-regexp regexp)))))
+        (outline-hidden-headings-restore-paths paths)))))
 
 (defun outline-revert-buffer-rehighlight ()
   "Rehighlight outlines when reverting buffer under `outline-minor-mode'.
 This function rehighlights outlines after the buffer under
 `outline-minor-mode' is reverted by `revert-buffer' when font-lock
 can't update highlighting for `outline-minor-mode-highlight'."
-  (when (and outline-minor-mode-highlight
-             (not (and global-font-lock-mode
-                       (font-lock-specified-p major-mode))))
-    (lambda ()
-      (outline-minor-mode-highlight-buffer))))
+  (lambda () (outline-minor-mode-highlight-buffer)))
 
 \f
 ;;; Visibility cycling