Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding dynamic blocks to org-roam #2251

Open
1 task done
chrisbarrett opened this issue Aug 18, 2022 · 7 comments
Open
1 task done

Adding dynamic blocks to org-roam #2251

chrisbarrett opened this issue Aug 18, 2022 · 7 comments

Comments

@chrisbarrett
Copy link

chrisbarrett commented Aug 18, 2022

Brief Abstract

org-mode has a feature called dynamic blocks (dblocks), which allow you to define blocks whose contents are computed dynamically. I've got an implementation that builds lists of links to org-roam nodes, allowing you to create 'canned searches'. I'd like to offer it for inclusion if there's interest.

Long Description

Dynamic blocks are an extension mechanism that allows packages to teach org-mode about new kinds of blocks. The contents of these blocks are dynamically computed.

I've written an implementation that inserts a list of nodes from the org-roam db that match criteria in the header. I have also written a corresponding minor-mode that automatically updates the blocks on file visit and before save, which keeps them up-to-date.

Use-cases for these blocks include dynamically discovering nodes based on criteria (e.g. tags) to build up Maps of Content (MOCs), list backlinks that match certain predicates, etc.

As a simple example, I have the following nodes in my DB:

  • Action (genre)
  • Sci-Fi (genre)
  • Horror (genre)

Say I want to abstract over these nodes with a higher-level MOC listing all genres. I could go into each note and insert backlinks, or built a list of links in the MOC by hand.

- [[id:...][Action (genre)]]
- [[id:...][Sci-Fi (genre)]]
- [[id:...][Horror (genre)]]

But since they all follow a title convention (they end with (genre)) I could use one of these dynamic blocks to generate the list for me:

#+BEGIN: notes :match "(genre)$"
#+END:

Hitting C-c C-c on the header fills it out:

#+BEGIN: notes :match "(genre)$"
- [[id:...][Action (genre)]]
- [[id:...][Sci-Fi (genre)]]
- [[id:...][Horror (genre)]]
#+END:

Assuming I've enabled the auto-update minor mode, I can then create new nodes later with the <name> (genre) convention. Next time I open up my genres MOC, that list will update automatically to include links to those new nodes.

Proposed Implementation (if any)

https://github.com/chrisbarrett/nursery/blob/main/lisp/org-roam-dblocks.el

The implementation uses some support libraries defined in that repo. I'd remove these dependencies if this were to be contributed.

Commentary:

Defines dynamic block types for use with org-roam.

Example configuration:

   (use-package org-roam-dblocks
     :hook (org-mode . org-roam-dblocks-autoupdate-mode))

The dblock types defined are:

- "backlinks": lists the backlinks for this node, with optional filter
  criteria.

    E.g., in my TV Tropes note I have:

    #+BEGIN: backlinks :match trope$
    - [[id:...][Advanced Ancient Humans Trope]]
    - [[id:...][Bizarre Alien Biology Trope]]
    - [[id:...][Evil Brit Trope]]
    - [[id:...][Humans Are Bastards Trope]]
    - [[id:...][Lost Superweapon Trope]]
    - [[id:...][Mega-Corporations Trope]]
    - [[id:...][One-Man-Army Trope]]
    - [[id:...][Precursor Alien Civilisation Trope]]
    - [[id:...][Scary Dogmatic Aliens Trope]]
    - [[id:...][Sealed Evil in a Can Trope]]
    #+END:

- "notes": lists org-roam notes based on filter criteria.

    E.g. A block that collects open questions in my Zettelkasten:

    #+BEGIN: notes :match (rx "?" eos) :tags (-answered -snooze -outline)
    - [[id:...][Are Alien and Blade Runner in the same universe?]]
    - [[id:...][Can attention span be increased through training?]]
    - [[id:...][Is there research supporting the claimed benefits of the Pomodoro Technique?]]
    #+END:

Implemented filters:

