1

Is it possible to use imenu (or another package) to copy the target (specified by a regex) rather than jumping to it?

This is an effort for tackling the question: How to list or autocomplete pandoc-crossref citations in an org-mode buffer?

I'd like to find all labels in the buffer that match a certain regex pattern. Then, instead of jump to that label, I'd like to copy it to the current point. Is this possible with imenu or similar packages?

Related:

How to use a menu to jump between sections of a document?

Get all regexp matches in buffer as a list

-- UPDATE --

To clarify what I was looking for with a minimal example:

I have the following packages installed for the auto-completion:

ivy ivy-hydra imenu imenu-anywhere counsel swiper

and then (require 'imenu) (as suggested).

Now in a buffer with two lines:

aaa
bbb

and the cursor at the third line, I'd like to use the regex aaa to insert the text aaa at the point. E.g. by :

imenu-match-insert('aaa')

I am using a trivial regex, but for a real one, I'd like to use imenu and the minibuffer to select from the list of all labels in the current buffer (before and after the point) that matches the regex.

tinlyx
  • 1,276
  • 1
  • 12
  • 27

3 Answers3

1

This does what you ask for.

(defun imenu-insert-label (s)
  (interactive "sRegexp to search with: ")  
  (insert (completing-read "Choose a target: "
                           (seq-filter (lambda (z)
                                         (string-match s (car z)))
                                       (cdr (imenu--make-index-alist))))))

It uses the seq package to filter the labels. If you are using ivy or helm you get visual feedback on the labels you are choosing from. Obviously you could replace (cdr (imenu--make-index-alist)) by any list you wished to choose from so you could adjust this recipe for many other cases.

Assign this command to a key and you are ready to go.

Aidan Schofield
  • 514
  • 3
  • 5
  • Thanks! Could you give an example of how to use it with `counsel-imenu`? Sorry, I am new to elisp. Just added your code to init.el, and got `cdr: Symbol’s function definition is void: imenu--make-index-alist`. – tinlyx Jun 22 '20 at 17:17
  • The error message means that you do not have imenu loaded at the time you tried the function. Put `(require 'imenu)` before this code. I do not have any experience with `counsel-imenu` so cannot help with that. – Aidan Schofield Jun 22 '20 at 17:39
1

I think that I misunderstood what the OP asked for. He wished to search for matches to arbitrary regexps not simply within the matches found by the usual imenu. This can be done.

(defun imenu-match-insert (s)
  (interactive "sRegexp to search with: ")
  (let ((imenu--index-alist nil)
        (imenu-generic-expression (list (list nil s 0))))
    (insert (completing-read "Choose a target: "
                             (seq-filter (lambda (z)
                                           (string-match s z))
                                         (seq-uniq (mapcar 'car (cdr (imenu--make-index-alist)))))))))

 (defun imenu-for-regexp (s)
  (interactive "sRegexp to search with: ")
  (let ((imenu--index-alist nil)
        (imenu-generic-expression (list (list nil s 0))))
    (imenu--make-index-alist)
    (call-interactively 'imenu)))

The first command allows you to insert at point a match within the buffer for a regexp. The second command allows you to go to an arbitrary match within the buffer just like imenu but for an arbitrary regexp you specify. To make this useful it would be good to use various regexp makers, functions from strings to regexps. For example

(defun symbol-fuzzy-regexp (str)
  (concat "\\_<\\(\\w\\|\\s_\\)*"
          (mapconcat #'identity (split-string str "" t) "\\(\\w\\|\\s_\\)*")
          "\\(\\w\\|\\s_\\)*\\_>"))

(defun imenu-insert-fuzzy-symbol (str)
  (interactive "sType some of the letters in the symbol: ")  
  (imenu-match-insert (symbol-fuzzy-regexp str)))

The command imenu-insert-fuzzy-symbol will find symbols that are matched fuzzily by the letters you type and you choose which of them you want and that is inserted at point

Aidan Schofield
  • 514
  • 3
  • 5
  • Thanks again. I added some clarification. Tried `imenu-match-insert` and `imenu-insert-fuzzy-symbol`, and then entered `aaa` at prompt, but I got: `imenu-unavailable-error: imenu unavailable: "No items suitable for an index found in this buffer"`. – tinlyx Jun 22 '20 at 18:14
  • I guess I'll do more reading on how to use imenu or ivy first before I can follow up with more meaningful questions. – tinlyx Jun 22 '20 at 18:16
  • That means that imenu did not find anything that matched. Try inputting `mzz` when you call `imenu-insert-fuzzy-symbol` in a buffer that has this inside it. – Aidan Schofield Jun 22 '20 at 18:22
  • I just read your clarification and both `imenu-match-insert` and `imenu-insert-fuzzy-symbol` work with input `aaa` (as intended). – Aidan Schofield Jun 22 '20 at 18:41
  • This is weird. I was testing on an .org file, and it gave the error above. But with a .txt file, this actually works. Could org mode configuration interfere with this? – tinlyx Jun 22 '20 at 18:51
  • Ugh. I have had org-mode introduce incomprehensible problems before. I will go away and play with this in org-mode to see what the problem is. Org does all kinds of weird things but I hope that I can find out what the problem is. – Aidan Schofield Jun 22 '20 at 18:56
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/109705/discussion-between-aidan-schofield-and-tinlyx). – Aidan Schofield Jun 22 '20 at 19:17
  • I added a work-around below (for org mode) to switch to text-mode first, perform your functions, and finally switch back to org. – tinlyx Jun 23 '20 at 04:43
  • Doesn't `imenu` complicates things unnecessarily here? You could collect the matches yourself which would probably also avoid the `org-mode` issues the comments above talking about. – clemera Jul 20 '20 at 09:42
  • @clemera, Yes, it does. In the side discussion https://chat.stackexchange.com/rooms/109705/discussion-between-aidan-schofield-and-tinlyx, I recommended doing precisely that and gave a function that did that. – Aidan Schofield Jul 20 '20 at 14:53
0

This is a temporary workaround that worked for me based on @Aidan Schofield's answer.

The imenu-match-insert function there had some issues working with org files. And I added a variant imenu-match-insert-text below (a thin wrapper), which switches the current buffer temporarily to text mode, does the insert and then switch back to the original mode:

(defun imenu-match-insert (s)
  (interactive "sRegexp to search with: ")
  (let ((imenu--index-alist nil)
        (imenu-generic-expression (list (list nil s 0))))
    (insert (completing-read "Choose a target: "
                             (seq-filter (lambda (z)
                                           (string-match s z))
                                         (seq-uniq (mapcar 'car (cdr (imenu--make-index-alist)))))))))

(defun imenu-match-insert-text (s)
  (interactive "sRegexp to search with: ")
  (let ((current-mode major-mode))
    (text-mode)
    (imenu-match-insert s)
    (funcall current-mode)
  )
)
tinlyx
  • 1,276
  • 1
  • 12
  • 27