3

I would like to define my own rules for what a section of a document is, then use this so I can popup a list of sections I can auto-complete or use arrow keys to jump between them.

Take a simple example where sections match against ^SECTION\s(.*), how would this be possible?

eg:

Some Text

SECTION Section One

Some More text

SECTION Section Two

Other text.

How could I make a menu that pops up all sections, and activating the item navigates the cursor to the selected section?


Note that I'm not sure if this question is spesific to ivy.

2 Answers 2

4

This is exactly what Imenu is for.

You can define positions to jump to using a menu. The menu can be in the menu-bar or popup, or you can use completion against it as a popup (e.g. in buffer *Completions*).

See the Emacs Wiki page linked above - it introduces multiple ways of using Imenu.

Imenu+, for instance, offers a few improvements for Imenu, such as better sorting of submenus and menu items, and toggling sorting and case-sensitivity.

For Elisp information about it see also the Elisp manual, node Imenu and its subnodes.

The general idea is that you set buffer-local variable imenu-generic-expression in a given buffer, to organize sections there to access by menu. The variable value is a list of elements (MENU-TITLE REGEXP INDEX), where REGEXP recognizes (defines) a section.


If you use Icicles then you can use completion (so different keys) to navigate among or search Imenu items - see Icicles Imenu.

8
  • Does Imenu allow me to do this in a way that can be assigned to a key? - so one function can initialize one list, another function a different kind of imenu? - I ask this because the docs read to me as if defining the imenu regex will prevent regular imenu from being used.
    – ideasman42
    Commented Feb 10, 2018 at 23:56
  • Sorry, I don't understand your followup question in the comment. What list(s)? What "regular imenu"? Perhaps your original question is not clear/complete?
    – Drew
    Commented Feb 10, 2018 at 23:59
  • Say I want one key to jump between functions, another key to jump between sections of documentation (doxygen groups for eg). Can Imenu be setup so both are possible?
    – ideasman42
    Commented Feb 11, 2018 at 0:00
  • That's a different question. Vanilla Imenu is an Emacs menu - usable with the mouse. There are other, related Imenu features linked to from that wiki page, however, some of which provide key access.
    – Drew
    Commented Feb 11, 2018 at 0:03
  • The question is not how to jump between functions/documentation, thats just an example use case. The question is - is it possible to define an imenu - that doesn't become the only kind of imenu for that buffer, so I can assign different key-bindings for different imenus.
    – ideasman42
    Commented Feb 11, 2018 at 0:04
0

Here is a working example of a menu that jumps between sections using completing-read (which can be enhanced by enabling ivy):

;; complete init.el (for convenience)

(require 'package)
(package-initialize)
;; optional, you can disable this but `completing-read` is less useful.
(require 'ivy)

;; See: https://emacs.stackexchange.com/a/35033/2418

(defun custom-popup (menu-prompt menu-index menu-content) "
Pop up menu
Takes args: menu-prompt, default-index, menu-content).
Where the content is any number of (string, function) pairs,
each representing a menu item.
User can hit just the first char of a menu item to choose it.
Or click it with `mouse-1' or `mouse-2' to select it.
Or hit RET immediately to select the default item.
"
  (interactive)
  (let* (;; don't sort when 'completing-read' from 'ivy' is in use.
         (ivy-sort-functions-alist nil)
         (choice
          (completing-read
           ;; menu prompt & content
           menu-prompt menu-content nil t nil nil
           ;; default index
           (nth menu-index menu-content)))
         (action  (cdr (assoc choice menu-content))))
    (funcall action)))

;; helper functions

;; https://emacs.stackexchange.com/a/7156/2418 (with minor edits)
(defun matches-in-buffer-forward (regexp regexp-group &optional buffer)
"Return a list of matches and position pairs of REGEXP in BUFFER
or the current buffer if not given."
  (let ((matches))
    (save-match-data
      (save-excursion
        (with-current-buffer (or buffer (current-buffer))
          (save-restriction
            (widen)
            (end-of-buffer)
            ;; search backwards since resulting list will be reversed
            (while (search-backward-regexp regexp nil t 1)
              (push
               (list
                (match-string-no-properties regexp-group)
                (match-beginning 0))
               matches)))))
      matches)))


(defun doc-jump-section-from-matches (menu-prompt matches &optional buffer) "
Take a list of '(text, positions)' and use them to activate a menu
(with the active item closest above or matching the current line).
"
  (interactive)
  (setq
   menu-content '()
   menu-default-index -1
   index 0)
  (let ((point-eol (save-excursion (end-of-line) (point))))
    (dolist
        (section-pair matches)
      (let ((section-title (nth 0 section-pair))
            (section-pos (nth 1 section-pair)))
        (when (>= point-eol section-pos)
          (setq menu-default-index index))
        (add-to-list 'menu-content
         `
         (,section-title
          .
          (lambda
            ()
            (goto-char ,section-pos)
            ;; move the cursor after the section block
            ;; (forward-paragraph)
            ;; show the section at the top
            (recenter scroll-margin)))))
      (setq index (+ 1 index)))
    (setq menu-content (reverse menu-content))
    (custom-popup menu-prompt menu-default-index menu-content)))

;; Jump to 'SECTION' - an example.
(defun my-jump-to-section ()
  (interactive)
  (doc-jump-section-from-matches
   "Jump: "
   (matches-in-buffer-forward ".*SECTION\s*\\(.*\\)" 1)))

(global-set-key (kbd "<f8>") 'my-jump-to-section)
2
  • FWIW: The Lisp code here is not syntactically correct. E.g., missing paren in my-jump-to-matches, missing parameter list and interactive spec in my-jump-to-section. (And doc strings that start and finish in unconventional places - not an error, but odd.)
    – Drew
    Commented Jun 1, 2018 at 15:01
  • @Drew, thanks - updated the answer to a complete self contained working example.
    – ideasman42
    Commented Jun 2, 2018 at 8:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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