- :match, which matches note titles (case-insensitively).

    A match filter must be an `rx' form or regexp string. String
    double-quotes may be safely omitted for regexps that are just a single
    alphanumeric word.

    Examples:
    - foo, "foo", (rx "foo")
    - "foo bar", (rx "foo bar")
    - "[?]$", (rx "?" eol)

- :tags, which matches the note's headline and file tags.

    A tags filter must be a single tag (double-quotes optional) or a list of
    tags. Each tag may be preceded by a minus sign to indicate a forbidden tag,
    or a plus symbol to indicate a required tag. Tags are interpreted to be
    required if neither +/- is specified.

    Examples of tags matches:
    - required: foo, "foo", +foo, "+foo"
    - forbidden: -foo, "-foo"
    - multiple tags (and-ed together): (foo "+bar" -baz)

- :filter, and its logical opposite :remove, provide a generic way to decide
  which nodes to include.

     A filter can be a symbol, which is interpreted to be a function name, a
     lambda expression, or a bare S-expression.

     When a function or lambda expression is provided, it will be called on
     each node to decide whether to include that node in results. The given
     function should accept a single argument, which is an `org-roam-node'.

     Otherwise, the form is interpreted to be an 'anaphoric' S-expression,
     where the symbol `it' is bound to an `org-roam-node', before being
     evaluated.

     Examples:
     - my-predicate
     - (lambda (node) (equal 0 (org-roam-node-depth node)))
     - (equal 0 (org-roam-node-depth it))

     If :filter and :remove are both provided, they are logically and-ed.

Keeping blocks up-to-date:

These dynamic blocks can optionally be updated when opening and saving
buffers. To do this, enable `org-roam-dblocks-autoupdate-mode'.

The autoupdate can be customised using `org-roam-dblocks-auto-refresh-tags'
so that it only runs in files/headings with specific tags. This is useful if
you want to have both index-style cards and stable canned searches.

Please check the following:

  • No similar feature requests
@qingshuizheng
Copy link

qingshuizheng commented Aug 19, 2022

@chrisbarrett
Just tried it out, and it is really promising (other packages as well, still trying).
Would probably save me a lot of time updating the links.

BTW: does it support "nodes" level?
I I change "notes" to "nodes", and add "nodes" to block names, got no luck.

(setq org-roam-dblocks-names '("notes" "backlinks" "nodes"))

@chrisbarrett
Copy link
Author

chrisbarrett commented Aug 19, 2022

@qingshuizheng probably better to ask for support on my repo at this stage, but I'll answer these quick questions here. :)

BTW: does it support "nodes" level?

You can use the :filter header argument to filter on anything you'd want.

#+BEGIN: notes :filter (zerop (org-roam-node-level it))
#+END:

(setq org-roam-dblocks-names '("notes" "backlinks" "nodes"))

^ This variable is not intended to be customised--the names of blocks are hardcoded and cannot be customised due to limitations of the dblocks API.

@qingshuizheng
Copy link

probably better to ask for support on my repo at this stage

@chrisbarrett Ahh! Exactly. Apologies. :-)

You can use the :filter header argument to filter on anything you'd want.

I must have been blind for skipping this. Let me dive into it.

@EFLS
Copy link

EFLS commented Sep 15, 2022

I just want to chime in that this is an amazing addition (in terms of functionality -- can't judge the code) and I hope it gets included in Org-roam itself! Or, if not, that you'd package it outside your 'nursery' 😄

@reyman
Copy link

reyman commented Sep 27, 2022

Wow this is great, i think about something similar using org-ql (alphapapa/org-ql#183). perhaps that could enhance some of your already function to filter content @chrisbarrett ?

@reyman
Copy link

reyman commented Dec 6, 2022

Perhaps there is a link between the two project, alphapapa/org-ql#303 and the recent merge made by @jethrokuan for #2288 ?

@ahmed-shariff
Copy link
Contributor

This is pretty cool.

For what it's worth, I have a very rough version working with org-ql (https://github.com/ahmed-shariff/dotfiles/blob/master/.emacs.d/customFiles/okm-ql-view.el), but for some reason org-ql (and org) with large number of files is super slow (as seen in alphapapa/org-ql#303)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants