How to to get [[Title of the target note]] working with inline autocomplete in Org-roam

In this post, I want to briefly talk about how to set up liking to another note via [[Title of the target note]] syntax.

This is related to official manual’s section on Completions.

See the outcome in this GIF animation.

It may not look much, but there are a couple of components working together.

  1. When you type [[, the closing brackets ]] gets automatically inserted

  2. [[Title of a note]] establishes a link (and backlink) as it is. It does not automatically change to the normal file link when you save the note

  3. You type two characters “in”; Emacs automatically suggests candidate notes’ titles, and the matching is case-insensitive

  4. You select a candidate, and hit the Tab key; the candidates are narrowed down up to their common part “introduction_” (“_” indicates a space, discarding note entitled “Introduction”), and this partial completion is case-insensitive

  5. You hit the Enter key to select a note; the cursor automatically goes out of the ]] bracket, so that you can keep typing

  6. (Idea to tweak UX further) There is some development idea I have posed separately :wink:

  7. [Added 30 Sept 2020] If you prefer auto-completion without “[[”, there is a way. Open the quote box just below here:

You can easily set up your Emacs configuration to do 1–3; I will show you how in a minute.

Point 4 turns out to require local change to code (add one line) in company-capf.el. As far as I can see there is no easy way around it; let me know if you know a good way. I will sleep on it a couple of nights, and then will probably raise a PR in the Company project’s GitHub repo. I will show you the code change I have done to get it to work.

For Point 5, I raised a PR to the Org-roam project; Jethro and I had a brief exchange. It has turned out Jethro has been working on some changes in this area (completion-at-point). What I am writing here might need some tweaking to align with the upcoming changes, but I believe you can still take away the principle from this brief post.

Configuration

See the excerpt of configuration with my comments relevant for points 1–3.
This is for vanilla Emacs.

;; Smartparens
;; To automatically close "]]" brackets and other parentheses,
;; you need a package called "smartparens" Set it up globally.
(smartparens-global-mode t)

;; Company
;; You need package called `company`.
;; I believe what these variables are meant to do is self-explanatory.
;; You type minimum 2 characters and wait for ¼ seconds for the candidates
;; to appear automatically. It uses a backend `company-capf` (part of
;; `company`; capf stands for "completion-at-point function"). I would
;; call it inline automatic completion. Org-roam has functions to work
;; with `company-capf`.
(add-hook 'after-init-hook 'global-company-mode)
(setq company-minimum-prefix-length 2)
(setq company-idle-delay 0.25)
(add-to-list 'company-backends 'company-capf)

;; This enables candidates matching to be case-insensitive
(setq completion-ignore-case t)

;; You need this for your org-roam part of configuration
;; This prevents it from automatically replacing [[Title of a note]]
;; into [[file:path/to/note][Title of a note]].
;;(setq org-roam-auto-replace-fuzzy-links nil)

;; Some keybindings for within Company mode
;; Use (with-eval-after-load) to defer the use of `company-active-map`.
;; Otherwise, Emacs will error when loading `init.el` as it does not
;; recognise any Company related variables and functions yet.
;; This delay is defined above as we load 'global-company-mode' with
;; `after-init-hook`.
(with-eval-after-load 'company
  (define-key company-active-map (kbd "C-n") #'company-select-next)
  (define-key company-active-map (kbd "C-p") #'company-select-previous))

For Doom, it has modules for Company (in :completion as company) and Smartparens (in :config as (default +smartparens)). Because these modules already set most of the variables above for you, I believe you just need to add these two variables.

;; This enables matching candidates case-insensitive
(setq completion-ignore-case t)

;; You need this for your org-roam part of configuration
;; This prevents it from automatically replacing [[Title of a note]]
;; into [[file:path/to/note][Title of a note]].
;;(setq org-roam-auto-replace-fuzzy-links nil)

Even the keybindings for Company mode seem to be done for you via the module.

Partial completion (company-complete-common)

As far as I could check, you can’t do this with configuration alone. If your candidates contain different cases (via completion-ignore-case set to t), hitting a tab key does not do what the GIF animation shows you.

Your candidates are matched case-insensitive, but your partial completion works case-sensitive… I would like consistency in these two matching commands for my use case.

Long story short, you need to change the source of company-capf.el to enable ignore-case function – not part of the source. My sample implementation adds one line to add ignore-case and uses completion-ignore-case to keep consistency; it works as shown in the animated GIF in the beginning of this post.


This is it for this brief how-to tip.

I will probably come back to this post once Jethro’s new changes have been rolled into the master branch of Org-roam.

'til then, keep roaming :+1:

5 Likes

Overnight, the new commit I alluded to is now in the master branch. I will revisit this post soon. Thanks @jethro :+1:


Just very quickly…:

The new commit introduces two main things relevant for this post:

  1. variable org-roam-link-auto-replace
    Set it to nil to prevent Emacs from automatically converting [[Title]] into a standard Org link [[file:path][Description]]. Optional, though, if you prefer the default behaviour (i.e. replace).

  2. Type [[roam:]] to trigger inline automatic completion (instead of just [[]])

And then, overnight, Jethro fixed all of this :+1:

Both [[title of a note]] and [[roam: title of a note]] work…
Isn’t it great!?

For those who would prefer to do “inline auto-completion” without the “[[” prefix, you have a way (from this Slack thread).

(setq org-roam-completion-everywhere t)

See it in action in the GIF below.

2 Likes

Before I forget, you can link to a headline within the same note like this via Org-roam.
Or simply [[Eat breakfast]] using “fuzzy link” of native Org Mode (not shown in the demo below).

@nobiot thanks for the tutorial. I have some issues to enable this essential feature on roam, I have listed my problem in the slack channel: https://orgroam.slack.com/archives/CV160S8EL/p1605571986351400

  1. It seems the completion functions are not registered for completion-at-point.
  2. [[| does trigger the completion properly (even after I manually add the function)

Do you have some hints about where should I begin the debug? (I reproduced this with a clean emacs start)

Does it work if you add “roam:” as “[[roam:|]]”?

Is there a way to have links without the roam: prefix, just like company-roam used to do?

Are you talking about this?

Set this variable to t?

I’m trying all this as well, but without any success.

  • Emacs: GNU Emacs 27.1 (build 1, x86_64-suse-linux-gnu, GTK+ Version 3.24.20, cairo version 1.16.0)
  • Framework: N/A
  • Org: Org mode version ( @ /home/rainer/.emacs.d/elpa/org-9.4.4/)
  • Org-roam: 1.2.3

What have you tried so far? Do you have Company-mode? And did configuration?

I tried to copy the config to Scratch and execute every statment with C-j. Did not work out. :wink:

Now I copied the config to my startup file, tried again, and yes, now it seems to work.

Maybe its also my fault because I thought I can type any string to match against the title, but it turns out that you need to start typing the title get the choices.

Thanks anyway.

2 Likes

Regarding this, perhaps you might refer to this Slack post I did long ago

1 Like

That does not do it form me. I still get links that look like this [[roam:Title]] instead of [[roam:Title][Title]].

I believe that’s how it is designed to behave at the moment. Can’t be sure. That flag is meant to be used if you would like the regular org link (like, [[file:path/to/file.org][title]] when t).

I’ll stick with C-c n I for now. org-roam-auto-replace-fuzzy-links is as yet undocumented.

Oh, you are right. Perhpas it was replaced by this:
Instead, have a look at this customizing variable: org-roam-link-auto-replace.

Regardless, you ca also use this command: org-roam-link-replace-all.

I tried the command; it seems to work.

Thanks for the great guide, @nobiot. I am getting errors, however, when trying autocomplete to work on headlines to the same file (completion of headlines of other files work fine).

For instance, in your examples here (How to to get [[Title of the target note]] working with inline autocomplete in Org-roam - #5 by nobiot) I would get

Company: An error occurred in auto-begin
Company: backend company-capf error "Wrong type argument: arrayp, nil" with args (candidates Ea)

(In fact, links created via [[roam:*headline]] do not work for me —I am not sure if those links are supposed to work or not. Links created via [[*headline]] do work; it is just the autocompletion that does not).

A workaround is, of course, to create the link to the headline by using the procedure of creating links to headlines in other files, but I think this is an ugly hack.

I am using versions of org-roam and company updated today (company: 0.9.13; org-roam: 1.2.3), under Emacs 28.0.50 (the native-comp branch), from about 10 days ago. I started emacs via emacs -Q and in the scratch buffer pasted the code below, so I do not think it is anything in my init.el.

(package-initialize)

(use-package org-roam
	     :custom
	     (org-roam-directory "~/org/org-roam-files/")
	  :bind (
		 ("C-c n l" . org-roam)
		 ("C-c n f" . org-roam-find-file)
		 ("C-c n g" . org-roam-graph-show )
		 ("C-c n i" . org-roam-insert)
		 ("C-c n I" . org-roam-insert-immediate)
		 ("C-c n t" . org-roam-tag-add)
		 )
	  )

(use-package company)
(smartparens-global-mode t)
(global-company-mode)
(setq company-minimum-prefix-length 2)
(setq company-idle-delay 0.25)
(setq company-backends '(company-capf))

Are you visiting a file within the org-roam directory?
I will try next time I work on PC, but I have never seen the errors you have had.

I’m on Windows and I don’t think I can be on native comp branch of Emacs. This might be the cause of the issue. Does calling completion-at-point work if you call it without using Comapny?

Also, you said you did emacs -Q, and put that code in scratch buffer. I will assume you evaluated the entire buffer (correct? no error?).

I don’t use use-package; does Emacs correctly load Org-roam and Company without adding load path to them, when use-package does not seem to be loaded? In other words, when you test company-capf for Org-roam, are Company and Org-roam actually working?

Thanks for your reply :slight_smile:

Yes (and everything else works, such as linking to other files, or the headlines if I use [[roam:file*headline]]

I’ve repeated this using Emacs 27.1 (the built-in package for Debian) and the same thing happens. With company and org-roam installed again from scratch.

Calling completion-at-point gives me the error Wrong type argument: arrayp, nil (both Emacs 28 and Emacs 27.1)

Yes, I evaluated every line.

Yes, all are loaded (and, as I said, inking to other files, or the headlines if I use [[roam:file*headline]] works). Anyway, I tried again with emacs -Q and these lines, and I get the exact same issues (in both Emacs 28 and Emacs 27.1):

(require 'package)
(package-initialize)

(require 'org)
(require 'org-roam)
(require 'company)

(setq org-roam-directory "~/org/org-roam-files/")

(org-toggle-link-display)

(smartparens-global-mode t)

(global-company-mode)
(setq company-minimum-prefix-length 2)
(setq company-idle-delay 0.25)
(setq company-backends '(company-capf))

I am wondering if it is something else that is the cause of the problem. Is the syntax [[roam:*headline]] supposed to work? (It does not for me, regardless of completion —I can write it, of course, but clicking on it gives me a Wrong type argument: arrayp, nil. This [[*headline]] does work, though —the link; the completion does not work) .