org-mode links meet hydra

| categories: hydra, orgmode, emacs | tags:

I have played with a lot of options to give org-mode links extra functionality. Here are a few of the ideas I have looked at so far.

  1. Enabling right clicks on links
  2. A home made minibuffer menu in org-ref
  3. A helm buffer in org-ref

Here, I want to explore a hydra menu for a link. The idea is pretty simple, we need functions that do something with the link at point, and a hydra interface to call them. This turned out to be a little tricky. I could not get the path from the link in the link lambda function, and we need a way to pass the path to the function. I use a global variable for that. I wish there was another way to do that, but this does actually work. We illustrate it here with a more functional doi link.

(defun doi-crossref ()
  "Search DOI in CrossRef."
  (interactive)
  (browse-url
   (format
    "http://search.crossref.org/?q=%s" *doi-hydra-path*)))

(defun doi-google-scholar ()
  "Google scholar the doi."
  (interactive)
  (browse-url
   (format
    "http://scholar.google.com/scholar?q=%s" *doi-hydra-path*)))

(defun doi-pubmed ()
  "Pubmed the doi."
  (interactive)
  (browse-url
   (format
    "http://www.ncbi.nlm.nih.gov/pubmed/?term=%s"
    (url-hexify-string *doi-hydra-path*))))

 (defhydra doi-hydra ()
   "org-ref"
   ("c" doi-crossref "Crossref")
   ("g" doi-google-scholar "Google Scholar")
   ("p" doi-pubmed "Pubmed"))

(org-add-link-type "doi"
  (lambda (path) (setq *doi-hydra-path* path) (doi-hydra/body)))
lambda (path) (setq doi-hydra-path path) (doi-hydra/body)

Now for a test, 10.1021/jp047349j.

It works fine, when you click on a link, you get a minibuffer menu with context hints, and pressing any other key than is defined simply cancels the command.

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

Export org-mode to docx with citations via pandoc

| categories: orgmode, docx | tags:

Pandoc continues to develop, and since the last time I wrote about it there is improved support for citations. We will use that to convert org documents to Word documents that actually have citations and a bibliography in them. This post explores using helm-bibtex to insert pandoc compatible citations, and then using pandoc to convert the org file to a word document (docx). We can define the format of citations that helm-bibtex inserts in a function, and tell helm-bibtex to use it when in org mode.

Here is that code. This is just to give me a convenient tool to insert citations with searching in my bibtex file. I think you could just as easily use reftex for this, or an ido-completing function on bibtex keys. See Pandoc - Pandoc User’s Guide for directions on citation format. The key is to format the cite links to the pandoc format.

(defun helm-bibtex-format-pandoc-citation (keys)
  (concat "[" (mapconcat (lambda (key) (concat "@" key)) keys "; ") "]"))

