7

So I'm trying to make an "autolinks mode" to emulate the functionality of notetaking software like Tomboy Notes or notes.vim, which automatically linkifies any text that happens to be the title of a note. But I'm totally new to Emacs and to Elisp. This is what I have so far (it's like the first elisp I've ever written, be gentle):

;; Set directory of files to search for *.org files 
(setq autolinks-mode-dir "~/Dropbox/Org/")

;; Get all .org files in this directory. 
(setq autolinks-mode-files (directory-files autolinks-mode-dir t "[.]org\\'"))

;; Convert it to a list of just the filenames, without paths.
(setq autolinks-mode-files-bare
      (mapcar 'file-name-nondirectory autolinks-mode-files))

;; Strip extensions
(setq autolinks-mode-topics
      (mapcar 'file-name-sans-extension autolinks-mode-files-bare))

So far, so good. Now I I have a list of topics, and a list of corresponding files. For example, the topic "James" corresponds to an org file at "/path/to/notes/James.org". Now I want to manually update org's list of links for a particular buffer with this list of topics, so that, when I turn on this mode, it highlights any instance of the word "James" in an org buffer and makes it a link to /path/to/notes/James.org. Same with a multi-word org file, like "Henry James", which would point to /path/to/notes/Henry James.org. Note that I'm not trying to replace all instances of James with [[James]] or <James> or something, just trying to get those text snippets to behave as links.

I've been browsing through org.el, and I saw this variable:

(defvar org-link-re-with-space nil
  "Matches a link with spaces, optional angular brackets around it.")

Which in my system has all kinds of stuff in it, probably due to org-ref and other things:

org-link-re-with-space is a variable defined in ‘org.el’.
Its value is
"<?\\(A\\(?:cp?\\|utocite[*s]?\\)\\|Cite\\(?:a\\(?:l[pt]\\|uthor\\*?\\)\\|[pst]\\)?\\|Gls\\(?:pl\\)?\\|Notecite\\|P\\(?:arencites?\\|notecite\\)\\|Smartcites?\\|Textcites?\\|a\\(?:c\\(?:p\\|r\\(?:full\\|long\\|short\\)\\)?\\|ddbibresource\\|uto\\(?:cite[*s]?\\|ref\\)\\)\\|b\\(?:bdb\\|ib\\(?:entry\\|liography\\(?:style\\)?\\|tex\\)\\)\\|cite\\(?:a\\(?:l\\(?:[pt]\\*\\|[pt]\\)\\|uthor\\*?\\)\\|date\\*?\\|num\\|p\\*\\|t\\(?:\\*\\|ext\\|itle\\*?\\)\\|url\\|year\\*?\\|[*pst]\\)?\\|do\\(?:cview\\|i\\)\\|e\\(?:l\\(?:feed\\|isp\\)\\|qref\\)\\|f\\(?:ile\\(?:\\+\\(?:\\(?:emac\\|sy\\)s\\)\\)?\\|notecite\\|oot\\(?:cite\\(?:s\\|texts?\\)?\\|fullcite\\)\\|tp\\|ullcite\\)\\|g\\(?:ls\\(?:desc\\|link\\|\\(?:p\\|symbo\\)l\\)?\\|nus\\)\\|h\\(?:elp\\|ttps?\\)\\|i\\(?:n\\(?:dex\\|fo\\)\\|pynb\\|rc\\)\\|l\\(?:abel\\|ist-of-\\(?:\\(?:figur\\|tabl\\)es\\)\\)\\|m\\(?:ailto\\|\\(?:essag\\|h\\|u4\\)e\\)\\|n\\(?:ameref\\|ews\\|o\\(?:bibliography\\|\\(?:te\\)?cite\\)\\)\\|orgit\\(?:-\\(?:log\\|rev\\)\\)?\\|p\\(?:a\\(?:geref\\|rencite[*s]?\\)\\|notecite\\|rint\\(?:bibliography\\|index\\)\\)\\|r\\(?:ef\\|mail\\)\\|s\\(?:hell\\|martcites?\\|upercites?\\)\\|textcites?\\|w3m\\):\\([^]    

So it seems Org has the functionality to be able to highlight plain text as a link. I just don't know the best way to do it. I think I can append my list of link words to org-link-re-with-space, maybe? But then how do I set their targets? Any help would be much appreciated.

  • You probably cannot use org-links for this. It sounds like you want to use font-lock to get the behavior you want. You might find some inspiration here kitchingroup.cheme.cmu.edu/blog/2015/06/22/…. In particular the use of button-lock-set-button to make some patterns clickable. – John Kitchin May 22 '17 at 0:16
  • I am not sure I understand completely what you are trying to do, but why not just replace all occurences of James with [[file:/path/to/notes/James.org][James]] in the buffer. Then it will appear as a link if the buffer is in org-mode major mode. – Håkon Hægland Jun 21 '17 at 21:03
  • 1
    The idea is not to make new links, but to automatically emulate links for any filename. That way, I can delete James.org, change its name, and not have to worry about updating the link. Hard linking is silly compared to autolinking. – Jonathan Jun 21 '17 at 23:52
1

I believe this is essentially what you want:

;; Set directory of files to search for *.org files
(setq autolinks-mode-dir "/Users/jkitchin/vc/journal/2018/08/07/")

;; Get all .org files in this directory.
(setq autolinks-mode-files (directory-files autolinks-mode-dir t "[.]org\\'"))

;; Convert it to a list of just the filenames, without paths.
(setq autolinks-mode-files-bare
      (mapcar 'file-name-nondirectory autolinks-mode-files))

;; Strip extensions
(setq autolinks-mode-topics
      (mapcar 'file-name-sans-extension autolinks-mode-files-bare))

(button-lock-set-button
 (regexp-opt autolinks-mode-topics)
 (lambda (event)
   (interactive "e")
   (let ((click-pos (posn-point (event-end event))))
     (save-mark-and-excursion
      (save-match-data
    ;; This is clunky, but with grouping the properties may not extend to
    ;; the whole regexp, e.g. if you set properties on a subgroup.
    (goto-char (previous-single-property-change click-pos
                            'button-lock))
    ;; now make sure we see it again to get the match-data
    (while (not (looking-at (regexp-opt autolinks-mode-topics)))
      (backward-char))
    (find-file (expand-file-name
            (concat (match-string 0) ".org")
            autolinks-mode-dir))))))
 :face (list 'org-link))

You could embed that in a function, and then call it in an org-mode-hook and turn on button-lock-mode. That should make your topics look like links, and when you click on them it will open the file. One possible hiccup is the order that the topics are searched. You want to make sure "Henry James" is searched before James, which may take some sorting by length or something.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.