Can buffer names match note titles?

Right now, it seems like the name of org-roam note buffers simply copy the note’s file name. Does anyone else feel it would be nicer to have it copy the +TITLE of the note?

For example, all my note names have the format “%<%Y%m%d%H%M%S>”, a simple year, month, day, hour, minute, second timestamp. I like the uniformity this brings to my org-roam notes folder, and I like that note titles are decoupled from file names.

My most recent note is named “20200612171529.org”. It happens to have the +TITLE “Some Note-Taking Conventions”. It would be nice if the name of the buffer could be “Some Note-Taking Conventions” instead of “20200612171529.org”.

With the current state of things, C-x-b to switch-buffer (with ivy) shows me tons of notes named with timestamps like “20200612171529.org”. I know I can use org-roam-find-file instead, but it would be nice for the Emacs buffer list to be populated with informational names rather than timestamps.

I realize that org-roam would have to handle changing the buffer name should the name of the note be changed. This doesn’t seem like a huge problem to me, but I also don’t know much about the internals of org-roam. For all I know, this is already a feature and I’m unaware.

If this doesn’t exist already, should I submit a feature request somewhere? Could this be implemented simply in my init.el? Does anyone else even like this idea? Thanks for reading!

On the one hand, you can modify org-roam-capture-templates to define your own naming scheme:

(setq org-roam-capture-templates
      '(("d" "default" plain
         #'org-roam-capture--get-point
         "%?"
         :file-name "%<%Y%m%d%H%M%S>-${slug}"   ; <------
         :head "#+title: ${title}\n#+created: %u\n#+last_modified: %U\n\n"
         :unnarrowed t)))

${slug} is by default a processed version of the title in which spaces and illegal symbols are replaced with underscores.

On the other hand, we’ve created commands which allow you to select notes based on their titles: org-roam-find-file. Because of this, you might prefer it to the traditional buffer-switching commands in Emacs.

I personally just keep the default naming scheme, and exclude roam files from helm-find-files altogether:

(add-to-list 'helm-boring-buffer-regexp-list "^[0-9]\\{14\\}.+\\.org$")

I also use eyebrowse-mode to switch between different workspaces, so my note-taking environment is separated from whatever project I am working in Emacs.

1 Like

Thanks for your reply!

I am already using org-roam-find-file, and it works great for limiting my search to only org-roam notes. I like the uniformity of my timestamp-named-files, especially because I sometimes choose to change the title of the note and there’s no point in having it coupled with the file.

Changing the name of the buffer itself seems cleaner to me. What are your thoughts on whether that’s possible/a good idea @zaeph ? Anywhere else you think I could post to get some more opinions?

Changing the name of the buffer is possible, but not advisable.

Buffers which are linked to files should always feature the name of the file. On the other hand, indirect buffer can get away with fancier names. The thing we might be able to do is include the title of the file at the end of the buffer-name, just as Emacs disambiguate buffers which would otherwise have the same name.

The result would be something like 20200407001953-foo.org<Foo>. Please open a PR on the tracker if you’d like us to consider this more formally.

hi @neilyio, pardon me if this has been part of the exchange above; is it possible that the org-roam-switch-to-buffer command does what you would like to achieve? You can assign a key binding for it, like C-c n b as an example. If notes in Org-roam are open as buffers, you should be able to see a list of their titles. I don’t use it myself, so I may be mistaken.

Thanks for pointing that out! That is exactly the way that I’m currently switching org-roam buffers, and there’s nothing wrong with it.

I guess I was just curious as to why, from an org-roam design perspective, my note buffers can’t be better integrated with regular old emacs buffer switching. Buffer switching is one of those reflexive actions you do without thinking about the shortcuts you’re pressing.

I find myself constantly wanting to switch between code files that I’m writing and notes that I’m writing, and it’s a little annoying when I hit the wrong shortcut. I have to remember that C-c n b is what I use to switch to a note buffer, and C-x b is what I use to switch to any other buffer.

I take @zaeph’s word for it that “buffers which are linked to files should always feature the name of the file”. These guys have done an outstanding job with org-roam so far, and I’m sure that design compromises like this have good reasons behind them.

If this is the way it is, I think I’ll follow @hieuphay’s advice and try and exclude these files from regular helm/ivy, and just maintain a “separate” area of emacs for org-roam. I suppose I could even try and re-bind C-x b to org-roam-find-file in that separate area. I usually don’t like to complicate my config with things like this, but org-roam is certainly worth it.

Still interested in the feature if anyone else wants to discuss it, but I’m glad everyone could pitch in here.

I tested this out quickly, and it seems like it works:

(add-hook 'find-file-hook (lambda ()
                            (when (org-roam--org-roam-file-p)
                              (rename-buffer (org-roam--get-title-or-slug (buffer-file-name))))))

