org-protocol intercepts calls from emacsclient to trigger custom actions without external dependencies. Please refer to this file for the basic setup required.
You might want to watch the screencast on youTube.
Defining custom handlers
org-protocol scans the list of filenames passed to the emacs-server for
org-protocol:/sub-protocol:/" and triggers actions associated with
sub-protocol through the custom variable
To defun a custom org-protocol handler basically means to define two basic elements:
- a sub-protocol that triggers the action
- a function that consumes the data (i.e. the part of an URL that follows
To install the custom handler's protocol, we add an entry to
(add-to-list 'org-protocol-protocol-alist '("Hello World" :protocol "hello-world" :function my-hello-world))
:protocol property is the sub-protocol, that triggers the action. Note,
that names of protocols (or URL schemes) are only allowed to consist of a
restricted set of characters. See rfc1738, section 2.1.
:function is an arbitrary function that takes exactly one argument: the
string that follows our protocol, found in a filename passed to emacs through
emacsclient. All the three standard handlers split and decode that string
using a helper function in
org-protocol-split-data (data &optional unhexify separator)
You may use different separators for your custom handlers and pass them to
Here is a simple definition:
(defun hello-world (data) "Say hello to the world." (message data) nil)
Now the URL
org-protocol://hello-world://encoded-data will call our function
with the string "
encoded-data". Hence an
will put "
encoded-data" into the minibuffer.
Killing the client
If your handler uses interactive functions that could be canceled by the user
by typing '
C-g', consider to supply the '
:kill-client' property when you
define the protocol.
This is what we did for the capture handler:
(defconst org-protocol-protocol-alist-default '(("org-capture" :protocol "capture" :function org-protocol-capture :kill-client t) ;; ... ))
Otherwise, if the user has an interactive property defined in her capture
template, discarding it through '
C-g' would lead to emacsclient waiting for
ever, thus to the appropriate questions when exiting emacs.
All filenames passing from emacsclient to the emacs will be ignored if you
:kill-client to a non-nil value.
Note, that our
hello-world handler explicitly returns
nil. This tells
org-protocol to remove the filename from the list of files passed to the
emacs-server. If more than one filename was supplied, all those filenames are
searched for protocols. Only filenames without protocols are passed to the
emacs-server as usual.
Another possible return value is a string. If the string is a valid filename,
and if that file can be read,
org-protocol replaces the original filename with
the one returned from the handler.
Using more than one value
Passing one argument to our custom handler is nice, but sometimes more parameters are needed. We would have to encode the the data and split it into parts using a separator.
This is where
org-protocol-split-data comes into play. It takes a string as
its first argument, an optional parameter to tell if the string should be
considered URL-encoded UTF-8 text and finally an optional separator. By
default, no URL-encoding is assumed and '
/' is used as the separator.
The return value is a list of strings. If a non-nil value is supplied as the
second argument, each elements of the returned list will be URL-decoded using
org-protocol-unhex-string. If the second argument is a function, that function
is used to decode each element of the list. The function should take a string
as its only parameter, and return the decoded value 1.
This is a rewrite of our handler:
(defun hello-world (data) "Say hello to the world." (let* ((parts (org-protocol-split-data data nil '::my-separator::')) (one (car parts)) (two (cadr parts)) (three (caddr parts))) ;; ... do something with one, two and three ) nil)
Using more than one value the greedy way
Finally, it is possible to define a greedy handler. Basically it will discard all the filenames from the servers list of files that follow the filename that triggered the handler.
A handler is greedy, if you add the
:greedy property to
org-protocol-protocol-alist, regardless of its return value:
(add-to-list 'org-protocol-protocol-alist '("Greedy" :protocol "greedy" :function my-greedy-handler :greedy t))
The one argument to greedy handlers is the rest of the list of filenames, the one that triggered the handler included. But read on, please.
The list of filenames
Here I have to admit, that I was lying all the time. emacsclient does not pass a list of filenames to the emacs-server. It's a list of lists. And the list is the list of emacsclient's arguments reversed.
As an example, the following commandline:
emacsclient org-protocol:/greedy:/one two three +15:42
is passed as
((/dir/three (15 . 42)) (/dir/two) (/dir/org-protocol:/greedy:/one))
to the emacs-server, where
org-protocol grabs it and reverses it to make it
look like this:
((/dir/org-protocol:/greedy:/one) (/dir/two) (/dir/three (15 . 42)))
This is now, what our greedy handler will receive as its only parameter.
/dir/" prefix is added by emacsclient. It's the absolute path to its
You may set
nil to inhibit the
reversion. But that leads to unexpected results. In this example, the only
filename left would be the one that triggered the actions. That seems not
very greedy, and reversing the arguments on the commandline seems
unnatural. Note though, that the sequence is not changed for the server.
Flatten the list of arguments
org-protocol.el provides a function to flatten the list of arguments for
org-protocol-flatten-greedy (param-list &optional strip-path replacement)
This function takes the list of lists your greedy handler gets as its only parameter, and turns it into a flat list. Also, all prefixes and protocols are stripped from the element that triggered your handler.
This is, what the first parameter might look like:
(("/dir/org-protocol:/greedy:/one") ("/dir/two") ("/dir/three" (15 . 42)))
If only the first parameter is supplied,
return this list:
("/dir/one" "/dir/two" "/dir/three" 15 42)
If you supply a non-nil value as the second parameter for the function:
("one" "two" "three" 15 42)
And, last not least, if you supply a replacement "
REPL-" (must be a string):
("REPL-one" "REPL-two" "REPL-three" 15 42)
Note, that this works exactly this way regardless of your setting of
org-protocol-reverse-list-of-files". The sequence of the returned list will
always reflect the sequence of arguments on the command line.
emacsclient compresses double and triple slashes to one. That's why it doesn't really matter how many slashes succeed the scheme part of the URL, also known as protocol.
This behavior is the main reasons, why the slash was chosen as the default separator for data fields. Keeping the slashes is insecure, since some of the data fields could contain double or triple slashes themselves.
The function feature was added with the Org-mode 6.26 release (commit 6a9acfa9a3ec4ad889951d02c9809f55ac7491fb).