Skip to content
Ahmed Shariff edited this page May 17, 2025 · 5 revisions

Welcome to the org-roam-ql wiki! Recording a few predicates/expansion functions that can be used.

Ripgrep search content

  (defvar consult-org-roam-ql--history nil)

  (defun consult-org-roam-ql (&optional initial-input filter-fn sort-fn
                                        require-match prompt)
    "Consult with org-roam-ql for searching/narrowing."
    (interactive)
    (minibuffer-with-setup-hook
        (lambda ()
          ;; KLUDGE: No idea why this is here!
          (set-syntax-table emacs-lisp-mode-syntax-table)
          (add-hook 'completion-at-point-functions
                    #'org-roam-ql--completion-at-point nil t))
      (let* (split-pos mb-str is-highlight
             (consult-async-split-styles-alist
              `((org-roam-ql
                 ;; :initial ?\;  ;; this was interacting with the embark
                 :function
                 ;; Override how the empty string is handled!
                 ;; When empty async-str should return default candidates
                 ,(lambda (str style)
                    (pcase-let* ((res (consult--split-perl str style))
                                 (`(,async-str ,pos ,start-highlight . ,end-highlight) res)
                                 (force (or (get-text-property 0 'consult--force async-str)
                                            (and (not (null start-highlight)) (not (null end-highlight))))))
                      ;; This gets called at severaal places. We only want the data when it is
                      ;; called with the force value!
                      (setq is-highlight (or start-highlight end-highlight))
                      (when force
                        (setq split-pos pos
                              mb-str str))
                      (cond
                       ((and force (equal "" async-str))
                        (setf (car res) (propertize "-" 'consult--force end-highlight)))
                       ;; when no leading char split-perl can use, just fallback to normal matching
                       ((and (not (or is-highlight
                                      (string-empty-p async-str))))
                        (setf res (list (propertize (car res) 'consult--org-roam-ql-state 'no-narrow)
                                        ;; forcing the actual matching to work
                                        0 '(0 . 0) '(0 . 0)))))
                      res)))))
             (corfu-auto nil)
             (consult-async-input-debounce 1)
             ;; Default candidates
             (nodes (mapcar (lambda (node)
                              (cons (propertize (org-roam-node-title node) 'node node) node))
                            (org-roam-node-list)))
             ;; The sink is what holds the candidates and feed it back to all-completions
             (sink (consult--async-sink))
             (overriden-keymap (make-sparse-keymap))
             (delete-minibuffer-override
              (lambda ()
                (interactive)
                (cond
                 ((and mb-str split-pos)
                  (delete-minibuffer-contents)
                  (insert (substring mb-str 0 split-pos)))
                 ((not is-highlight)
                  (delete-minibuffer-contents))))))

        (define-key overriden-keymap "\M-d" delete-minibuffer-override)
        (define-key overriden-keymap "\M-D" #'delete-minibuffer-contents)
        (define-key overriden-keymap (kbd "C-,") (lambda ()
                                                   (interactive)
                                                   (when (minibufferp)
                                                     (embark-select)
                                                     (funcall delete-minibuffer-override))))
        (when (not (featurep 'org-roam-ql))
          (require 'org-roam-ql))
        (set-keymap-parent overriden-keymap org-roam-ql--read-query-map)
        ;; Feeding initial set of candidates to sink
        (funcall sink nodes)
        (-->
         (consult--async-pipeline
          (consult--async-split 'org-roam-ql)
          (consult--dynamic-collection
              (lambda (input)
                (if (or (length= input 0)
                        (equal input "-")
                        (eq (get-text-property 0 'consult--org-roam-ql-state input)
                            'no-narrow))
                    nodes
                  (condition-case err
                    (mapcar
                     (lambda (node)
                       (cons (propertize (org-roam-node-title node) 'node node) node))
                     (org-roam-ql-nodes (read input)))
                    (user-error
                     (minibuffer-message (propertize (cadr err) 'face 'consult-async-failed))
                     nodes)))))
          (consult--async-indicator)
          (consult--async-refresh))
         (funcall it sink)
         (consult--read
          it
          :prompt (or prompt "Node: ")
          :initial (if initial-input initial-input ";")
          :keymap overriden-keymap
          :category 'org-roam-node
          :sort nil ;; TODO
          :require-match require-match
          :async-wrap nil
          :state (amsha/consult-org-roam--node-preview
                  (lambda (node)
                    (not (string-match "research_papers" (org-roam-node-file node)))))
          :history 'consult-org-roam-ql--history
          ;; Taken from consult-org-roam
          ;; Uses the DEFAULT argument of alist-get to return input in case the input is not found as key.
          :lookup (lambda (selected candidates input narrow) (alist-get selected candidates input nil #'equal)))
         (if (org-roam-node-p it)
             it
           (org-roam-node-create :title it))))))

  (advice-add #'consult-org-roam-node-read :override #'consult-org-roam-ql))

Using org-roam-ql with minibuffer completion of org-roam-nodes

For example, with org-roam-node-find, one could use org-roam-ql to narrow down the nodes showing up with the following:

image

To provide context:

  • I have an org-roam-ql saved-query (fam - in the image above) which allows me to quickly filter with queries I use often.
  • This also has the same completion-at-point function in org-roam-ql (see demo) configured, which is handy.
  • I don’t use the org-roam’ templating for completion candidates. Instead, I have a custom marginalia annotator. I use orderless’s annotation search when I want to further query on the annotations.
  • I prefer “;” as the default value for consult’s perl split style 😅

The consult-org-roam-ql function:

  (defvar consult-org-roam-ql--history nil)

  ;; TODO: filter-fn and sort-fn does notthing now!
  (defun consult-org-roam-ql (&optional initial-input filter-fn sort-fn
                                        require-match prompt)
    "Consult with org-roam-ql for searching/narrowing."
    (interactive)
    (minibuffer-with-setup-hook
        (lambda ()
          ;; KLUDGE: No idea why this is here!
          (set-syntax-table emacs-lisp-mode-syntax-table)
          (add-hook 'completion-at-point-functions
                    #'org-roam-ql--completion-at-point nil t))
      (let* (split-pos mb-str
             (consult-async-split-styles-alist
              `((org-roam-ql
                 ;; :initial ?\;  ;; this was interacting with the embark
                 :function
                 ;; Override how the empty string is handled!
                 ;; When empty async-str should return default candidates
                 ,(lambda (str style)
                    (pcase-let* ((res (consult--split-perl str style))
                                 (`(,async-str ,pos ,start-highlight . ,end-highlight) res)
                                 (force (or (get-text-property 0 'consult--force async-str)
                                            (and (not (null start-highlight)) (not (null end-highlight))))))
                      ;; This gets called at severaal places. We only want the data when it is
                      ;; called with the force value!
                      (when force
                        (setq split-pos pos
                              mb-str str))
                      (when (and force (equal "" async-str))
                        (setf (car res) (propertize "-" 'consult--force end-highlight)))
                      res)))))
             (corfu-auto nil)
             (consult-async-input-debounce 1)
             ;; Default candidates
             (nodes (mapcar (lambda (node)
                              (cons (propertize (org-roam-node-title node) 'node node) node))
                            (org-roam-node-list)))
             ;; The sink is what holds the candidates and feed it back to all-completions
             (sink (consult--async-sink))
             (overriden-keymap (make-sparse-keymap))
             (delete-minibuffer-override
              (lambda ()
                (interactive)
                (when (and mb-str split-pos)
                  (delete-minibuffer-contents)
                  (insert (substring mb-str 0 split-pos))))))

        (define-key overriden-keymap "\M-d" delete-minibuffer-override)
        (define-key overriden-keymap "\M-D" #'delete-minibuffer-contents)
        (define-key overriden-keymap (kbd "C-,") (lambda ()
                                                   (interactive)
                                                   (when (minibufferp)
                                                     (embark-select)
                                                     (funcall delete-minibuffer-override))))
        (when (not (featurep 'org-roam-ql))
          (require 'org-roam-ql))
        (set-keymap-parent overriden-keymap org-roam-ql--read-query-map)
        ;; Feeding initial set of candidates to sink
        (funcall sink nodes)
        (-->
         (consult--async-pipeline
          (consult--async-split 'org-roam-ql)
          (consult--dynamic-collection
              (lambda (input)
                (if (and (> (length input) 0) (not (equal input "-")))
                    ;; TODO: can I update the state/indicator somehow?
                    (condition-case err
                        (mapcar
                         (lambda (node)
                           (cons (propertize (org-roam-node-title node) 'node node) node))
                         (org-roam-ql-nodes (read input)))
                      (user-error
                       (minibuffer-message (propertize (cadr err) 'face 'consult-async-failed))
                       nodes))
                  nodes)))
          (consult--async-indicator)
          (consult--async-refresh))
         (funcall it sink)
         (consult--read
          it
          :prompt (or prompt "Node: ")
          :initial (if initial-input initial-input ";")
          :keymap overriden-keymap
          :category 'org-roam-node
          :sort nil ;; TODO
          :require-match require-match
          :async-wrap nil
          :state (consult-org-roam--node-preview)
          :history 'consult-org-roam-ql--history
          ;; Taken from consult-org-roam
          ;; Uses the DEFAULT argument of alist-get to return input in case the input is not found as key.
          :lookup (lambda (selected candidates input narrow) (alist-get selected candidates input nil #'equal)))
         (if (org-roam-node-p it)
             it
           (org-roam-node-create :title it))))))

  (advice-add #'consult-org-roam-node-read :override #'consult-org-roam-ql))

Note that the above also uses consult-org-roam--node-preview from consult-org-roam to preview state. You can have the :state in consult--read if you don't want previews.

Originally posted in: https://org-roam.discourse.group

Clone this wiki locally