Integrating Emacs and Hook, with org-mode

I’m testing out org-mode on spacemacs to deal with my organisational needs. I can easily capture url links into my org-mode documents, but emacs (or spacemacs) doesn’t seem to have access to the hook:// url scheme on my mac and I’m not sure how to make this work.

I’m not sure if it’s a hook question or an emac question, to be honest, but I figured anybody who’s looked into it would be lurking around these parts!

1 Like

Hook works out of the box with several apps. When an app doesn’t work straight out of the box with Hook, if it has AppleScript or JavaScript automation to get /set a few key pieces of information, then we or others can develop integration scripts so that Hook can do its magic with them. That’s summarized here:

Creating Integration Scripts – Hook

If the app doesn’t support AppleScript or JavaScript, we and/or the community investigate whether screen scripting is an option. We reach out to third-party app developers to request they consider adding some automation; automation is easy and benefits everyone. Several developers understand the value of indexing and have added support (e.g., Benny Kjær Nielsen did this for his MailMate app; nvUltra has the right support).

Hook integration scripts are in its Preferences > Scripts pane. There’s an integration server enabling CogSci Apps to make integrations available without users needing to restart the entire app.

We use various editors at CogSci Apps. I haven’t tried emacs / spacemacs with Hook yet. We’ll have a look.

(Aside: when I was a student in the UK [quite some time ago], I used the VED editor (for programming in Pop-11; reading/writing email; reading newsgroups; etc). VED is quite similar to emacs, and I absolutely loved it. VED is written in Pop-11 – a most delightful “classical” AI language. VED is to Pop-11 what emacs was to Lisp. That was on SunOS, Solaris and later Linux. Poplog was ported to Mac OS, but that didn’t survive the transition from PPC to Intel.)

sorry, I didn’t pay close enough attention to who was asking the question! So my answer is quite redundant for you since you’ve been using Hook for a while.

the short of it is that we’ll have a look. As you said, maybe someone on the forum has already looked into it.

No worries - I really should have a look at the scripting options anyway. Also, the link to VED alone was worth it .
Thanks!

1 Like

I’ve never allowed myself to get into emacs (afraid I might have too much fun and never come out of it), but I think the trick would be to derive a file path for the current window / buffer.

(Hook can derive links for text files based on their paths)

The Emacs.app wrapper does implement a basic osascript interface, but it doesn’t return a document value for the front window. You might be able to do something by calling a shell script from inside AppleScript or JS for Automation.

Flying blind here, but I find that in Emacs.app I can use the window name to display the file path of the current buffer using:

(setq frame-title-format '((:eval buffer-file-name)))

and from AppleScript that then enables me to write:

tell application "Emacs"
    name of front window
end tell

to obtain a full file-path.

1 Like

So empirically, as long as the Emacs.app setup includes the incantation:

(setq frame-title-format '((:eval buffer-file-name)))

I find that I can get Hook to make links if I add a Get Address entry for Emacs.app in Hook > Preferences > Scripts along the lines of:

(Using JavaScript for Automation, which makes url-encoding easier than in AppleScript. See the JXA script below the screenshot)

//JavaScript
(() => {
    'use strict';

    const main = () => {
        const fp = Application('Emacs').windows.at(0).name();
        return doesFileExist(fp) ? (
            `[${fp}](${encodeURI('file://' + fp)})`
        ) : ''
    }

    // doesFileExist :: FilePath -> IO Bool
    const doesFileExist = strPath => {
        const ref = Ref();
        return $.NSFileManager.defaultManager
            .fileExistsAtPathIsDirectory(
                $(strPath)
                .stringByStandardizingPath, ref
            ) && 1 !== ref[0];
    };

    return main();
})();
3 Likes

I echo your concern: I keep getting pulled down configuration rabbit-holes for emacs, and org-mode, and I don’t have ten percent of your hacking chops! I know I’m procrastinating on something really big when the emacs urge takes me …

Anyway, I’ll try out your suggestions, but at first glance it seems to me it’s a (clever) solution to the opposite of my problem. I’ve got a hook:///file … url inside an org-mode document, but I can’t access it because emacs won’t recognize it as a valid url scheme. In your case, I think you’ve answered the problem: how to get a link to an emacs buffer i’ve opened.

Got it – sounds like you need something that parses the URL and launches a sub-process to open it.

Perhaps something like the BrowseUrl described here ?

https://www.emacswiki.org/emacs/BrowseUrl

Yes. However:

  • i don’t know the first thing about e-lisp, hence my request for help
  • in my initial overview of BrowseUrl or simple the URL package, it seems the “url schemes” are fairly limited (and hook, sadly, is not one of them) and more importantly, not “expandable” easily …

Frankly, if nobody’s looked into it, it’s fine. I can always open my org files with some other software … And I wouldn’t want you to fall into the rabbit hole!
Thanks for looking.

a couple of work-arounds for unsupported apps:

First, you can paste links into files, assuming the language supports comments. E.g., if it’s a Markdown file, you can add a link like this <!-- [2019-10-20-Hook-in-three-points](hook://file/cU9GBzBji?p=QkxPRy8yMDE5&n=2019-10-20-Hook-in-three-points) -->, which is a link to a Finder folder which can be used by anyone at CogSci Apps who has a copy of the SVN repository in which we store the source of our blog posts. ( WordPress is not enough :slight_smile: ) .

Second, you can paste unique ID’s (which Hook can generate for you in the Gear menu, but any unique ID will do) in a comment, like this <!-- 21U92-G9K77 -->. Then you can create a Search link with it, again via the Gear menu, Copy as Search Link from Clipboard, which yields hook://search/?q=21U92-G9K77. You can then paste those links in the Hook window, or in in fields/files of other apps.

The latter is described here , and elsewhere on this forum. Search links are hardly discussed on the forum, and they’re a bit nerdy (hence a Pro feature); but they’re actually quite handy for many purposes. You can use them to store various spotlight searches into a compact link. You can also put them in .hook files.

I’ve just started using Hook, and as an org-mode fan, I searched for integration with it and was very happy to find this thread!

Thanks @RobTrew for your script - that helped me enormously. I would like to also be able to link to an org headline in the way this is possible when you store an org-link within Emacs, but I can work on that.

I think I have found a solution for @seishonagon too: you can add custom URL formats to emacs org-mode fairly easily which use the macOS command open to open the hook: URL.

What you need to do is add the following code somewhere in your init.el file (or wherever you customise Emacs in your setup):

(defun my/hook (hook)
  "Create an org-link target string using `hook://` url scheme."
  (shell-command (concat "open \"" hook "\"")))

  (org-add-link-type "hook" 'my/hook)

The function tells Emacs what to do when you click a custom hook:// link. We just wrap the hook URL in double quotes and then use the open command to open it. The last line adds our new link type and tells it to call the function we have just written to handle those links.

There are fancier ways of building these links when allow autocompletion of possibilities and so on, but this works well enough for me.

Once you have restarted Emacs (or just executed that code) you should be able to call (org-insert-link) (however you normally call it), then filter the list to find your new hook link. Paste the URL in and hit return, then add a description when prompted. Now you should be able to click that link and be taken to the file linked by Hook.

I’ve also set up a template.org file in the custom templates folder and set that as the default for notes, so I am now a really happy bunny! Thanks so much for Hook Luc - it’s a really clever way to save each context of your work, and I’m really happy I can now use it with Emacs!

6 Likes

Ha, @bsag, from micro.blog to hook!

Thanks for the tip. I will try … once I reinstall emacs. It’s really too much of a rabbit hole for me, and I was clearly procrastinating :frowning_face_with_open_mouth: Although I’m this :pinching_hand: close to reinstalling it again!

:wave:

No problem - I know the rabbit hole well, and understand the reasons for wanting to avoid it :wink:

1 Like

I edited my init.el file with:

(defun my/hook (hook)
  "Create an org-link target string using `hook://` url scheme."
  (shell-command (concat "open \"" hook "\"")))

  (org-add-link-type "hook" 'my/hook)

I can see the link in org mode, but Emacs shows the following error when I try to open the hook link:

The file //file/qqjclb7B1p=d2VpaGFua290LORlc2tb3A=sn=orgcard.pdf does not exist.

image

The hook link works when opened from other apps.

Do I need to do anything else to get hook:// links to work in org mode?

Thanks!

Welcome to the Hook Productivity Forum , @finesc. and thanks for asking

Just had a quick look (and am not a Lisp dev), but what needs to happen is for macOS to be asked to open a URL of the form hook://file/qqjclb7B1?p=... (where … is additional text provided by Hook) whereas what the code seems to yield is a string of the form //file/... .

Thanks for the reply, Luc! The updated snippet below is working:

(defun my/hook (hook)
  "Create an org-link target string using `hook://` url scheme."
  (shell-command (concat "open hook:\"" hook "\"")))

  (org-add-link-type "hook" 'my/hook)

P.S. I just listened to the recent Mac Power Users episode on which you shared your workflows. Super cool! Thanks for that – and for creating Hook!

3 Likes

Thanks for sharing your code! I am sure many people will use it. And thanks for the kind words about the MPU episode.

In the MPU episode I mention Pop-11. It’s similar in many ways to Lisp, though the syntax is different and has lots of unique features. And it too has its own editor: VED. In fact, the POPLOG system containing Pop-11 (the core language) also supports Lisp, ML and Prolog. I haven’t used it in years however. But I’m on their mailing list and there is still some activity. Poplog was available for a while on the Mac, but that was in the days before the shift to Intel.

This works beautifully. Thank you.

I’m using Doom so my modification went into config.el with something like this/snipping my other modifications to org:

(after! org 

(defun my/hook (hook)
  "Create an org-link target string using `hook://` url scheme."
  (shell-command (concat "open hook:\"" hook "\"")))
  (org-add-link-type "hook" 'my/hook)

  )

I think I have working Hookmark integration with the Doom Emacs config. It allows linking to text files in emacs. It also allows linking to org-mode tasks both from and org file and from the org-agenda. It likely can be improved…

Update “/opt/homebrew/bin/emacsclient” in the scripts below to the path that emacsclient is at. From a terminal type which emacsclient to know what to use.

Add the following code to Hookmark:

;; Open Item Script
do shell script "/opt/homebrew/bin/emacsclient --eval '(hookmark-open-address \"$0\")'"

Also add “org-hookmark” as the URL scheme.

;;Get Name Script

set emacsLink to do shell script "/opt/homebrew/bin/emacsclient --eval '(hookmark-get-name)'"
set newLink to text 2 thru -2 in emacsLink
return newLink

;; Get Address Script

set emacsLink to do shell script "/opt/homebrew/bin/emacsclient --eval '(hookmark-get-address)'"
set newLink to text 2 thru -2 in emacsLink
return newLink

This works for Doom Emacs. It’ll need to be adapted for other configs. Make sure emacs server is running.

Add the following to your Emacs config.

(after! org

;; Open `hook` links from org-mode files.
  (defun hookmark-org-link (link)
    "Create an org-link target string to LINK using `hook://` url scheme."
    (shell-command (concat "open hook:\"" link "\"")))

  (org-link-set-parameters "hook"
                           :follow #'hookmark-org-link)

;; Helper functions.
  (defun hookmark--org-todo-path nil
    (let ((address))
      (cond
       ((derived-mode-p 'org-agenda-mode)
        (let* ((col (current-column))
               (hdmarker (org-get-at-bol 'org-hd-marker))
               (buffer (marker-buffer hdmarker))
               (pos (marker-position hdmarker))
               (inhibit-read-only t))
          (org-with-remote-undo buffer
            (with-current-buffer buffer
              (widen)
              (goto-char pos)
              (org-fold-show-context 'agenda)
              (setq address (org-id-get-create)))
            (org-move-to-column col))))
       ((derived-mode-p 'org-mode)
        (setq address (org-id-get-create))))

      (princ address)))

  (defun hookmark--org-todo-todo nil
    (let ((todo))
      (cond
       ((derived-mode-p 'org-agenda-mode)
        (let* ((col (current-column))
               (hdmarker (org-get-at-bol 'org-hd-marker))
               (buffer (marker-buffer hdmarker))
               (pos (marker-position hdmarker))
               (inhibit-read-only t))
          (org-with-remote-undo buffer
            (with-current-buffer buffer
              (widen)
              (goto-char pos)
              (org-fold-show-context 'agenda)
              (setq todo (substring-no-properties (org-get-heading t t t t)))
              (org-move-to-column col)))))
       ((derived-mode-p 'org-mode)
        (setq todo (substring-no-properties (org-get-heading t t t t)))))

      (princ todo)))

;; Main functions.
  (defun hookmark-get-address nil
    "Get buffer path or org ID."
    (switch-to-buffer (car (buffer-list)))
    (cond
     ((or (derived-mode-p 'org-agenda-mode) (derived-mode-p 'org-mode))
      (princ (format "org-hookmark://%s"
                     (hookmark--org-todo-path))))
     (t
      (princ (format "%s" (buffer-file-name (car (buffer-list))))))
     ))

  (defun hookmark-get-name nil
    "Get buffer filename or org TODO text."
    (switch-to-buffer (car (buffer-list)))
    (cond
     ((or (derived-mode-p 'org-agenda-mode) (derived-mode-p 'org-mode))
      (princ (format "TODO %s"
                     (hookmark--org-todo-todo))))
     (t
      (princ (format "%s" (buffer-name (car (buffer-list))))))
     ))

  (defun hookmark-open-address (address)
    "Open ADDRESS provided by the Hookmark app."
    (org-link-open-from-string (format "[[id:%s]]" (nth 1 (split-string address "//"))))
    )
  )
1 Like

Dear seishounagon,
I have seen your name on the Tinderbox forum from time to time.
(Provided the same nickname doesn’t exist elsewhere.)
Dear brigard,
It seems I discovered your posts here too late.
I am running Mac OS 10.14.6 Mojave.
I use Spacemacs with Emacs-plus@28.
I was thinking of moving from Sapcemacs to Tinderbox 9?
Still using Spacemacs with org-roam and org-roam-ui.
I like it very much.
I also found an article that allows me to use org from this Hookmark.
I found, so to completely switch to Tinderbox
I’m sure it will take some more time to completely switch to Tinderbox.

Dear brigard,
I’ve been trying to get the part of your code that follows the shell script to work.
/usr/local/Cellar/emacs-plus@28/28.2/bin/emacsclient
and rewrite it to use it.
I never thought it would be possible to use Hookmark with Spacemacs.

My question is: Where does "Add the following to your Emacs config.
Where do I put it?
I tried Doom Emacs once, but have now returned to Spacemacs.
I have returned to Spacemacs.
If I have any questions, I will post them here again.
Thank you for your continued support.
Respectfully yours, WAKAMATSU kunimitsu

P.S.
Normal copy links work with Spacemacs ver. 0.999 and
Jumping to the pasted site by clicking on the URL is successfully done.
Jumping from links displayed by org-roam-ui is also confirmed in Safari.

1 Like