These are user customizations that don’t have to be included as part of org-roam, but it’s interesting to know that it’s possible.

1 Like

Building on @jethro’s approach, you could do this if you are using ivy-rich-mode (as you are using ivy, you may already be).

It works with C-x b. The column in the left shows file names, and the middle one, title if Org-roam notes.

Add a custom func like the one below to ivy-rich-display-transformers-list.

Personally, I don’t find this presentation clean, but it may be a way. Extending this idea, you could even
conditionally show either file name or title in one column…

This custom func below is just a quick proof of concept. It will likely fail if you have not loaded org-roam before hitting C-x b (because it relies on functions from org-roam).

(defun my/ivy-switch-buffer-org-roam-title (candidate)
  (if (ivy-rich-switch-buffer-user-buffer-p candidate)
      (let ((file (buffer-file-name (get-buffer candidate))))
        (if (org-roam--org-roam-file-p file)
            (org-roam--get-title-or-slug file)
          ""))
    ""))
2 Likes

This is definitely the better solution. I don’t think it’s a good idea to mess with the buffer name, if the buffer corresponds to the file. Might be more confusing than it is not.

1 Like

I found this bit of code in stackoverflow.

With this, if I have a buffer that starts with #+TITLE:, the title is used instead of the file name when I call ibuffer (my favorite way of swiching buffers) or ivy-switch-buffers. It works like a charm, not only on org-roam but in all org-mode buffers.

(defun org+-buffer-name-to-title (&optional end)
  "Rename buffer to value of #+TITLE:.
If END is non-nil search for #+TITLE: at `point' and
delimit it to END.
Start an unlimited search at `point-min' otherwise."
  (interactive)
  (let ((beg (or (and end (point))
         (point-min))))
    (save-excursion
      (when end
    (goto-char end)
    (setq end (line-end-position)))
      (goto-char beg)
      (when (re-search-forward "^[[:space:]]*#\\+TITLE:[[:space:]]*\\(.*?\\)[[:space:]]*$" end t)
    (rename-buffer (match-string 1)))))
  nil)

(defun org+-buffer-name-to-title-config ()
  "Configure Org to rename buffer to value of #+TITLE:."
  (font-lock-add-keywords nil '(org+-buffer-name-to-title)))

(add-hook 'org-mode-hook #'org+-buffer-name-to-title-config)
1 Like

This looks great. I try to make it work but titles not show up. Am I missing something? My config:

(use-package ivy-rich
  :config
    (progn
      (ivy-rich-mode 1)
      (setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line)

      (defun my/ivy-switch-buffer-org-roam-title (candidate)
        (if (ivy-rich-switch-buffer-user-buffer-p candidate)
         (let ((file (buffer-file-name (get-buffer candidate))))
         (if (org-roam--org-roam-file-p file)
          (org-roam--get-title-or-slug file)
        ""))
    ""))

  (add-to-list 'ivy-rich-display-transformers-list 'my/ivy-switch-buffer-org-roam-title)
))

I figured this out myself:

(defun jarfar/ivy-switch-buffer-org-roam-title (candidate)
  (if (ivy-rich-switch-buffer-user-buffer-p candidate)
    (let ((file (buffer-file-name (get-buffer candidate))))
      (if (org-roam--org-roam-file-p file)
        (org-roam--get-title-or-slug file)
        ""))
    ""))

(setq ivy-rich-display-transformers-list
  '(ivy-switch-buffer
     (:columns
       ((jarfar/ivy-rich-switch-buffer-icon (:width 2))
         (ivy-rich-candidate (:width 30))
         (jarfar/ivy-switch-buffer-org-roam-title (:width 40))
         (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
       :predicate (lambda (cand) (get-buffer cand)))))

EDIT:
Sadly I found issue with that. When we create new note via org-roam-capture and invoke counsel-switch-buffer we will get an error:

Error in post-command-hook (ivy--queue-exhibit): (wrong-type-argument stringp nil)

EDIT 2:
To get around this I change function to:

(defun jarfar/ivy-switch-buffer-org-roam-title (candidate)
  (if (ivy-rich-switch-buffer-user-buffer-p candidate)
    (let ((file (buffer-file-name (get-buffer candidate))))
      (if (org-roam--org-roam-file-p file)
        (condition-case nil
          (org-roam--get-title-or-slug file)
          (error ""))
        ""))
    ""))

After a second thought, I think it’s probably a bug in org-roam somewhere down org-roam–get-title-or-slug, so it probably should be addressed upstream rather than workaround like this.

1 Like

Why isn’t this advisable? rename-buffer will rename the buffer without changing the underlying file the buffer is visiting. Any code dealing with manipulating the file should not be reliant on the buffer-name to do so.