464 lines
19 KiB
Org Mode
464 lines
19 KiB
Org Mode
** User:
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created (header)
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED: property."
|
||
; Get user input for the header
|
||
(interactive "sHeader: ")
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer)))))
|
||
|
||
|
||
** Ellama:
|
||
This function moves an Org header to an Org-roam-dailies file based on its :CREATED: property. It is
|
||
an interactive function so it will ask for user input when executed.
|
||
|
||
There are a few important parts of this function:
|
||
- The ~let*~ statement is used to define local variables.
|
||
- The ~org-entry-get~ function gets the value of a property from the current Org header.
|
||
- The ~org-read-date~ function converts a string to a time value (also note that it is wrapped in an
|
||
~and~ function to avoid errors when the property doesn't exist).
|
||
- The ~when~ statement is a conditional statement which will only execute its body when the
|
||
preceding condition (in this case, ~(date)~) is non-nil.
|
||
- The ~save-excursion~ function saves the current buffer and point, and restores them after the body
|
||
(everything in the parentheses after ~save-excursion~) is executed. The ~org-cut-subtree~ function
|
||
cuts the current header and all nested elements.
|
||
- The ~org-roam-dailies-find-date~ function opens the daily org-roam file for the given date.
|
||
- The ~goto-char~ and ~newline~ functions move the point to the end of the buffer and create a new
|
||
line.
|
||
- The ~org-paste-subtree~ function pastes the cut header at the point. Finally, the ~save-buffer~
|
||
saves the changes.
|
||
|
||
** User:
|
||
Does this work on the org header at point?
|
||
|
||
** Ellama:
|
||
Yes, when this function is run, it will operate on the org header that the point (cursor) is
|
||
currently on. Here's how it does this:
|
||
|
||
- ~(interactive "sHeader: ")~ prompts you for the header name, but it actually doesn't do anything
|
||
with this input because the function principal operation revolves around the point.
|
||
- ~(org-entry-get (point) "CREATED")~ gets the 'CREATED' property of the current org header at the
|
||
point.
|
||
- ~(org-cut-subtree)~ cuts the org header at the current point, including all its contents and
|
||
nested elements.
|
||
- ~(org-paste-subtree)~ pastes the cut org header at the end of the org-roam-dailies file.
|
||
|
||
Throughout all these operations, the org header that your point was originally on is the one being
|
||
manipulated.
|
||
|
||
** User:
|
||
#+begin_src elisp
|
||
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created (header)
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED: property."
|
||
; Get user input for the header
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading")
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer))
|
||
)
|
||
)
|
||
(message "Moved header to the corresponding org-roam-dailies file.")
|
||
)
|
||
#+end_src
|
||
|
||
|
||
** Ellama:
|
||
This updated version of the function includes an additional check to make sure that the function is
|
||
called while the point is on an Org heading. It does so using the ~(org-at-heading-p)~ function,
|
||
which returns ~t~ if the point is on a heading and ~nil~ otherwise. If the point is not on a
|
||
heading, the function will ~error~ out with the message "Not at an org heading".
|
||
|
||
It also removes the request for user input for a header; making this function completely based on
|
||
the location of the point in your Org file.
|
||
|
||
Additionally, this version of the function gives a confirmation message ("Moved header to the
|
||
corresponding org-roam-dailies file.") when the refile process is complete, using the ~(message)~
|
||
function. This can give you confirmation that the function has done its job.
|
||
|
||
Like the original, this function will cut the entire Org subtree at the point (header and all
|
||
content beneath it), and paste it into the Org-roam-dailies file as per the date found in the
|
||
~CREATED~ property of the heading.
|
||
|
||
Do note that the 'ERROR' might stop the function's execution and thus might prevent the 'MESSAGE'
|
||
function to run. If you would like to ensure that a message is always displayed, consider using a
|
||
~condition-case~ error handling block.
|
||
|
||
** User:
|
||
#+begin_src elisp
|
||
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created (header)
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED: property."
|
||
; Get user input for the header
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading")
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer))
|
||
)
|
||
)
|
||
(message "Moved header to the corresponding org-roam-dailies file.")
|
||
)
|
||
#+end_src
|
||
|
||
|
||
** Ellama:
|
||
There seems to be an error with the parenthesizing of your ~unless~ clause. The closing parenthesis
|
||
should be right after the ~(error "Not at an org heading")~ to properly encase the error condition.
|
||
|
||
Here is the corrected code:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created ()
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED:
|
||
property."
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading"))
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer))
|
||
)
|
||
)
|
||
(message "Moved header to the corresponding org-roam-dailies file.")
|
||
)
|
||
#+END_SRC
|
||
As originally stated, this error checking with ~unless~ ensures that the function will stop
|
||
executing and display an error message if it is not currently positioned on an org heading.
|
||
|
||
** User:
|
||
#+begin_src elisp
|
||
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created (header)
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED: property."
|
||
; Get user input for the header
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading")
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer))
|
||
)
|
||
)
|
||
(message "Moved header to the corresponding org-roam-dailies file.")
|
||
)
|
||
#+end_src
|
||
|
||
|
||
** Ellama:
|
||
I notice that you still have the argument ~(header)~ in the function definition but it is not being
|
||
used within the function. Since you are removing the need for the user to specify a header and are
|
||
instead working with the header at the current point, you can remove that argument from the function
|
||
definition.
|
||
|
||
Also, you still have a missing closing parenthesis for the ~unless~ scope. I corrected it below:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created ()
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED:
|
||
property."
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading"))
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer)))
|
||
(message "Moved header to the corresponding org-roam-dailies file.")
|
||
)
|
||
#+END_SRC
|
||
When you call the function, it checks whether the current point is at an org heading, throws an
|
||
error if not, and if yes, it extracts the "CREATED" property and uses that date to decide where to
|
||
refile the current org header. After refiling, it gives a success message.
|
||
|
||
** User:
|
||
I'd like the org-roam-dailies node to always follow the following template `(plain ""
|
||
:if-new (file+head "%<%Y-%m-%d>.org"
|
||
"#+title: %<%Y-%m-%d>\n\n"))
|
||
|
||
** Ellama:
|
||
To achieve that, you need to configure the ~'org-roam-capture-templates'~ variable with your custom
|
||
template. Below is the general way to configure the dailies capture templates:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-roam-dailies-capture-templates
|
||
'(("d" "daily" plain
|
||
(function org-roam-capture--get-point)
|
||
""
|
||
:file-name "daily/%<%Y-%m-%d>"
|
||
:head "#+title: %<%Y-%m-%d>\n\n"
|
||
:immediate-finish t)))
|
||
#+END_SRC
|
||
|
||
This configuration will set up a ~org-roam-dailies~ capture template that creates a new file for
|
||
each day in the ~daily/~ directory of your org-roam directory with the name in ~YYYY-MM-DD~ format,
|
||
and use ~#+title: YYYY-MM-DD~ as the file's heading line. The file is created immediately without
|
||
prompting for additional input (~:immediate-finish t~).
|
||
|
||
You can adjust the path and the file format according to your needs.
|
||
|
||
** User:
|
||
Will the function make sure the org-roam-dailies node has a #+title following the template?
|
||
|
||
** Ellama:
|
||
No, the function itself doesn't explicitly manage how org-roam-dailies nodes are created or
|
||
formatted.
|
||
|
||
The formatting of a newly created org-roam-dailies node is governed by the
|
||
~org-roam-dailies-capture-templates~ setting, as in the previous example.
|
||
|
||
If you set the ~org-roam-dailies-capture-templates~ to include the specific title format you
|
||
mentioned (like the example in the previous response), then yes, when the function calls
|
||
~org-roam-dailies-find-date~ to create/fetch a daily node, it should follow the defined template.
|
||
|
||
However, if the date node already exists and hence is not newly created by the function call, its
|
||
title would not be affected/changed by the function call.
|
||
|
||
Please note that you will need to set the ~org-roam-dailies-capture-templates~ outside of this
|
||
function, in your overall configuration. The function itself does not alter that variable; it only
|
||
makes use of the setting (via ~org-roam-dailies-find-date~).
|
||
|
||
** User:
|
||
When I run the function on a header nothing happens. this is the function as it stands #+begin_src elisp
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created-date ()
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED: property."
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading")
|
||
)
|
||
(let* (
|
||
; Get the CREATED property at point
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Convert the CREATED property to a date
|
||
(date (and created
|
||
(org-read-date nil t created))))
|
||
; Check if date exists
|
||
(when date
|
||
(save-excursion
|
||
; Cut the subtree
|
||
(org-cut-subtree)
|
||
; Find a daily org-roam node based on the date
|
||
(org-roam-dailies-find-date date)
|
||
; Go to the end of the node
|
||
(goto-char (point-max))
|
||
; Add a new line
|
||
(newline)
|
||
; paste the subtree
|
||
(org-paste-subtree)
|
||
; Save the buffer
|
||
(save-buffer))
|
||
)
|
||
)
|
||
(message "Moved header to the corresponding org-roam-dailies file.")
|
||
)
|
||
#+end_src
|
||
|
||
** Ellama:
|
||
Looking at your code, it seems to be perfectly fine logically and should work if you have an
|
||
org-roam-dailies file for the corresponding date already available.
|
||
|
||
Possible reasons why nothing happens could be that either org-mode is not recognising the CREATED
|
||
property as a valid date (org-mode expects dates in the format <YYYY-MM-DD>), or there isn't a
|
||
corresponding org-roam-dailies file for that date.
|
||
|
||
Here's a way to debug this:
|
||
|
||
- Print the CREATED date to the mini-buffer and check whether it is valid or not. You can do this by
|
||
adding ~(message "CREATED: %s" created)~ just after the ~let*~ line.
|
||
- Add additional error handling to check if the corresponding org-roam-dailies file exists.
|
||
|
||
Try adding some debugging points within the function to see where it's stopping.
|
||
|
||
Also, org-roam-dailies-find-date creates new the file if it does not exist. If your org-roam files
|
||
follow a different format or reside in a different directory this function may not be creating the
|
||
file at the location you expect.
|
||
|
||
Make sure ~org-roam-directory~ and ~org-roam-dailies-directory~ are set correctly in org-roam setup.
|
||
|
||
** User:
|
||
this is the date property from the file I am trying to move :CREATED: [2009-05-15 Fri 23:51]
|
||
|
||
** Ellama:
|
||
The date format ~"[2009-05-15 Fri 23:51]"~ contains both date and time but your function is parsing
|
||
this as only date. Therefore, the format of the date is causing an issue.
|
||
|
||
We need to convert date and time format into the expected date only format.
|
||
|
||
Modify your function to extract the date from CREATED property in the following way:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my/org-refile-header-to-org-roam-dailies-by-created-date ()
|
||
"Move the current org header to the corresponding org-roam-dailies file based on its :CREATED:
|
||
property."
|
||
(interactive)
|
||
(unless (org-at-heading-p)
|
||
(error "Not at an org heading"))
|
||
(let* (
|
||
; Get the CREATED property at point and parse it to the proper datetree format
|
||
(created (org-entry-get (point) "CREATED"))
|
||
; Extract the date and ignore the time
|
||
(date-string (when created
|
||
(nth 0 (split-string created " "))))
|
||
; Convert the string to a date
|
||
(date (and date-string
|
||
(org-read-date nil t date-string))))
|
||
(when date
|
||
(save-excursion
|
||
(org-cut-subtree)
|
||
(org-roam-dailies-find-date date)
|
||
(goto-char (point-max))
|
||
(newline)
|
||
(org-paste-subtree)
|
||
(save-buffer))))
|
||
(message "Moved header to the corresponding org-roam-dailies file."))
|
||
#+END_SRC
|
||
In this modified version, ~split-string~ function is used to split the CREATED property value into
|
||
date and time. Then, the function only looks at the date part and converts it into date datatype
|
||
using ~org-read-date~. It should work with your CREATED format.
|
||
|
||
|
||
** User:
|
||
I already have a file named 2009-05-15.org but the header was not killed or yanked into it
|
||
|
||
** Ellama:
|
||
It's interesting that it's not working as intended, as from the code it looks good. Here are a few
|
||
things to troubleshoot:
|
||
|
||
1. Ensure that the ~org-roam-dailies-directory~ is correct. It needs to point to where your daily
|
||
files are located.
|
||
|
||
2. Ensure that the ~org-roam-dailies-capture-templates~ is configured correctly. If it's not
|
||
configured or is configured incorrectly, org-roam might not be able to find or create the new daily
|
||
files.
|
||
|
||
3. Add debug prints in your function, similar to this:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(message "Date string: %s, Date: %s" date-string date)
|
||
#+END_SRC
|
||
After the let* clause. This will print to the minibuffer the date string extracted from CREATED
|
||
property and the Date after ~org-read-date~.
|
||
|
||
4. Run your function in debug mode. You can do this by running ~M-x debug-on-entry <function-name>~.
|
||
Replace ~<function-name>~ with the name of your function. After setting this, every time you run
|
||
your function, it will open debugger and you can see the actual values of your variables and step
|
||
through the function.
|
||
|
||
5. Check if any error message is printed to the Messages buffer when running your function.
|
||
|
||
6. Make sure that the header you are trying to refile has a CREATED date property which is not nil
|
||
and is in the correct format.
|
||
|
||
Please check these points, hopefully, it's one of these causing the issue.
|
||
|
||
** User:
|
||
|
||
I got this debugging message helm-M-x-execute-command: Symbol’s value as variable is void: date-string
|
||
|
||
** Ellama:
|