Subject: [PATCH] docview: fixes for imenu generation

From: Jose A Ortega Ruiz <[email-masked]>
Date: Thu, 29 Sep 2022 20:51:45 +0100

Tags: patch

As mentioned in the commit log, a fix for a bug i introduced in my
previous changes, and also for the bad behaviour observed by Daniel for
documents without an outline (or an associated buffer-file-name for that
matter).

In GNU Emacs 29.0.50 (build 20, x86_64-pc-linux-gnu, cairo version
 1.16.0) of 2022-09-27 built on rivendell
Repository revision: 7368cdd359325cb6ed83688178ae4b4eaf22f4d5
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12101004
System Description: Debian GNU/Linux bookworm/sid

Configured using:
 'configure --prefix=/usr/local/stow/emacs29 --with-x-toolkit=no
 --with-imagemagick -C'

>From d2d2d829c9b8daf2cb0aca15cd58bca4679ea192 Mon Sep 17 00:00:00 2001
From: Jose A Ortega Ruiz <[email-masked]>
Date: Thu, 29 Sep 2022 20:19:11 +0100
Subject: [PATCH] docview: fixes for imenu generation

* lisp/doc-view.el: (doc-view--pdf-outline):
(doc-view-imenu-index):
(doc-view-imenu-setup): fix multiple empty index generation for
documents without an outline, caching the result (see discussion in
bug#58103).
(doc-view--imenu-subtree): fix for nested imenus (bug introduced in
commit fe002cc8ce)
---
 lisp/doc-view.el | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index 80c4fd21de..b1ea90c212 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -1900,6 +1900,9 @@ doc-view-search-previous-match
 (defconst doc-view--outline-rx
   "[^\t]+\\(\t+\\)\"\\(.+\\)\"\t#\\(?:page=\\)?\\([0-9]+\\)")
 
+(defvar-local doc-view--outline nil
+  "Cached PDF outline, so that it is only computed once per document.")
+
 (defun doc-view--pdf-outline (&optional file-name)
   "Return a list describing the outline of FILE-NAME.
 Return a list describing the current file if FILE-NAME is nil.
@@ -1907,19 +1910,20 @@ doc-view--pdf-outline
 Each element in the returned list contains information about a section's
 title, nesting level and page number.  The list is flat: its tree
 structure is extracted by `doc-view--imenu-subtree'."
-  (let* ((outline nil)
-         (fn (or file-name (buffer-file-name)))
-         (fn (shell-quote-argument (expand-file-name fn))))
-    (with-temp-buffer
-      (insert (shell-command-to-string (format "mutool show %s outline" fn)))
-      (goto-char (point-min))
-      (while (re-search-forward doc-view--outline-rx nil t)
-        (push `((level . ,(length (match-string 1)))
-                (title . ,(replace-regexp-in-string "\\\\[rt]" " "
-                                                    (match-string 2)))
-                (page . ,(string-to-number (match-string 3))))
-              outline)))
-    (nreverse outline)))
+  (let ((fn (or file-name (buffer-file-name))))
+    (when fn
+      (let ((outline nil)
+            (fn (shell-quote-argument (expand-file-name fn))))
+        (with-temp-buffer
+          (insert (shell-command-to-string (format "mutool show %s outline" fn)))
+          (goto-char (point-min))
+          (while (re-search-forward doc-view--outline-rx nil t)
+            (push `((level . ,(length (match-string 1)))
+                    (title . ,(replace-regexp-in-string "\\\\[rt]" " "
+                                                        (match-string 2)))
+                    (page . ,(string-to-number (match-string 3))))
+                  outline)))
+        (nreverse outline)))))
 
 (defun doc-view--imenu-subtree (outline act)
   "Construct a tree of imenu items for the given outline list and action.
@@ -1932,7 +1936,8 @@ doc-view--imenu-subtree
         (nested (not doc-view-imenu-flatten))
         (index nil))
     (while (and (car outline)
-                (or nested (<= level (alist-get 'level (car outline)))))
+                (or (not nested)
+                    (<= level (alist-get 'level (car outline)))))
       (let-alist (car outline)
         (let ((title (format-spec doc-view-imenu-title-format
                                   `((?t . ,.title) (?p . ,.page)))))
@@ -1953,16 +1958,18 @@ doc-view-imenu-index
 the buffer other than the current buffer, and a jumping function
 GOTO-PAGE-FN other than `doc-view-goto-page'."
   (let* ((goto (or goto-page-fn 'doc-view-goto-page))
-         (act (lambda (_name _pos page) (funcall goto page))))
-    (car (doc-view--imenu-subtree (doc-view--pdf-outline file-name) act))))
+         (act (lambda (_name _pos page) (funcall goto page)))
+         (outline (or doc-view--outline (doc-view--pdf-outline file-name))))
+    (car (doc-view--imenu-subtree outline act))))
 
 (defun doc-view-imenu-setup ()
   "Set up local state in the current buffer for imenu, if needed."
   (when (and doc-view-imenu-enabled (executable-find "mutool"))
     (setq-local imenu-create-index-function #'doc-view-imenu-index
                 imenu-submenus-on-top nil
-                imenu-sort-function nil)
-    (imenu-add-to-menubar "Outline")))
+                imenu-sort-function nil
+                doc-view--outline (doc-view--pdf-outline))
+    (when doc-view--outline (imenu-add-to-menubar "Outline"))))
 
 ;;;; User interface commands and the mode
 
-- 
2.37.2

-- 
Some stories are true that never happened.
 -Elie Wiesel, writer, Nobel laureate (b. 1928)