;; inform helm-bibtex how to format the citation in org-mode
(setf (cdr (assoc 'org-mode helm-bibtex-format-citation-functions))
  'helm-bibtex-format-pandoc-citation)
helm-bibtex-format-pandoc-citation

Now, we can cite the org-mode book [@dominik-2010-org-mode], and some interesting papers on using org-mode [@schulte-2011-activ-docum; @schulte-2012-multi-languag]. You could pretty easily add pre and post text manually to these, after selecting and inserting them.

We need a bibliography file for pandoc to work. I will use a bibtex file, since I already have it and am using helm-bibtex to select keys. I found pandoc could not read my massive bibtex file, perhaps it does not support all the types yet, so I made a special small bibtex file for this. So, now all we need to do is convert this file to a docx. I use a function like this to do that. It uses an org-ref function to get the bibliography defined in this file, derives some file names, and then runs pandoc.

(defun ox-export-to-docx-and-open ()
 "Export the current org file as a docx via markdown."
 (interactive)
 (let* ((bibfile (expand-file-name (car (org-ref-find-bibliography))))
        ;; this is probably a full path
        (current-file (buffer-file-name))
        (basename (file-name-sans-extension current-file))
        (docx-file (concat basename ".docx")))
   (save-buffer)
   (when (file-exists-p docx-file) (delete-file docx-file))
   (shell-command (format
                   "pandoc -s -S --bibliography=%s %s -o %s"
                   bibfile current-file docx-file))
   (org-open-file docx-file '(16))))

And now we run it to get our docx.

(ox-export-to-docx-and-open)

Here is the result: org-to-docx-pandoc.docx

It is not too bad. Not all the equations showed up below, and the figure did not appear for some reason. But, the citations went through fine. A downside of this is the citation links are not clickable (but see Making pandoc links for a way to do this), so they lack all the awesome features that org-ref gives them. Maybe pandoc can convert these to LaTeX links, but we already have such a good framework for that I do not see why you would want to do it. A better option is to figure out how to export the org file to an org file, and transform the org citation links to pandoc citations, then use pandoc on the temporarily transformed buffer. That way, you keep the cite links and their functionality, and ability to export to many formats, and get export to docx via pandoc.

There are other options in pandoc to fine tune the reference format (you need a csl file). That can be included in the org-file via file tags pretty easily. These citations are not links in the word document, and it does not look like they can be converted to footnotes, endnotes or interact with Endnote or Zotero at this time, but it is a step forward in getting a passable word document with references out of org-mode!

Since we are testing, let us try it some other typical features in an org-file.

1 Numbered list

  1. Item 1
  2. Item 2
  3. Item 3

2 Bulleted list

  • item 1
  • item 2
  • item 3
    • subitem

3 definitions

org-mode
tool for awesomeness

4 Math

One equation: \(e^{i\pi} - 1 = 0\)

A second equation:

\begin{equation}
e^{i\pi} - 1 = 0
\end{equation}

5 An image

Figure 1: A little icon.

6 A table

Table 1: A little table.
x y
1 2
3 4

a plain table

x y
1 2
3 4

7 Making pandoc links

Here I show a way to get clickable text on pandoc links. I found a nice library called button-lock that uses a regular expression to attach text properties to matching text.

Below I repeat the citations so it is easy to see the effect after running the code block. Indeed, you get clickable text, even org-ref like capability. I think you could even add the idle-timer messages, and the org-ref menu.

Now, we can cite the org-mode book [@dominik-2010-org-mode], and some interesting papers on using org-mode [@schulte-2011-activ-docum; @schulte-2012-multi-languag]. You could pretty easily add pre and post text manually to these, after selecting and inserting them.

You would need to make this code run in when you open an org-file to get it to work every time.

(require 'button-lock)
(global-button-lock-mode)

(button-lock-set-button
 "@\\([-a-zA-Z0-9_:]*\\)"
 (lambda ()
   (interactive)
   (re-search-backward "@")
   (re-search-forward  "@\\([-a-zA-Z0-9_:]*\\)")
   (let* ((key (match-string-no-properties 1))
          (bibfile (cdr (org-ref-get-bibtex-key-and-file key))))
     (if bibfile
        (save-excursion
          (with-temp-buffer
            (insert-file-contents bibfile)
            (bibtex-search-entry key)
            (message (org-ref-bib-citation))))
       (message "No entry found"))))
 :face (list 'org-link))

8 References

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

Redirecting stderr in org-mode shell blocks

| categories: orgmode | tags:

Org-mode shell source blocks do not capture stderr. For example, in this block, with the default setup:

echo "testing stdout" >&1
echo "testing stderr" >&2
testing stdout

You can see the second line is not in the output.

If you run this command, you get an Org-Babel Error Output buffer saying it is an illegal option.

date -g

It would be nice to just capture that error, and show it.

We solved this problem in Python by redirecting stderr at runtime. It is not that simple in shell blocks, but we can do a similar thing. The code block is executed (I think) by saving the block to a temporary file, and then running org-babel-sh-command on the file. That magic happens inside the command shell-command-on-region. We just need to make that command redirect stderr. Here is a new shell command that does this. This next block can be tangled out to an executable command. This script takes one argument, which I believe is a filename (the temporary file containing the source block region).

#!/bin/bash
{
bash $1
} 2>&1

Now, we set org-babel-sh-command to our new shell command.

(setq org-babel-sh-command "./sh_stderr.sh")
./sh_stderr.sh

Now, it appears we get what we want:

echo "testing stdout" >&1
echo "testing stderr" >&2
testing stdout
testing stderr

And, with the bad option to date, we get:

date -g
echo
date: illegal option -- g
usage: date [-jnu] [-d dst] [-r seconds] [-t west] [-v[+|-]val[ymwdHMS]] ...
            [-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]

Not bad! I have not tested this very thoroughly, i.e. beyond these little examples, but it seems like it could work.

Achim Gratz suggested this simpler approach that does not involve any external scripts. The : at the end is important!

exec 2>&1
echo "testing stdout" >&1
echo "testing stderr" >&2
date -g
:
testing stdout
testing stderr
date: illegal option -- g
usage: date [-jnu] [-d dst] [-r seconds] [-t west] [-v[+|-]val[ymwdHMS]] ...
            [-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]

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

Capturing stderr from Python in org-mode - take 2

| categories: python, orgmode, emacs | tags:

In a previous post I wrote about a sandbox module to help capture stderr in Python code blocks in org-mode. That module worked, but ran as a script.

stderr is not captured in the output of a code block in org-mode. For example:

import sys
print >>sys.stdout, "message on stdout"
print >>sys.stderr, "testing stderr"
message on stdout

The messages to stderr just disappears. Not good for code like this:

from scipy.integrate import odeint

def ode(y, x):
    return -k * x

xspan = [0, 1]
y0 = 1

sol = odeint(ode, y0, xspan)
print sol
[[ 1.]
 [ 1.]]

There is an error in that code, k is not defined. If you run that as a script, you get this output:

>>> Traceback (most recent call last):
  File "/var/folders/5q/lllv2yf95hg_n6h6kjttbmdw0000gn/T//python-69413hLF.py", line 4, in ode
    return -k * x
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "/var/folders/5q/lllv2yf95hg_n6h6kjttbmdw0000gn/T//python-69413hLF.py", line 4, in ode
    return -k * x
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "/var/folders/5q/lllv2yf95hg_n6h6kjttbmdw0000gn/T//python-69413hLF.py", line 4, in ode
    return -k * x
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "/var/folders/5q/lllv2yf95hg_n6h6kjttbmdw0000gn/T//python-69413hLF.py", line 4, in ode
    return -k * x
NameError: global name 'k' is not defined

But, that is evidently going to stderr, and not getting captured in org-mode. Boo. A silent error that returns a value! This behavior of odeint may be fixed in scipy 0.15, but it is a general deficiency of org-mode babel code blocks. So, today I am looking back into a way to fix it. We try something as mundane as just redefining stderr in Python at runtime.

import sys
sys.stderr = sys.stdout

print >>sys.stdout, "message on stdout"
print >>sys.stderr, "testing stderr"
message on stdout
testing stderr

That works fine. Let us test it with the other block.

import sys
sys.stderr = sys.stdout

from scipy.integrate import odeint

def ode(y, x):
    return -k * x

xspan = [0, 1]
y0 = 1

sol = odeint(ode, y0, xspan)
print sol
Traceback (most recent call last):
  File "<stdin>", line 6, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 6, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 6, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 6, in ode
NameError: global name 'k' is not defined
[[ 1.]
 [ 1.]]

Sweet, we get the errors. We still get the returned value, but it is immediately obvious something is wrong. I have wrapped that little tidbit into a Python module in pycse.orgmode , which you can import to get the same effect.

import pycse.orgmode

from scipy.integrate import odeint

def ode(y, x):
    return -k * x

xspan = [0, 1]
y0 = 1

sol = odeint(ode, y0, xspan)
print sol
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined

[[ 1.]
 [ 1.]]

Finally, you can avoid the import by setting your org-babel Python command like this:

(setq org-babel-python-command "python -i -c \"import pycse.orgmode\"")
python -i -c "import pycse.orgmode"

Now, we run our faulty block again:

from scipy.integrate import odeint

def ode(y, x):
    return -k * x

xspan = [0, 1]
y0 = 1

sol = odeint(ode, y0, xspan)
print sol
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined
Traceback (most recent call last):
  File "<stdin>", line 2, in ode
NameError: global name 'k' is not defined

[[ 1.]
 [ 1.]]

Excellent. The stderr is captured.

And we get basically the same output as before for regular code blocks. There is an extra line before and after the output for some reason. I can live with that!

print 6 + 7
13

Copyright (C) 2014 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 »