Automatically create filetags during org-roam-node-insert?

I typically use org-roam-node-insert to create another file. Then I switch back to the file I started with, copy the filetag, switch to the new file and paste the filetag from the parent file and add a tag for the new page. Is there a way to have the filetag line copied automatically during org-roam-node-insert?

I use a yasnipett to insert a bunch of things that I want at the top of every file.

You can create a template for every filetag. that way, you create the node, asked for the template with the corresponding tag

I read your question wrong the first time, I thought you wanted to paste tags from the new file to the old file. I am sorry -

Run this function while the cursor is on the inserted link. Tested lightly. I cannot find the correct hook to automate this

(defun org-roam-node-insert--update-filetags ()
  (interactive)
  (when-let* ((link (org-element-context))
              (path (org-element-property :path link))
              (query-target-tags (or (org-roam-db-query                 ;; we aditionally check for existing tags
				      [:select [tags:tag]
					       :from tags
					       :where (= node_id $s1)]
				      path)
				     '((""))))
              (query-target-tags-list (mapcar #'car query-target-tags))
              (current-filetags (or (split-string (or (cadr (assoc "FILETAGS"
								   (org-collect-keywords '("filetags"))))
						      "")
						  "[: ]" 'omit-nulls)
				    '("")))
              (new-tags (mapconcat 'identity
				   (seq-uniq (append current-filetags query-target-tags-list))
				   " ")))
    (org-roam-id-open path)
    (org-roam-set-keyword "FILETAGS" new-tags)))
1 Like

Thanks for this and thanks to the other people who replied. I got busy so haven’t really looked into things yet.

Ok. I’ve tried using this and I’m not sure if it is meant for the same thing that I was referring to. I am using org-roam-node-insert, so there is no link for me to place the cursor on, yet. I haven’t yet read about what the various functions do, in your function.

What about after you finish inserting the link :slight_smile:

ezgif-3-5239c2d1f7

I would choose a more concise name for the function – maybe

But i cannot think of one yet. The current function name doesn’t make it obvious what it is doing.

(defun org-roam-id-push-filetags ()
  "Push current buffer's filetags to link at point
link at point must be an id type link."

  (interactive)
    (when-let* ((link (org-element-context))
                (path (org-element-property :path link))
                (query-target-tags (or (org-roam-db-query                 ;; we aditionally check for existing tags
                                        [:select [tags:tag]
                                                 :from tags
                                                 :where (= node_id $s1)]
                                        path)
                                       '((""))))
                (query-target-tags-list (mapcar #'car query-target-tags))
                (current-filetags (or (split-string (or (cadr (assoc "FILETAGS"
                                                                     (org-collect-keywords '("filetags"))))
                                                        "")
                                                    "[: ]" 'omit-nulls)
                                      '("")))
                (new-tags (mapconcat 'identity
                                     (seq-uniq (append current-filetags query-target-tags-list))
                                     " ")))
      (org-roam-id-open path)
      (org-roam-set-keyword "FILETAGS" new-tags)))

More appropriate name


More Versatile version

A more versatile version that can handle both id and roam type links


(defun org-roam-link-push-filetags ()
  "Push current buffer's filetags to link at point.
Link may be of type \"id:\" or \"roam:\"."

  (interactive)
  (when-let* ((link (org-element-context))
              (path (org-element-property :path link))
	      (link-type (org-element-property :type link))
	      (id (cond
		   ((string= "id" link-type) path)
		   ((string= "roam" link-type) (org-roam-node-id
						(org-roam-node-from-title-or-alias path :NOCASE)))
		   (t (user-error
		       "Link type %s is not of either ID or ROAM type, Aborting!" link-type))))
	      (query-target-tags (or (org-roam-db-query                
                                      [:select [tags:tag]
                                               :from tags
                                               :where (= node_id $s1)]
                                      id)
                                     '((""))))
	      (target-tags-list (mapcar #'car query-target-tags))
	      (current-filetags (or (split-string (or (cadr (assoc "FILETAGS"
                                                                   (org-collect-keywords '("filetags"))))
                                                      "")
                                                  "[: ]" 'omit-nulls)
                                    '("")))
	      (new-tags (mapconcat 'identity
                                   (seq-uniq (append current-filetags target-tags-list))
                                   " ")))
    (org-roam-id-open id nil)
    (org-roam-set-keyword "FILETAGS" new-tags)))

Gif showing its inner workings using edebug

ezgif-3-5239c2d1f7

Can you elaborate on what this means? Perhaps the name is not the best.

push implies a stack…

buffers implies many but it applies only to current buffer?

does it “copy current filetags to file of destination at point”?
If the file does not exist, does it create it?

Professor,
I missed an apostrophe - it ought to be buffer’s - typographic error on my part

No it would warn that the file doesn’t exist - there are two places where errors would be caught depending on circumstances.

What do you suggest instead of push. The idea is to “push” to the other file and add to its filetags the filetags of the current buffer.

Correct me if I get it wrong. I would describe it as:

Appends current buffer’s filetags to filetags of file of destination node of link under point.

If the the file of the destination node does not exist

It should also somehow tell that if the referenced file doesn’t have filetags - it would create them with the filetags of this file - although append is workable but the code is tolerant to multiple cases - atleast 4 different circumstances need to be taken care of -

  • when there is no filetags in the current file
  • when there is no filetags in the referenced file
  • when there is no filetags in the current file but there are some filetags in the referenced file
  • when both the current file and the referenced file has filetags.

would append be a suitable word for all these cases?
We should also settle on something that is a little less tongue twister.

If the referenced file doesn’t exist in the database - the error would come from org-roam itself - this error case is taken care of by org-roam

I added an error case when the user evaluates this function on any link that is not an id type or roam link type - we need to get the tags of the referenced file from the database.

I am open to changing both the function name and the description- but it should both be succinct and obvious - I settled on push because colloquially push means to send it somewhere - I don’t know the exact meaning in software development though, either ways - I have found some difficulty in naming this functionality succinctly and in an obvious fashion.

I think all these options are implied. Except for one (more on that below) But you can clarity it by saying something like "tags already in destination are ignored. In a sentence below the main intention.

I find that when reading function descriptions, it is useful to have a brief easy to understand statement (like what we suggested), and then after, details such as what you are describing

Regarding if tags do not exist in current buffer. Does your code end with a message? or it creates a destination with empty filetags?

Oh, and one more: what if I want a new destination node inside a file that already exists? (but the node does not exist yet)

It creates an empty #+filetags: if current file doesn’t have any tags nor the referenced file have any - it is easy enough to change this but I let it happen - since we use when-let* just letting the current filetags be set to nil instead of "" when nil - will terminate the code prematurely - but this is left to the user discretion - it is easy to change the behaviour

node tags are a superset of filetags as well as headline tags - since we are working with filetags this is not relevant - but if youd like such a functionality - I can create a fork of this function - lets talk on DM about this - we shouldn’t burden oncoming users with clarifications.

Maybe @nobiot can suggest a name - but I don’t want to burden him - he is busy already until july - but he is a master of emacs design; I feel he writes functions that are made obvious to the user on first glance - but I am open to name recommendations - in my head append is relevant for the case that there is already tags, and doesn’t clarify enough the other functionalities.