emacs-diffs
[Top][All Lists]
Advanced

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

master b1c1ff9ee1: docview: imenu access to table of contents


From: Lars Ingebrigtsen
Subject: master b1c1ff9ee1: docview: imenu access to table of contents
Date: Tue, 27 Sep 2022 07:53:56 -0400 (EDT)

branch: master
commit b1c1ff9ee110a9fe739dc4407468f3e9b819484c
Author: Jose A. Ortega Ruiz <jao@gnu.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    docview: imenu access to table of contents
    
    * lisp/doc-view.el (doc-view-imenu-enabled): user option to disable
    imenu generation.
    * lisp/doc-view.el (doc-view--outline-rx):
    (doc-view--pdf-outline, doc-view--imenu-subtree, doc-view-imenu-index):
    functions implementing the imenu index generation via mutool.
    * lisp/doc-view.el (doc-view-imenu-setup, doc-view-mode): setup of the
    new functionality in doc-view mode.
    * lisp/imenu.el (imenu-submenus-on-top):
    (imenu--split-menu): new local variable to optionally inhibit
    grouping of entries with children at the top of imenu menus.
    * doc/emacs/misc.texi: documentation for the new functionality
    (bug#58103).
---
 doc/emacs/misc.texi |  7 ++++++
 lisp/doc-view.el    | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lisp/imenu.el       | 16 +++++++++---
 3 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 10b44028bb..f3a6e6c33b 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -584,6 +584,13 @@ you instead want the image to be re-rendered at the new 
size, set
 default size for DocView, customize the variable
 @code{doc-view-resolution}.
 
+@vindex doc-view-imenu-enabled
+  When the @command{mutool} executable is available, DocView will use
+to generate entries for an outline menu, making it accessible via the
+imenu facility (@pxref{Imenu}).  To disable this functionality even
+when @command{mutool} can be found in your @code{exec-path}, customize
+the variable @code{doc-view-imenu-enabled}.
+
 @node DocView Searching
 @subsection DocView Searching
 
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index fbd1427946..fe772efcfc 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -214,6 +214,11 @@ are available (see Info node `(emacs)Document View')."
   :type 'boolean
   :version "29.1")
 
+(defcustom doc-view-imenu-enabled (and (executable-find "mutool") t)
+  "Whether to generate an imenu outline when mutool is available."
+  :type 'boolean
+  :version "29.1")
+
 (defcustom doc-view-svg-background "white"
   "Background color for svg images.
 See `doc-view-mupdf-use-svg'."
@@ -1874,6 +1879,69 @@ If BACKWARD is non-nil, jump to the previous match."
             (y-or-n-p "No more matches before current page.  Wrap to last 
match? "))
        (doc-view-goto-page (caar (last doc-view--current-search-matches)))))))
 
+;;;; Imenu support
+(defconst doc-view--outline-rx
+  "[^\t]+\\(\t+\\)\"\\(.+\\)\"\t#\\(?:page=\\)?\\([0-9]+\\)")
+
+(defun doc-view--pdf-outline (&optional file-name)
+  "Return a describing the outline of FILE-NAME (or current if nil).
+
+Each element in the 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 . ,(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.
+
+This auxliary function constructs recursively all the items for
+the first node in the outline and all its siblings at the same
+level.  Returns that imenu alist together with any other pending outline
+entries at an upper level."
+  (let ((level (alist-get 'level (car outline)))
+        (index nil))
+    (while (and (car outline) (<= level (alist-get 'level (car outline))))
+      (let-alist (car outline)
+        (let ((title (format "%s (%s)" .title .page)))
+          (if (> .level level)
+              (let ((sub (doc-view--imenu-subtree outline act))
+                    (fst (car index)))
+                (setq index (cdr index))
+                (push (cons (car fst) (cons fst (car sub))) index)
+                (setq outline (cdr sub)))
+            (push `(,title 0 ,act ,.page) index)
+            (setq outline (cdr outline))))))
+    (cons (nreverse index) outline)))
+
+(defun doc-view-imenu-index (&optional file-name goto-page-fn)
+  "Create an imenu index using mutool to extract its outline.
+
+For extensibility, a FILE-NAME other than the current buffer and
+a jumping function, GOTO-PAGE-FN other than `doc-view-goto-page'
+can be specified."
+  (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))))
+
+(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")))
+
 ;;;; User interface commands and the mode
 
 (put 'doc-view-mode 'mode-class 'special)
@@ -2047,7 +2115,7 @@ If BACKWARD is non-nil, jump to the previous match."
   "Major mode in DocView buffers.
 
 DocView Mode is an Emacs document viewer.  It displays PDF, PS
-and DVI files (as PNG images) in Emacs buffers.
+and DVI files (as PNG or SVG images) in Emacs buffers.
 
 You can use \\<doc-view-mode-map>\\[doc-view-toggle-display] to
 toggle between displaying the document or editing it as text.
@@ -2142,6 +2210,7 @@ toggle between displaying the document or editing it as 
text.
     (setq mode-name "DocView"
          buffer-read-only t
          major-mode 'doc-view-mode)
+    (doc-view-imenu-setup)
     (doc-view-initiate-display)
     ;; Switch off view-mode explicitly, because doc-view-mode is the
     ;; canonical view mode for PDF/PS/DVI files.  This could be
diff --git a/lisp/imenu.el b/lisp/imenu.el
index c407f501d6..bfc2429100 100644
--- a/lisp/imenu.el
+++ b/lisp/imenu.el
@@ -208,6 +208,13 @@ called within a `save-excursion'.
 
 See `imenu--index-alist' for the format of the buffer index alist.")
 
+;;;###autoload
+(defvar-local imenu-submenus-on-top t
+  "Flag specifiying whether items with sublists should be kept at top.
+
+For some indexes, such as those describing sections in a document, it
+makes sense to keep their original order even in the menubar.")
+
 ;;;###autoload
 (defvar-local imenu-prev-index-position-function 'beginning-of-defun
   "Function for finding the next index position.
@@ -373,10 +380,11 @@ The returned alist DOES NOT share structure with 
MENULIST."
     (if (memq imenu--rescan-item menulist)
        (setq keep-at-top (list imenu--rescan-item)
              menulist (delq imenu--rescan-item menulist)))
-    (dolist (item menulist)
-      (when (imenu--subalist-p item)
-       (push item keep-at-top)
-       (setq menulist (delq item menulist))))
+    (if imenu-submenus-on-top
+        (dolist (item menulist)
+          (when (imenu--subalist-p item)
+           (push item keep-at-top)
+           (setq menulist (delq item menulist)))))
     (if imenu-sort-function
        (setq menulist (sort menulist imenu-sort-function)))
     (if (> (length menulist) imenu-max-items)



reply via email to

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