I found myself wanting to automatically keep a log of every org-roam entry I work on. After struggling with the capture system for a while, I managed to make something work.

(setf (alist-get "u" org-roam-dailies-capture-templates nil nil #'equal)
      '("updated" plain nil
        :target (file+head+olp "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n" ("Updated"))
        :unnarrowed t :prepend t))

(defmacro +save-current-buffer (&rest body)
  "Save current buffer; execute BODY; restore original buffer.
I have found that this macro works under some circumstances in
which ‘save-current-buffer’ does not."
  (declare (indent 0))
  `(let ((original-buffer (current-buffer)))
     (unwind-protect (progn ,@body)
       (when (buffer-name original-buffer)
         (switch-to-buffer original-buffer)))))

(defmacro +edit-to-string (&rest body)
  "Eval BODY in a temporary buffer and extract contents to a string."
  (declare (indent 0))
  `(with-temp-buffer
     ,@body
     (buffer-string)))

(defun +org-element-contents (element)
  "Extract contents of ELEMENT from current buffer."
  (buffer-substring (org-element-property :contents-begin element)
                    (org-element-property :contents-end element)))

(defun +org-roam-updated-on-save ()
  "Log current Org-roam buffer as modified."
  (when (org-roam-buffer-p)
    (let* ((original-buffer (current-buffer))
           (id (org-id-get (point-min)))
           (title (org-get-title))
           (link-text (format "%s\n" (+edit-to-string
                                       (org-insert-link nil id title)))))
      (save-excursion  ;; in case we are currently editing the daily note
        (+save-current-buffer
         (org-roam-dailies-goto-today "u")
         (unless (eq original-buffer (current-buffer))
           (let ((elt (org-element-context)))
             (if (not (eq 'plain-list (org-element-type elt)))
                 (insert "\n- " link-text)
               (org-down-element)
               (cl-loop
                for elt = (org-element-context)
                while (eq 'item (org-element-type elt))
                if (equal link-text (+org-element-contents elt)) return nil
                do (org-forward-element)
                finally (insert "- " link-text))))))))))

(add-hook 'after-save-hook #'+org-roam-updated-on-save)

I originally tried to do this using just capture templates (i.e., just an interpolated Lisp expression, no wrapper code), but I’m not sure that that’s possible due to some flexibility problems with the Org-roam capture system.

One thing I’m not happy about is having to use my weird homespun +save-current-buffer macro, but the original save-current-buffer macro doesn’t switch back for some reason.

I am kind of? happy about having gotten some experience with the loop macro. I’m still not sure I like it, but I understand better why it exists in a language that doesn’t have other, more structure looping facilities.

  • fuzzbomb23@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    10 months ago

    The org-roam-timestamps package might be worth a look. It adds created/modified timestamp properties to org-roam nodes, and automatically updates these when saving the node.

    An interesting feature is that it can optionally keep a sequence of modification timestamps, not just the last-modified timestamp.

    It doesn’t actually change the SQLite schema though. All it does is update the properties in the Org file. (Org-roam considers the files to be canonical; the DB is really just a disposable index for speedy lookups.) The node table already has a properties column, so the created/modified timestamps end up buried in this.

    The org-roam-timestamps package is a bit lacking in features, I’d say. All it does is save and update the timestamp properties for each Org-roam node. It doesn’t provide any useful query functions. I’ve had this package installed for ages, but I don’t really do anything with it. For now, it’s just collecting timestamps for the sake of posterity.

    I have a vague idea to write some query functions, such as “what nodes did I create/modify on the same day as the current node?”. The results could populate a section in the Org-roam buffer (after the backlinks), or an Org dynamic block.

    • field_thought_slight@alien.topOPB
      link
      fedilink
      English
      arrow-up
      1
      ·
      10 months ago

      I use org-roam-timestamps; but, as you say, there are no query functions.

      If you have org-roam-timestamps and just want to answer the question “which nodes did I modify on a given day”, I think it would be very easy to write a function for that. But I don’t think there’s any harm in duplicating that data in the daily log, either.

      • fuzzbomb23@alien.topB
        link
        fedilink
        English
        arrow-up
        1
        ·
        10 months ago

        I keep a journal using org-roam-dailies. My headlines there include “Notes created” and “Notes updated”. I manually create links there as I go along; it’s no hardship to do that a dozen times a day, so I haven’t yet automated it.

  • reddit_ran@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    10 months ago

    Well, this is pretty much a very complex idea, but it’s commonly used in software development. I was wondering the usability of it. Maybe I’m just not seeing it right now, but I’m open to the discussion. Every time I have trouble with understanding those high tech, I always go back to the analogous version of the Zettelkasten system. In that case, if you have a new version of an old node, in that case you just write a new one. You can just start at the very beginning of that node to refer back to the old nodes. You don’t have to say all the changes you make for that single node, as long as you’re accumulating those new ideas and creating them together. As long as that process of making things together is evolving, then your system is okay. It introduce another layer of complexity without adding the value that is corresponding to that cost. If we go back to the basic philosophy of UNIX design, keep things simple as possible, then I’ll put a big question mark on adapting this idea in my workflow right now.

    • field_thought_slight@alien.topOPB
      link
      fedilink
      English
      arrow-up
      1
      ·
      10 months ago

      In that case, if you have a new version of an old node, in that case you just write a new one. You can just start at the very beginning of that node to refer back to the old nodes.

      If you keep your notes in version control, you can achieve a similar effect with git-auto-commit-mode.

  • MoistFew@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    10 months ago

    Damn this is neat I didn’t realize I wanted this until now thank you!

    I use a datetree format for my dailies so I managed to make this work via a few modifications to +org-roam-updated-on-save. Maybe this will come in handy for someone else

    With my current daily capture templates as:

    (setq org-roam-dailies-capture-templates
    '(("d" "default" entry "* %?" :target (file+datetree "dailies.org" week))))
    

    some tiny changes to the key for org-roam-dailies-goto-today, the link text, and making it go to end of subtree before inserting the link

    (defun +org-roam-updated-on-save ()
        ...
        (link-text (format "[[id:%s][%s]]\n" id title)))
        ...
        (org-roam-dailies-goto-today "d")
        ...
        (if (not (eq 'plain-list (org-element-type elt)))
          (progn
            (org-end-of-subtree)          
            (insert "\n- " link-text))
    

    Edit: this might not fully work with regards to not putting the same node over and over