1

When I create an imenu with the help of the lisp snippet

(defun my-index-function ()
  (message "Running `my-index-function'.")
  '(("1" . 1) ("1.1" ("1.1.1" . 2) ("1.1.2" . 3) ("1.1.3" . 4)) ("1.2" . 5)))
(setq imenu-create-index-function #'my-index-function)
(imenu-add-to-menubar "index menu")

I get an index menu with the items

  • 1.1
    • 1.1.1
    • 1.1.2
    • 1.1.3
  • 1
  • 1.2

But this does not correspond to the wanted order of elements in the list returned by my-index-function – which is

  • 1
  • 1.1
    • 1.1.1
    • 1.1.2
    • 1.1.3
  • 1.2

What am I missing? Is this some special feature of imenu or is it just a bug? How can I get the wanted order in the index menu?

emacs-version: GNU Emacs 25.1.1


The real application is adding imenu-support to reftex. Below you find the first rough test version which works for a single-LaTeX-source buffer.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; imenu-support for reftex

(require 'imenu)

(unless (fboundp #'substring-no-indent)
  (defun substring-no-indent (str)
    "Return STR without its leading whitespace."
    (substring str (string-match "[^[:space:]]" str))))

(defun reftex-outline-tree-recursive (docstruct &optional level)
  "Returns a cons of a tree generated from DOCSTRUCT and the rest of DOCSTRUCT to be processed.
The initial DOCSTRUCT should be the value of the symbol stored at `reftex-docstruct-symbol'."
  (unless (equal (caar docstruct) 'toc)
    (setq docstruct (cl-remove-if-not (lambda (entry)
                    (equal (car entry) 'toc))
                      docstruct)))
  (unless level
    (setq level (nth 5 (car docstruct))))
  (let (tree)
    (cl-block nil
      (while docstruct
    (let* ((entry (car docstruct))
           (entry-level (nth 5 entry))
           (next-entry (car-safe (cdr docstruct)))
           (next-entry-level (nth 5 next-entry))
           leaf)
      (cond
       ((or (null (numberp entry-level))
        (< entry-level level))
        (cl-return))
       ((and (numberp next-entry-level) (> next-entry-level entry-level))
        (cl-multiple-value-setq (leaf docstruct) (reftex-outline-tree-recursive (cdr docstruct)))
        (setq leaf (cons (cons "-\"-" (car (last entry))) leaf)) ; prepend the section itself
        (setq tree (cons (cons (substring-no-indent (nth 2 entry)) leaf) tree)))
       (t ; entry-level == level
        (setq tree (cons (cons (substring-no-indent (nth 2 entry)) (car (last entry))) tree)
          docstruct (cdr docstruct)))))))
    (list (nreverse tree) docstruct)))

(defun reftex-outline-tree (docstruct)
  "Returns the toc tree generated from DOCSTRUCT as stored as value of the symbol stored at `reftex-docstruct-symbol'."
  (let (subtree tree)
    (while docstruct
      (cl-multiple-value-setq (subtree docstruct) (reftex-outline-tree-recursive docstruct))
      (setq tree (append tree subtree)))
    tree))

(defcustom reftex-outline-flat-default nil
  "The index menu \"TeX Outline\" has a flat structure if this option is true."
  :group 'reftex-table-of-contents-browser
  :type 'boolean)

(defvar reftex-outline-flat reftex-outline-flat-default
  "The index menu \"TeX Outline\" has a flat structure if this option is true.
The default is read from `reftex-outline-flat-default'.")

(defun reftex-outline ()
  "Add the index of the TeX document."
  (interactive)
  (reftex-access-scan-info t)
  (let ((docstruct (cl-remove-if-not (lambda (entry)
                       (equal (car entry) 'toc))
                     (symbol-value reftex-docstruct-symbol))))
    (if reftex-outline-flat
    (mapcar (lambda (item)
          (cons (format "%s" (nth 2 item))
            (car (last item))))
        docstruct)
      (reftex-outline-tree docstruct))))

(defun reftex-toggle-outline-flat (&optional force)
  "Toggle `reftex-outline-flat'.
With prefix arg > 0 switch flat outline on. With prefix arg <= 0 switch it off."
  (interactive "p")
  (if (numberp force)
      (setq reftex-outline-flat (> force 0))
    (setq reftex-outline-flat (null reftex-outline-flat))))

(defun reftex-imenu-update-advice (&rest args)
  "Add \"Flat Outline\" to \"TeX Outline\" menu."
  (ignore args)
  (when (and imenu--index-alist reftex-docstruct-symbol)
    (easy-menu-add-item imenu--menubar-keymap nil ["Flat Outline" (progn (reftex-toggle-outline-flat) (imenu--menubar-select '("*Rescan*" . -99))) :style toggle :selected reftex-outline-flat] "*Rescan*")))

(defun reftex-outline-imenu-enable ()
  "Enable imenu in the current TeX buffer."
  (interactive)
  (setq-local imenu-create-index-function
          #'reftex-outline)
  (imenu-add-to-menubar "TeX Outline")
  (advice-add #'imenu-update-menubar :after #'reftex-imenu-update-advice))

(defun reftex-outline-imenu-disable ()
  "Disable imenu in the current PDF buffer."
  (interactive)
  (setq-local imenu-create-index-function nil)
  (local-set-key [menu-bar index] nil))

This gives the index entry -"- for the start of the first sub-section after the first sub-sub-section for the following example:

\documentclass{article}

\begin{document}
\section{1}
\subsection{11}
\subsubsection{111}
\subsubsection{112}
\subsection{12}
\end{document}

%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End:

I know about imenu-add-menubar-index but I don't like the splitting of the table of contents into groups. I like better if imenu creates sub-menus for subsections.

5
  • 1
    I had to solve a similar problem in pdf-tools.
    – politza
    Mar 30 '17 at 12:52
  • @politza Thanks a lot. I almost copied your solution and added it at the end of the question. I did not answer my own question to give you the opportunity to write a short answer. You can just combine your comment text with a pointer to the answer at the end of the question. I would accept and upvote your answer. Thanks again. By the way, pdf-tools is cool! (I got it running under Cygwin.)
    – Tobias
    Mar 30 '17 at 13:11
  • 1
    @politza Have you considered to issue a feature-request at emacs-devel@gnu.org to make the sorting in imenu optional?
    – Tobias
    Mar 30 '17 at 13:17
  • No, I haven't. And please feel free to answer your own question.
    – politza
    Mar 30 '17 at 14:07
  • @politza Okay. Then I move the solution from the question to an answer.
    – Tobias
    Mar 30 '17 at 14:09
1

The comment of user poliza provided the following solution based on pdf-outline--imenu--split-menu from pdf-tools.

;; See http://emacs.stackexchange.com/questions/31791/order-of-items-in-imenu?noredirect=1#comment48799_31791
(advice-add #'imenu--split-menu :around #'reftex-outline-imenu--split-menu)

(defun reftex-outline-imenu--split-menu (oldfun menulist title)
  "Replacement function for `imenu--split-menu'.
This function does not move sub-menus to the top, therefore
keeping the original outline order of the document.  Also it does
not call `imenu-sort-function'."
  (if (reftex-outline-p)
      (let ((menulist (copy-sequence menulist))
        keep-at-top)
    (if (memq imenu--rescan-item menulist)
        (setq keep-at-top (list imenu--rescan-item)
          menulist (delq imenu--rescan-item menulist)))
    (if (> (length menulist) imenu-max-items)
        (setq menulist
          (mapcar
           (lambda (menu)
             (cons (format "From: %s" (caar menu)) menu))
           (imenu--split menulist imenu-max-items))))
    (cons title
          (nconc (nreverse keep-at-top) menulist)))
    (funcall oldfun menulist title)))
1
  • If this solves your problem, could you accept the answer, please?
    – Dan
    Aug 27 '17 at 17:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.