Org-roam-search with consult-ripgrep (Updated)

Original Post

There was a post in this discourse many years ago on how to enable org-roam search with consult and ripgrep, the variable which enabled the function to work is now deprecated and will no longer work. Since the comments are many pages long and no activity has taken place for a certain amount of time I decided it would be better to open a new topic and post a working piece of code, since most users will not take the time to scroll to the end of the page.

Provided consult is installed inside Emacs, and ripgrep (rg) is available as a system package, the following code maybe used in the init file:

(defun org-roam-search ()
  "Search org-roam directory using consult-ripgrep. With live-preview."
  (interactive)  
  (let ((consult-ripgrep-args "rg --null --ignore-case --type org --line-buffered --color=never --max-columns=500 --no-heading --line-number"))
    (consult-ripgrep org-roam-directory)))
(global-set-key (kbd "C-c f s") 'org-roam-search)

Explanations:

  1. The original code used the variable consult-ripgrep-command : this is no longer valid. Instead we use the variable consult-ripgrep-args.
    This variable is responsible for passing arguments to ripgrep (rg)

  2. The original code used --color=always, this causes the search to breakdown and show no matches. (why? idk), instead we may use either --color=auto or --color=never

  3. (let ((var "value"))
    --code to which this is visible) 
    

allows us to temporarily change the value of a variable, we might as well have used setq, but using let has the upshot of allowing us to use M-x consult-ripgrep in default-directory without these customisations - such as searching inside html/tex files and so on.

  1. Customisations to the code can be easily done - look into rg --help in the system terminal and apply those options as may be required. For example:

    4.a) If we want to exclude our “journal” folder inside org-roam-directory we can use the option --glob=!{journal/*} as a argument to consult-ripgrep-args.

    4.b) If we want to exclude all sub-folders named “foobar” where-ever they exist inside
    org-roam-directory we can use --glob=!{foobar}

    4.c) To exclude the “journal” folder & all folders named “foobar” we can use
    --glob=!{journal/*,foobar} and so on.

  2. If there is a PATTERN that exists in one file only, the auto-completion (live preview) may not work as intended. In that case, it is recommended to nest the PATTERN inside two #, for example #foo#, pressing ‘Tab’ then will list all the instances inside that file as one would normally expect!

3 Likes

Problem: Auto-completions minibuffer doesn’t exit properly when there is only one match.

One problem with the above implementation is that if there is only one match candidate, there is no way to exit the minibuffer. To solve this bug, I have changed the code.

(defun org-roam-search-args ()
  "Search org-roam directory using consult-ripgrep. With live-preview." 
  (let ((consult-ripgrep-args "rg --null --ignore-case --type org --line-buffered --color=never --max-columns=500 --no-heading --line-number"))
    (consult-ripgrep org-roam-directory)))

(defun headlong ()
  "Make the current minibuffer completion exit when there is 1 candidate."
  (add-hook 'after-change-functions
            (lambda (&rest _)
              (let* ((all (completion-all-completions
                           (minibuffer-contents)
                           minibuffer-completion-table
                           minibuffer-completion-predicate
                           (max 0 (- (point) (minibuffer-prompt-end)))))
                     (last (last all)))
                (when last (setcdr last nil))
                (when (and all (null (cdr all)))
                  (delete-minibuffer-contents)
                  (insert (car all))
                  (exit-minibuffer))))
            nil t))

(defun org-roam-search ()
  (interactive)
  (minibuffer-with-setup-hook #'headlong (funcall #'org-roam-search-args)))

(global-set-key (kbd "C-c f s") 'org-roam-search)

I got the code snippet from here, and taken the liberty to make a slight adjustment so that we can only have one interactive function org-roam-search, as before customisations can be done by adjusting the consult-ripgrep-args variable.