I have a regular expression and an path, e.g. ("[Bb]ackup" . "/home/user/backup.org")
. Now I want every substring in the buffer that matches the regexp to look like an org-link and to be clickable so that a click opens the file. (I can't use actual org-links because it needs to work in non-org buffers as well.) I know about make-button
but I'd like links to be created (or removed) dynamically as I type similar to syntax highlighting. make-button
can't do that as far as I can see since you don't specify a regexp but the start and end point of the button in the buffer and you'd have to recalculate these yourself on each button press. highlight-regexp
looks close to what I want but afaics it only allows me to apply faces, so it can't make clickable links.
If you want a straight font-lock approach, I think this is what you want.
(defun next-notes (limit)
(when (re-search-forward "[Nn]otes" limit t)
(let ((map (make-sparse-keymap)))
(define-key map [mouse-1]
(lambda ()
(interactive)
(find-file "~/notes.org")))
(set-text-properties
(match-beginning 0) (match-end 0)
`(local-map ,map mouse-face 'highlight
help-echo "mouse-1: click me"))
t)))
(font-lock-add-keywords
nil
'((next-notes (0 'link t)))
t)
You can also use button-lock-mode:
(cl-loop for (regex . path) in '(("[Cc]heatsheet" . "/Users/jkitchin/backup.org"))
do
(button-lock-register-global-button
regex
`(lambda (event)
(interactive "e")
(find-file ,path))
;; I like to press return on functional text to activate it.
:keyboard-binding "RET"
;; These are the rest of the properties
:face (list 'link)
:help-echo (format "Click me to open %s" path)))
(button-lock-mode +1)
It works (lightly tested) I think. It is sort of tedious to debug, I am not sure if (button-lock-clear-all-buttons)
actually clears everything, and had to restart emacs a few times while testing it.
I wrote https://github.com/jkitchin/scimax/blob/master/scimax-functional-text.el sort of for this purpose. Hre is what it looks like. It feels a little clunky to use eval here, but it is a macro, and variables don't seem to expand in the call the right way. If anyone knows why or how to fix it, I would love to know. It has been a long time since I used this often, so it probably could use some attention.
(require 'scimax-functional-text)
(cl-loop for (regex . path) in '(("notes" . "~/notes.org")
("[bB]ackup" . "~/backup.org"))
do
(eval
`(scimax-functional-text ,regex ((find-file ,path)) :face '(link))))
(button-lock-mode +1)
-
This looks very promising. I tried to hack something with font-lock but failed to make the links clickable. I then thought I'd have to settle for overlays but your answer suggests that font-lock can do the job after all. Great! – tmalsburg Jan 11 at 15:31
-
1I added a simpler font-lock example, and simplified the button code too. Since you don't use any information about what you are looking at, it isn't necessary to do the complicated search that was done before. – John Kitchin Jan 11 at 15:57
-
Amazing! I would never have been able to come to this solution by reading the font-lock documentation (
font-lock-keywords
) which is either incomplete or too cryptic for me to understand. – tmalsburg Jan 12 at 12:17
post-self-insert-hook
so that the links are ready after every character insertion, but I suspect that then you'll have to wait for characters to appear, rather than links. I don't see a win-win solution here, only a trade-off. – NickD Jan 11 at 16:29