org-ref meets hydra

| categories: emacs | tags:

I am enjoying learning about abo-abo/hydra , which is a nice package for making minibuffer menus to run commands. It is light-weight solution that does not mess up your window too much, and it is easier to use than any home-grown solution I have made in the past. Here is a simple little code that gives me three options when I press "zz" quickly (a key-chord). I can press "c" to put in a cite link using helm, "r" to insert a ref link using helm, and "l" to insert a new label. Any other key just cancels the menu. One thing to remember ("zz"), and hints for the rest!

(require 'hydra)
(setq hydra-is-helpful t)

(require 'key-chord)
(key-chord-mode 1)
(key-chord-define-global
 "zz"
 (defhydra org-ref-hydra ()
   "org-ref"
   ("c" org-ref-helm-insert-cite-link "cite")
   ("r" org-ref-helm-insert-ref-link "ref")
   ("l" org-ref-helm-insert-label-link "label")
   ("R" org-ref "org-ref")))
org-ref-hydra/body

Pretty nice. Check out the nice hydra interface to words.el . A simple press of "ww" gets you easy access to single key presses of all the nice words functions. What would you hydra for?

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Extending the org-mode link syntax with attributes

| categories: orgmode, emacs | tags:

I make super heavy use of links in org-mode. I use them extensively in org-ref to create functional citations. One detail that has never been very satisfactory is the need for pre/post text in citations. I don't need that capability often, but it seems important to some. I have implemented a kind of clunky solution where I use the description part of a link with the pre/post text separated by a ::. Although that works, I dislike the way it looks, the need to parse it, and that the description covers the link.

[[cite:key][pre text::post text]]

Some time ago there was a suggestion of how to extend the link syntax, which was to my knowledge never implemented. Here is the proposed syntax:

$[link http://google.com
         :last-followed [2009-02-25 Wed 02:00]
         :label "click here for evil search engine"
         :export-label "click here for nice search engine"]

This is interesting because this syntax suggests the link has attributes which can be updated.

We will show here how to implement part of this idea with the existing link syntax. We will make a link that has attributes like that. The basic idea is to simply incorporate the attributes into the path, and use lisp to read them. We will wrap the link path in parentheses and read that as a lisp data structure. So, a link like link:key :pre "some pre text" :post "some post text" will be parsed as:

(read "(key :pre \"some pre text\" :post \"some post text\")")
key :pre some pre text :post some post text

The car of that list is the key, and the cdr contains the attributes. The quotes are necessary here to make sure all the text is correctly parsed as a single element for each attribute. So, here is an example link

(org-add-link-type
 "slink"
 ;;  follow function
 (lambda (path)
   (let* ((data (read (format "(%s)" path)))
          (head (car data))
          (plist (cdr data))
          (link (org-element-context))
          (begin (org-element-property :begin link))
          (end (org-element-property :end link)))
     (setq plist (plist-put plist :last-clicked (current-time-string)))
     (save-excursion
     (setf (buffer-substring begin end) "")
     (goto-char begin)
     (insert (format "[[slink:%s %s]]" head
         (substring (format "%S" plist) 1 -1))))))
 ;; format function
 (lambda (path description backend)
   (let* ((data (read (concat "(" path ")")))
          (head (car data))
          (plist (cdr data)))
     (format "\\%s[%s][%s]{%s}"
             (plist-get plist :type)
             (plist-get plist :pre)
             (plist-get plist :post)
             head))))

Now, each time I click on this link, the time stamp gets updated.

\cite[See for example][page 47]{kitchin-2010}

[[slink:kitchin-2010 :pre "See for example" :post "page 47" :type "cite" :last-clicked "Thu Feb  5 09:31:15 2015"]]

And, the generic export of this link is:

\cite[See for example][page 47]{kitchin-2010}

Is this a good idea? I am not using this for anything right now. Sometimes my version of org-mode has trouble recognizing that is a link. It is strange, as I am typing, sometimes it flashes in and out of being recognized as a link. Anyway, it is an interesting idea!

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Helm at the Emacs

| categories: helm, emacs | tags:

I have written several (intro , multiple args , prefix args) times about using Helm in Emacs so far. Today, I want to share a way I use helm to get me where I want to be in Emacs for my daily activities. This came out of a desire to have single command that would give me a lot of options to open exactly the buffer/file I wanted when I need it. I call the command hotspots, and it is bound to f9 for me, so when I press f9 I get a helm buffer to select what I want from.

So, what kinds of things do I want. First, I want to be able to open my mail, calendar, News feed or agenda from this command. Second, I have a list of hotspots I developed using the code at http://ergoemacs.org/emacs/emacs_hotkey_open_file_fast.html , which I want easy access to. Third, I want to be able to open any org-file in my agenda list. Fourth, any bookmark I have, or to set a bookmark. Fifth, I want recent files as candidates. There is certainly some redundancy in their, but that is ok, it gets me where I want to be.

Here is the code that does that for me. There are six helm sources that provide candidates and actions.

(defun hotspots ()
  "helm interface to my hotspots, which includes my locations,
org-files and bookmarks"
  (interactive)
  (helm :sources `(((name . "Mail and News")
                    (candidates . (("Mail" . (lambda ()
                                               (if (get-buffer "*mu4e-headers*")
                                                   (progn
                                                     (switch-to-buffer "*mu4e-headers*")
                                                     (delete-other-windows))

                                                 (mu4e))))
                                   ("Calendar" . (lambda ()  (browse-url "https://www.google.com/calendar/render")))
                                   ("RSS" . elfeed)
                                   ("Agenda" . (lambda () (org-agenda "" "w")))))
                    (action . (("Open" . (lambda (x) (funcall x))))))
                   ((name . "My Locations")
                    (candidates . (("master" . "~/Dropbox/org-mode/master.org")
                                   (".emacs.d" . "~/Dropbox/kitchingroup/jmax" )
                                   ("blog" . "~/blogofile-jkitchin.github.com/_blog/blog.org")
                                   ("ese" . "~/Dropbox/books/ese-book/ese.org" )
                                   ("passwords" . "~/Dropbox/org-mode/passwords.org.gpg")
                                   ("Pycse" . "~/Dropbox/books/pycse/pycse.org")
                                   ("references" . "~/Dropbox/bibliography/references.bib")
                                   ("notes" . "~/Dropbox/bibliography/notes.org")
                                   ("journal" . "~/Dropbox/org-mode/journal.org")
                                   ("tasks" . "~/Dropbox/org-mode/tasks.org")))
                    (action . (("Open" . (lambda (x) (find-file x))))))

                   ((name . "My org files")
                    (candidates . ,(f-entries "~/Dropbox/org-mode"))
                    (action . (("Open" . (lambda (x) (find-file x))))))
                   helm-source-recentf
                   helm-source-bookmarks
                   helm-source-bookmark-set)))

Interesting to me is that there are not a lot of actions in here. I mostly use this command for navigation to various places. For example, I press f9, type meet, and I can quickly get to the meetings file in my agenda list, or I can type the first few letters of a student's name and open the org-file associated with them. Or I press f9 and go down an entry to open my calendar, etc… I find this enormously helpful because it opens these files no matter where I am in Emacs, and it relieves my mind from remembering where they are, or the keystrokes/commands to get to them.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

helm and prefix functions

| categories: helm, emacs | tags:

Helm modifies how you use prefix arguments in Emacs. A prefix argument is when you type C-u before a command to modify its behavior. There are a few variations of prefix arguments. Basically, pressing C-u once sets a prefix variable to '(4), pressing twice sets it to '(16). Alternatively, C-u 7 sets the prefix to 7. In regular emacs commands, you type the prefix keys before the command. In helm, you type the after you enter the helm selection buffer, and before you press enter or select your action. In helm, you access the prefix arg in the variable helm-current-prefix-arg. Let us look at how you might use it.

We make an action function that does something conditionally depending on the prefix arg. Yes, you could write several functions to accomplish that too, but maybe there is just a little difference that you can use the prefix arg to handle. What you cannot remember 4 prefix options? You do write good doc strings on your functions right ;) If not, you probably ought to write four functions with meaningful names, and meaningful helm descriptions!

(defun action (candidate)
  "Our action function.
with no prefix message no prefix arg
with one prefix arg message C-u
with two prefix args message C-u C-u
with a numeric prefix arg, message the number."
  (interactive "p")
  (cond
   ((eq nil helm-current-prefix-arg)
    (message-box "no prefix arg"))
   ((equal helm-current-prefix-arg '(4))
    (message-box "C-u"))
   ((equal helm-current-prefix-arg '(16))
    (message-box "C-u C-u"))
   (t
    (message-box (format "C-u %s" helm-current-prefix-arg)))))

(setq some-helm-source
      '((name . "HELM at the Emacs")
        (candidates . (1 2 3 4))
        (action . action)))

(helm :sources '(some-helm-source))
C-u (64)

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Handling multiple selections in helm

| categories: helm, emacs | tags:

The basic usage pattern of helm is you run a command which opens a buffer of choices. You enter some text in the minibuffer which eliminates choices that do not match what you type in. You can select multiple choices by using C-spc, or M-a to mark them all. When you press enter, the current selection is sent to the default action defined. The action is a function that does something, usually on the selected item(s). Here, we explore writing the action function to do what we want. The reason this is somewhat tricky is that when you mark an item in helm, the "cursor" moves to the next item, which means when you press enter it may be possible that the current highlighted item is not part of the items you have marked. If your action will perform a delete action, for example, you may have wanted to delete the marked items, and not the current selection! So, what we need is a way to get what we want.

An action function in helm should normally take one argument, which is going to be the currently selected item from helm. However, we can use two different functions to access either the selected item (helm-get-selection) or the marked items (helm-marked-candidates). So, we can write our function to do "do what we mean". Note, even if you do not mark any candidates, (helm-marked-candidates) will return a list that has the current selection in it. So we can write our action function to act on this list so it works on what is marked or what is selected if nothing is marked. That is probably "what we mean".

Here is one way to work on a selection or marked list of selections. We define an action function that takes an arg, but inside we operate on each element of the marked candidates.

(defun some-action (candidate)
  (loop for cand in (helm-marked-candidates)
        do
        (message-box "working on %s" cand)))

(helm :sources '(((name . "HELM")
                  (candidates . (1 2 3 4))
                  (action . (("open" . some-action))))))

Here is an alternative approach. Here we define the action function to work on one candidate. That might be helpful for testing, for example. Then, we use mapc to apply the function to each marked candidate.

(defun some-action (candidate)
  (message-box "single working on %s" candidate))

(helm :sources '(((name . "HELM")
                  (candidates . (1 2 3 4))
                  (action . (("open" . (lambda (candidate)
                                         (mapc
                                          'some-action
                                          (helm-marked-candidates)))))))))

A little more verbose method might be like this. Here we just pull out the lambda function to another function, to make the helm source definition a little shorter. I cannot tell if this is easier to follow, it is just another option.

(defun some-action (candidate)
  (message-box "single2 working on %s" candidate))

(defun some-actions (candidate)
  (mapc 'some-action (helm-marked-candidates)))

(helm :sources '(((name . "HELM")
                  (candidates . (1 2 3 4))
                  (action . some-actions))))

So there you have it. You can select multiple things in helm, and then operate on them with your action function!

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter
« Previous Page -- Next Page »