Md-roam: Use Markdown with Org-roam V2

Hi all,

Just want to quickly show my humble Org-roam plug-in I named “Md-roam”. With it, you can use Org-roam with Markdown together with Org files (yes, you can mix them in a single Org-roam directory). I finally got it to work with V2.

I don’t intend it to make it to MELPA or any other public package repositories at this stage; it is meant to be my personal enhancement to Org-roam. If this is OK with you folks, you are welcome to try it.

I use it everyday now, so it works as much as I want it to be. I use iPhone and iPad when I’m not at the desk with my laptop. I want to take notes on my mobile devices and sync them with my laptop. As Markdown offers more options for note-taking on iOS, I want the bridge between Markdown and Org-roam. Md-roam is the bridge :slight_smile:

One remark for those interested: I don’t export/publish my notes, so Md-roam does not really do anything for the process. The [[title/alias]] wiki links work only within Org-roam (I use its database to resolve IDs). If you want to export md files with these links that work outside Org-roam, you will need to resolve the links yourselves somehow.


From Md-roam’s GitHub README:

  1. Title and other meta data in the YAML front matter
  2. #tag support to categorize notes
  3. Note as a reference material (literature notes or notes on website) with roam_refs:
  4. Aliases of a note with roam_aliases: in the YAML array syntax with ["alias1", "alias two"]
  5. Link with [[wiki-link]] syntax that appears as a backlink and “in-line search” with Company or Corfu ; you can use the title or an alias of a note
  6. Citations with Pandoc style [@citekey] , @citekey -@citekey , etc. for Markdown files; for Org, Org-ref or Org-cite styles as Org-roam support them
  7. Markdown and Org citations for reference materials; they appear in the reflink section
  8. Backlinks between Org and Markdown files both ways; you can mix both formats in a single Org-roam database
  9. Org-roam standard backlink buffer with no modification to the database schema and backlink buffer
  10. Partial support by Org-roam-ui (ORUI)

ORUI with Markdown files is already functional and useful for most cases. The links work. As of 2021-11-10, it’s “partial support” because the preview of note on the right panel is not entirely compatible with the Markdown format yet – it is designed for the Org format. The headings with # are interpreted as Org’s comment, so not displayed. The YAML front matter is not correctly rendered. All text information appears to be present but the underscore _ is interpreted as an indicator for subscript.

Animated GIF for “in-line completion” (completion-at-point function, or capf) for wiki links:

6 Likes

Hi nobiot,

Thank you for such great plugin! I believe that it is the base for extending the usability of org-roam in an area where Emacs (and Linux in general) falters: mobile devices.

Let me explain my experience and workflow because I think it may help others with similar predicaments: After reading Ahrens’ How to take smart notes, the first thing that came to my mind as an Emacs user was: “The Zettelkasten workflow he describes sounds like something org-mode would do pretty well”. I was pleasantly surprised to find out that Jethro had already invented org-roam, and I’ve been a user ever since. However, as I began my Ph.D in the middle of the COVID19 pandemic, the occasions where I had the possibility to work comfortably with my own Emacs set up were becoming more and more limited: I saw myself working most of the time at cafés with small tables where working with several books and a big laptop was difficult. I ended up buying a Bluetooth keyboard to work from my phone. Furthermore, since I began capturing a lot of information with my smartphone such as scans, voice notes (not audio files, but text from speech recognition), screen captures, etc., I had to find a way to put all that into my Zettelkasten because there’s no Emacs client on iOS.

First, I would use Evernote, but notes kept accumulating there, which defeats the purpose of the Zettelkasten (i.e., to produce a communication partner and not a collection of notes). Then, I switched to Bear, but the same problem occurred because I use Emacs from a Linux computer. In the end, as Obsidian.md had recently released an iOS app, I jumped ship. After a couple of months of using Obsidian, I have settled for the fact that neither Emacs nor Obsidian offer all the features that I need in one place, so I use both on a daily basis for different things: I write 99% of my notes (research, dailies, task management, etc.) in Obsidian, but I use Emacs (bibtex-mode, bibtex-completion, markdown-mode) for managing my BibLaTeX files, exporting my markdown notes to pdf (using custom elisp functions and pandoc) and doing bulk edits to my notes (with dired).

