Hello all, and first of all thanks to everybody for this amazing tool!
I’m trying to add a minor org-follow-mode somewhere between org-agenda-follow-mode and org-roam-ui-follow-mode.
While this is not strictly an org-roam question, I’d like to see if this could be activated just for org-roam notes (and I’ll contact the org community as a next step).
My intuition so far was to start from a copy org-agenda-follow-mode (and subcommands), adapting it to an org file being the main window.
(defvar org-follow-mode nil)
(defvar org-pre-follow-window-conf nil)
(defun org-marked-tree-to-indirect-buffer (arg)
;; TODO needed?
;; (interactive "P")
;; (org-agenda-check-no-diary)
(let* ((marker
;;(or
(org-get-at-bol 'org-marker)
;; (org-agenda-error)
;; )
)
(buffer (marker-buffer marker))
(pos (marker-position marker)))
(with-current-buffer buffer
(save-excursion
(goto-char pos)
(org-tree-to-indirect-buffer arg))))
;; TODO needed?
;; (setq org-agenda-last-indirect-buffer org-last-indirect-buffer)
)
(defcustom org-follow-indirect nil
"Non-nil means `org-follow-mode' displays only the current item's
tree, in an indirect buffer."
:group 'org
:version "29.4"
:type 'boolean)
(defun org-do-context-action ()
"Show outline path and, maybe, follow mode window."
(let ((m (org-get-at-bol 'org-marker)))
(when (and (markerp m) (marker-buffer m))
(and org-follow-mode
(if org-follow-indirect
(let ((org-indirect-buffer-display 'other-window))
;; TODO or org-marked-tree-to-indirect-buffer from above
(org-tree-to-indirect-buffer nil))
;; TODO needed?
;; (org-agenda-show)
;; TODO
;; (and org-agenda-show-outline-path
;; (org-with-point-at m (org-display-outline-path org-agenda-show-outline-path)))
)))))
(defun org-follow-mode ()
"Toggle follow mode in a buffer."
(interactive)
(unless org-follow-mode
(setq org-pre-follow-window-conf
(current-window-configuration)))
(setq org-follow-mode (not org-follow-mode))
(unless org-follow-mode
(set-window-configuration org-pre-follow-window-conf))
;; TODO needed?
;; (org-set-mode-name)
(org-do-context-action)
(message "Follow mode is %s"
(if org-follow-mode "on" "off")))
Now this obviously doesn’t work, primarily because I have no org-marker defined as no agenda line is selected.
I then went over the org-roam-ui-follow-mode code, little of which I could use for this as most of the follow-mode looks simpler, directly using the network instead of having to manage windows.
Would anyone have some advice to set this up? How to replace the use of marker and read the current org position instead?
As an illustration, I just produced my Org-agenda buffer (I don’t use it, but just to understand what you want to do). So, as I move along the list of TODO’s in the window above, the window below displays the corresponding location of the originating Org buffer. I imagine you would have TODOs coming from multiple Org-roam nodes.
Here is what I think you want to do:
As I move along my list of TODOs in the agenda, Emacs shows the corresponding location in the Org file (org-agenda-follow-mode)
If the displayed location of Org (the bottom window in my example) happens to be an Org-roam node, I want ORUI to also show the corresponding location in the browser.
I want this to happen automatically without me moving my point to the Org buffer below, so that I can keep moving in the TODO list in the agenda view.
Is this what you wish to do? The direction of the “data” flow is from Emacs to ORUI. I don’t think it would be easy to also control org-agenda-follow-mode from within ORUI’s browser graph display (maybe possible).
Then… It may be much simpler, and would not require a minor mode. Try this. I don’t have org-roam-ui set up in my current Emacs, so I cannot test it. But it looks like this org-roam-ui function does what org-roam-ui-follow-modedoes. And org-agenda-afeter-show-hook is run when your point is inside the node.
Here’s one: both windows show normal org-roam files, but I use the top one as a list of other org-roam notes. I’d like that when I move around in the top window, the bottom window shows the content of the org-roam note I’m over in the top window.
So in the example, the cursor is at the “Current technology seems incompatible with …” link, and the bottom is the contents of that note.
(And please ignore the content – or I can explain properly in a couple weeks once this will have turned into a manuscript)
ORUI and agenda both have similar behaviours: one frame (or ORUI) can follow what is at point in another frame. Both are *-follow-mode.
Indeed, it’s not specific to org-roam, as this could also work with a regular (non-roam) org file, showing the contents (in the “follow” window) of any org link at point (in the main window),
Didn’t take too long, so sharing the result. Try the code below.
I suggest you manually test the command my/follow-link before adding it to post-command-hook. I believe you know what it does, and what it means to use hook locally (?).
It works on my end with the 3 bullet points with links on the left window. The link target files are visited on the right.
The code does not force a placement of the “other” window nor re-configure the original frame state. If you need these features, please investigate further.
You can define a local minor mode to add/remove the my/follow-link (please rename it) to/from post-command-hook.
For testing, you can call M-x remove-hook and manually remove it to tear down the set-up.
(defvar my/follow-preview-buffer nil)
(defun my/follow-link ()
"Automatically open the first link in the current line.
When it is a file to open, the buffer will open in \"other\"
window."
(interactive)
(when (and (member this-command '(previous-line next-line
forward-char backward-char
left-char right-char
org-previous-link org-next-link))
;; This second condition may be part of the minor-mode function
(derived-mode-p 'org-mode))
(when my/follow-preview-buffer (kill-buffer my/follow-preview-buffer)
(setq my/follow-preview-buffer nil))
(save-excursion
;; `cl-letf' is meant to ensure `file-file-other-window' to be used for
;; opening a file by temporarily changing the value of
;; `org-link-frame-setup' during evaluating the body.
(cl-letf (((alist-get 'file org-link-frame-setup)
#'find-file-other-window))
(let ((buffers-before-follow)
(win (selected-window))
(link
(when
(or (org-in-regexp org-link-any-re)
(re-search-forward org-link-any-re
(line-end-position) :t))
;; It seems `org-element-link-parser' must be located at
;; the beginning of the link at point.
(goto-char (match-beginning 0))
(org-element-link-parser))))
(when link
(setq buffers-before-follow (buffer-list))
(org-link-open link)
;; Current buffer is the link target
(unless (member (current-buffer) buffers-before-follow)
(rename-buffer (concat "preview: " (buffer-name)))
(setq my/follow-preview-buffer (current-buffer))))
(select-window win))))))
(add-hook 'post-command-hook #'my/follow-link nil :local)
Edited from the original.
Added a filter so that only cursor movement commands opens the link. Previous, even opening help documentation with “C-h” on the line would call a link
Rename the buffer with prefix "preview: " to the buffer opened newly by this function. If the buffer already exists before following the link, it will be unchanged.
One more note. The files are visited and thus will remain in the buffer list. So if you have many links and move into 30 of the links, your buffer list may feel cluttered with 30+ buffers. If you want to just “preview” the links and kill the buffer when you move to the next one, you’d need to deal with some complexity. You probably want to keep the buffers already open, but kill those this function newly visits as “previews”. I don’t know how this can be done.
Updated the code. See the revised code above. You can ignore the changes if you do not like any of it. I won’t take it personal or anything.
For your TODOs,
a filter so the main frame is the only one in which links are followed
I don’t quite understand what this is (maybe I don’t need to, and leave it to you), but I intended the following part to ensure that the link only opens within another window in the current frame. I only use one frame, so perhaps you are talking about when you already have an existing buffer as a link target in another frame… Also, the hook is local, so it will not affect any other buffer (let alone frames).
Edit: Sorry, what I said was not accurate. It should work. Try the latest code above with moving the cursor. org-next-link and org-previous-link should also open the preview, too.
Thank you. I shutdown my computer after every use, at least daily :).
See the revised code. A version of preview handling is there now — not as sophisticated as how Consult does it but mine seems to work for the purpose here.