Calling org-remember from inside conkeror

Posted: November 14, 2008 in Applications, Emacs
Tags: , , , , , ,

I use Carsten Dominik’s great emacs package org-mode for project planning, appointments and TODOs (org-mode homepage). One cool feature is that it integrates nicely with the remember package. So when an idea comes into my mind I need to remember for later, I hit M-x org-remember RET which presents me a buffer with a custom template, I type my note and hit C-c C-c. The note is filed then and the buffer is gone, so there’s really no interruption.

One cool thing is that org-remember automatically inserts a link to the “thing” you had open when adding the note. This “thing” could be a info buffer, a mail, a usenet article, an image,…

I thought it would be nice to use that facility to make TODO-bookmarks like “TODO read that great blog entry!” from inside my browser conkeror. And so I did.

Here’s the emacs side of the code (put it in your ~/.emacs):

(setq org-remember-templates 
      '(
        ;; lot of other templates...
        ("TODO"    ?t "* TODO %?
  :PROPERTIES:
  :created: %U
  :link: %a
  :END:
  %i")))

(defun th-org-remember-conkeror (url)
  (interactive "s")
  (org-remember nil ?t)
  (save-excursion
    (insert "\n\n  [[" url "]]"))
  (local-set-key (kbd "C-c C-c")
         (lambda ()
           (interactive)
           (org-ctrl-c-ctrl-c)
           (delete-frame nil t))))

And that’s the conkeror side (put it in your ~/.conkerorrc):

function org_remember(url, window) {
    var cmd_str = 'emacsclient -c --eval \'(th-org-remember-conkeror "' + url + '")\'';
    if (window != null) {
    window.minibuffer.message('Issuing ' + cmd_str);
    }
    shell_command_blind(cmd_str);
}

interactive("org-remember", "Remember the current url with org-remember",
        function (I) {
        org_remember(I.buffer.display_URI_string, I.window);
        });

This code may require emacs 23, I’m not too sure. What you need in every case is a running emacs server, so that you can connect to it with emacsclient.

UPDATE: Now I use org-protocol instead of something home-brewn.

Here’s the updated code for your ~/.conkerorrc. On the emacs side nothing is needed anymore.

function org_remember(url, title, text, window) {
    var eurl = encodeURIComponent(url);
    var etitle = encodeURIComponent(title);
    var etext = encodeURIComponent(text);
    var cmd_str = "emacsclient -c org-protocol://remember://" + eurl + "/" + etitle + "/" + etext; 
    window.minibuffer.message("Issuing " + cmd_str);
    shell_command_blind(cmd_str);
}

interactive("org-remember", "Remember the current url with org-remember",
        function (I) {
          org_remember(I.buffer.display_uri_string,
                       I.buffer.document.title,
                       I.buffer.top_frame.getSelection(),
                       I.window);
        });
Comments
  1. Memnon says:

    Hi!

    Great Setup, thank you!
    One question, though:

    If I get it right, pressing C-c C-c should file away the note and delete the frame created to write the note. But the (delete-frame nil t) is not executed here after pressing C-c C-c. Any idea, why?

    org 6.27a
    emacs-snapshot 23.0.91.1

  2. Tassilo says:

    No, sorry, but I don’t use that snippet anymore. Now I use org-protocol which contains a handler for remember. With that you only need a template on the emacs side like this:

    (setq org-remember-templates
    ‘((“TODO” ?t “* TODO %?\n (created: %U)\n %i\n %a”)
    (“BROWSER” ?w “* %:description :browser:\n (created: %U)\n\n %c\n\n %i”)))

    This is the new conkeror part:

    function org_remember(url, title, text, window) {
    var eurl = encodeURIComponent(url);
    var etitle = encodeURIComponent(title);
    var etext = encodeURIComponent(text);
    var cmd_str = “emacsclient -c org-protocol://remember://” + eurl + “/” + etitle + “/” + etext;
    window.minibuffer.message(“Issuing ” + cmd_str);
    shell_command_blind(cmd_str);
    }

    interactive(“org-remember”, “Remember the current url with org-remember”,
    function (I) {
    org_remember(I.buffer.display_URI_string,
    I.buffer.document.title,
    I.buffer.top_frame.getSelection(),
    I.window);
    });

    Bye!

  3. Memnon says:

    Ah, nice ;)
    Same problem, I have to press C-x 5 0 after I filed the note away with C-c C-c. Mhh… Nice little problem for my first elisp steps. I will solve it, eventually.
    Thank god so many people provide their snippets to learn from :).
    Thanks again!

  4. Kevin says:

    Just tried your org-protocol code, it works great in Aquamacs, although I had to remove the -c argument. What’s -c for? It doesn’t exist in the Aquamacs emacsclient.

    Is the TODO template really necessary? It seems to work fine without it…

    Also, maybe you should update the actual blog post with the new code? So newbies don’t have to know how to replace the fancy ” symbols with ‘real’ string delimiters… ;-)

  5. Tassilo Horn says:

    The -c tells emacsclient to create a new frame. That’s new in emacs 23. Maybe aquamacs is still based on 22.

    The TODO template is not necessary. I just copy&pasted my config.

    And jep, I’ll update the post. :-)

  6. justloop says:

    FYI, as of 22 Sep, the name of the display_URI_string was changed to display_uri_string, breaking the above script.

  7. David Wallin says:

    Some websites can give you problem with the code above. The solution is to shell_quote the URL (in org_remember):

    var cmd_str = “org-protocol://remember://” + eurl + “/” + etitle + “/” + etext;
    window.minibuffer.message(“Issuing ” + cmd_str);
    shell_command_blind(“emacsclient \””+shell_quote(cmd_str)+”\””);

    • Kevin says:

      error in process filter: ad-Orig-error: Login failed
      error in process filter: Login failed.

      Anyone know why I keep getting this when trying to issue org-remember from conkeror?

  8. redblue says:

    For me the %i argument does not work and I had to use %:region instead. Also the raised frame does not take the focus and is not deleted after C-c C-c.
    So here is my solution to the all of these:

    –on emacs side:

    (setq org-protocol-protocol-alist
    ‘((“remember2”
    :protocol “remember2”
    :function org-protocol-remember2)))

    (defun org-protocol-remember2 (info)
    (if (and (boundp ‘org-stored-links)
    (fboundp ‘org-remember))
    (let* ((parts (org-protocol-split-data info t))
    (template (or (and (= 1 (length (car parts))) (pop parts))
    org-protocol-default-template-key))
    (url (org-protocol-sanitize-uri (car parts)))
    (type (if (string-match “^\\([a-z]+\\):” url)
    (match-string 1 url)))
    (title (or (cadr parts) “”))
    (region (or (caddr parts) “sfsfsf”))
    (orglink (org-make-link-string
    url (if (string-match “[^[:space:]]” title) title url)))
    remember-annotation-functions)
    (setq org-stored-links
    (cons (list url title) org-stored-links))
    (kill-new orglink)
    (org-store-link-props :type type
    :link url
    :description title
    :region region
    )
    ;(raise-frame)
    (select-frame-set-input-focus (selected-frame))
    (org-remember nil (string-to-char template))
    (lower-frame)
    ;(delete-frame) ; uncoment this if you use -c in the call to n
    (setq org-remember-templates
    ‘((“Todo” ?t “* TODO %?\n %i\n %a” “TODO.org” “Tasks”)
    (“Journal” ?j “* %U %?\n\n %i\n %a” “JOURNAL.org”)
    (“Idea” ?i “* %^{Title}\n %i\n %a” “JOURNAL.org” “New Ideas”)
    (“Default” ?w “* %c%^G\n %t\n %:region %!” nil “Notes”)))

    — on conkeror side:
    function org_remember (url, title, selection, window) {
    var cmd_str = ’emacsclient \”org-protocol://remember2://’+url+’/’+title+’/’+selection+’\”‘;
    //var cmd_str = ’emacsclient -e \”(my-org-protocol-remember ‘+url+’/’+title+’/’+selection+’\”‘;
    if (window != null) {
    window.minibuffer.message(‘Issuing ‘ + title);
    }
    shell_command_blind(cmd_str);
    }

    • redblue says:

      sorry, didn’t yank org-protocol-remember2 properly, should be:

      (defun org-protocol-remember2 (info)
      (if (and (boundp ‘org-stored-links)
      (fboundp ‘org-remember))
      (let* ((parts (org-protocol-split-data info t))
      (template (or (and (= 1 (length (car parts))) (pop parts))
      org-protocol-default-template-key))
      (url (org-protocol-sanitize-uri (car parts)))
      (type (if (string-match “^\\([a-z]+\\):” url)
      (match-string 1 url)))
      (title (or (cadr parts) “”))
      (region (or (caddr parts) “sfsfsf”))
      (orglink (org-make-link-string
      url (if (string-match “[^[:space:]]” title) title url)))
      remember-annotation-functions)
      (setq org-stored-links
      (cons (list url title) org-stored-links))
      (kill-new orglink)
      (org-store-link-props :type type
      :link url
      :description title
      :region region
      )
      ;(raise-frame)
      (select-frame-set-input-focus (selected-frame))
      (org-remember nil (string-to-char template))
      (lower-frame)
      ;(delete-frame) ; uncoment this if you use -c in the call to emacsclient
      )
      (message “Org-mode not loaded.”))
      nil)

  9. Vera says:

    Hello! I could have sworn I’ve been to this website before but after browsing through a few of the articles I realized it’s new
    to me. Regardless, I’m certainly delighted I stumbled upon it and I’ll
    be bookmarking it and checking back often!

  10. carltonf says:

    Just a note, the argument to the emacsclient starting from org-protocol should be quoted with *double quote* as encodeURICompoent doesn’t encode single quote which is a special character in shell.

    Great article!

Leave a reply to Vera Cancel reply