I am very excited to hear that md-roam is now compatible with org-roam v2 because of the prospect that it offers to work on my Obsidian vault from within Emacs. For compatibility between Emacs’ markdown-mode and Obsidian, though, I had to give up on wiki-style links, so I made a minor change to the insert part of the function md-roam-note-insert to produce markdown-links:

(cl-defun md-roam-node-insert (&optional filter-fn &key templates info)
  "Find an Org-roam node and insert (where the point is) an \"id:\" link to it.
FILTER-FN is a function to filter out nodes: it takes an `org-roam-node',
and when nil is returned the node will be filtered out.
The TEMPLATES, if provided, override the list of capture templates (see
`org-roam-capture-'.)
The INFO, if provided, is passed to the underlying `org-roam-capture-'."
  (when (md-roam--markdown-file-p (buffer-file-name (buffer-base-buffer)))
    (unwind-protect
        ;; Group functions together to avoid inconsistent state on quit
        (atomic-change-group
          (let* (region-text
                 beg end
                 (_ (when (region-active-p)
                      (setq beg (set-marker (make-marker) (region-beginning)))
                      (setq end (set-marker (make-marker) (region-end)))
                      (setq region-text (org-link-display-format (buffer-substring-no-properties beg end)))))
                 (node (org-roam-node-read region-text filter-fn))
                 (description (or region-text
                                  (org-roam-node-formatted node))))
            (if (org-roam-node-id node)
                (progn
                  (when region-text
                    (delete-region beg end)
                    (set-marker beg nil)
                    (set-marker end nil))
                  (insert (concat "["
                                  (cond
                                   ((eq md-roam-node-insert-type 'id)
                                    (concat description "](" (org-roam-node-id node) ".md)" ))
                                   ((eq md-roam-node-insert-type 'title-or-alias)
                                    (concat  (org-roam-node-title node) "](" (org-roam-node-id node) ".md)")))))
                  ;; for advice
                  t)
              (org-roam-capture-
               :node node
               :info info
               :templates templates
               :props (append
                       (when (and beg end)
                         (list :region (cons beg end)))
                       (list :insert-at (point-marker)
                             :link-description description
                             :finalize 'insert-link)))
              ;; for advice
              t)))
      (deactivate-mark)
      ;; for advice
      t)))

This works for my workflow because I keep all my Obsidian notes in one folder and I name all my notes as “YYYYMMDDHHmmss.md”. Likewise, for the “id” property in the YAML front matter, I stick to the convention “id = note title” (kinda the way it was in org-roam v1) because Obsidian does not yet have the equivalent of org-id to identify nodes, so linking to headlines within a note, although supported in Obsidian, is not as robust as org-roam’s way of dealing with the problem. For now, sticking to the principle of “1 note = 1 node” as in org-roam v1 hasn’t affected my workflow, but it may to other people’s. (As a side note, since we historians work with so many references (several editions of the same work in different languages, different translations, etc.), I decided to use the “YYYYMMDDHHmmss” format for my BibLaTeX entries because I was wasting too much time maintaining my bibliography files and synchronizing that information with my literature notes. I did the change to this key format earlier this year, and I haven’t looked back).

The only thing I haven’t figured out yet is how to have the completion system produce markdown links instead of wiki links when typing [[ in a markdown file.

Which wiki-style link feature did you have to give up? Obsidian’s or Md-roam’s?

If I remember correctly, Obsidian’s is a file name without the .md extension. It shouldn’t be too hard to do — it’s the feature of Md-roam V1 and the stock Markdown-mode. You might like to take a look at the V1 source in the repo.

I don’t think I would re-implement it myself any time soon, though.

For this… I think you’d just need to convert the double brackets to the markdown link here, or the exit function right after it.

Also… Thank for the detailed portrait of the real situation. And for the code.

I was thinking of supporting the normal markdown link somehow. Perhaps as a configurable switch, and per-call switch by C-I. I might be able plug in your mod. Let me see how I might go with this idea.

I would not have the capacity to specifically support the Obsidian format, but if it can be done as a by-product, I’ll be happy. Let me see what I can do.

Is this in fact this?
id = filename (without .md)

If so… my early version in fact had a customizing option to support it. I removed it for simplicity. I might put it back on??

EDIT: I was mistaken… Thought removed it but I didn’t.

This is not relevant for your case anyway because it’s in the node-insert function you override. Let me think it through. I just came back to my desk to have a better look.

Apologies for a series of quick responses – I was on iPhone… Not the best to read on it.