When I use counsel-rg to grep through a directory hierarchy, once a list of search results has been displayed in the minibuffer, I can use ivy-occur to save the list in a dedicated buffer.

How can I instruct org-mode so that org-store-link can preserve a permanent link to such a buffer and org-insert-link can insert one and hitting org-return when the cursor is over such a link will re-run the search command and bring up a corresponding ivy-occur buffer?

2 Answers 2


The dedicated buffers you are talking about in your question are ivy-occur-grep-mode-buffers. Those are filled by counsel-ag-occur. Only the default directory and the buffer name containing the search words are needed for counsel-ag-occur. That information must be present in the links to ivy-occur-grep-mode-buffers. The following lisp code defines a new org link type councelrg with the corresponding functions for following and storing the link.

Links of that type consist of a elisp cons with the default-directory in the car and the buffer name in the cdr. Those links are read by read in org+-counselrg-follow.

(defconst org+-counselrg-link-type "counselrg"
  "String for identifying counsel-rg links.")

(defun org+-buffer-base-name (buffer-name)
  "Remove trailing <.*> in BUFFER-NAME."
    (when (string-match "<[^<>]+>\\'" buffer-name)
      (setq buffer-name (substring buffer-name nil (match-beginning 0)))))

(defun org+-counselrg-store ()
  "Store links of type counselrg:LINK-PATH."
  (when (derived-mode-p 'ivy-occur-grep-mode)
    (org-store-link-props :type org+-counselrg-link-type
              :link (format "%s:%S" org+-counselrg-link-type (cons default-directory (org+-buffer-base-name (buffer-name))))
              :description (format "%s in %s" (buffer-name) default-directory))

(defun org+-counselrg-follow (link-path)
  "Follow org-links of type counselrg:LINK-PATH."
  (let* ((caller-window (get-buffer-window))
     (dir-buf (read link-path))
     (dir-name (car dir-buf))
     (buf-name (cdr dir-buf))
      ;; First try to re-use already existing buffer.
      (cl-loop for buf in (buffer-list)
           if (with-current-buffer buf
             (derived-mode-p 'ivy-occur-grep-mode)
             (file-equal-p default-directory dir-name)
              (org+-buffer-base-name (buffer-name))
           do (with-current-buffer buf (ivy-occur-revert-buffer))
           and return buf
           ;; if there is no buffer for re-use:
           (with-current-buffer (generate-new-buffer buf-name)
             (setq default-directory dir-name)
             (let ((inhibit-read-only t)
               (counsel-ag-base-command counsel-rg-base-command))
               (setq-local ivy-occur-last
                    :directory default-directory
                    :caller 'counsel-ag
                    :window caller-window
                    :buffer (current-buffer)
    (switch-to-buffer buf)))

(org-link-set-parameters org+-counselrg-link-type
             :follow #'org+-counselrg-follow
             :store #'org+-counselrg-store)

If you like to try the code put it into your *scratch* buffer and evaluate the buffer with eval-buffer. If you even want to install the code put it into your init file and restart emacs.

  • I am loving this! thanks! May 21, 2019 at 20:44
  • had to change (counsel-ag-occur) to (counsel-rg-occur) May 21, 2019 at 20:52

I don't use counsel or ivy, but you can execute arbitrary elisp as part of a link. If I wanted to store search results from, say, M-x occur, I would use something like: [[elisp:(occur "phrase")][Search for phrase in this buffer]]

That approach has the downside of prompting you to Execute ... as elisp (y or n), which could be annoying. If you make this sort of link often, you might consider adding a custom link type with (org-add-link-type "occur" (lambda (key) (occur key))) And then you can type occur:blah, and it will be highlighted as a link without a prompt. You'll have to change it to work with ivy-occur of course.

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.