I do a lot of work on projects which are on Github and use the Github issues. Because I use Org mode a lot, I thought that it would be nice if I could create Github issues out of my Org mode todo entries. I wanted to learn Emacs Lisp since I started using Emacs, so this was a opportunity to do so.
At first, we need to get the current Org element. There is some documentation about the Org Element API here: http://orgmode.org/worg/dev/org-element-api.html
To get the current heading, we can use
To get the body of the element, there is
The similarly named
(org-entry-get) method returns something different: It gets
the property of an Org mode element. This is described in the Org mode manual
A.11 Using the property API.
We can use that to store the Github project for the current Org mode
file. Because properties and be inherited, we have to declare it once in the top
level heading properties.
:PROPERTIES: :GH-PROJECT: mbreit/foobar :END:
To get the property for the element at the point (that is how the current cursor position is being called in Emacs), we use:
(org-entry-get (point) "GH-PROJECT" t)
The last parameter for
org-entry-get tells the function, that we want to get
inherited properties from parent headline.
When creating issues from Org mode, I do not want the issue to be created automatically. If we can open URL in the browser with our Org mode entry details filled in, we could review and change the issue before creating it.
Luckily, Github has a URL scheme for prefilling the issue form field. If we open
a URL like
https://github.com/mbreit/foobar/issues/new?title=mytitle&body=mybody, we get
what we want. We have to URL encode our title and body of course, and that is
url-hexify-string Lisp function is for. Now we can define a function
that returns a URL like this:
(defun gh-issue-new-url (project title body) (concat "https://github.com/" project "/issues/new?title=" (url-hexify-string title) "&body=" (url-hexify-string body)))
To open a new browser tab with a given URL,
browse-url can be used.
(defun gh-issue-new-browse (project title body) (browse-url (gh-issue-new-url project title body)))
Now we define a function for our current Github project with the
function described above.
(defun gh-issue-get-project () (org-entry-get (point) "GH-PROJECT" t))
Let's put this all together in an interactive function that we can bind to a key:
(defun gh-issue-create () (interactive) (gh-issue-new-browse (gh-issue-get-project) (org-get-heading) (org-get-entry)))
This works great, but the body is filled with our Org mode entry, which might have some Org mode markup, which is not the same as Markdown, which Github uses for its issues.
The Org export framework can convert Org mode syntax to Latex and HTML, but also to Markdown. There is even an export for Github flavored markdown or GFM: http://orgmode.org/cgit.cgi/org-mode.git/plain/contrib/lisp/ox-gfm.el
The Org Export API has a function
org-export-as, which can return the converted
Org buffer in any format that the export framework supports. There is an
subtreep for exporting only the current subtree, which is
what we want.
ox-gfm is installed, we can use
(org-export-as 'gfm t) to get the current
entry as markdown. Let's use that instead of
(defun gh-issue-create () (interactive) (gh-issue-new-browse (gh-issue-get-project) (org-get-heading) (org-export-as 'gfm t)))
The complete code
ox-gfm installed, you can add the following code to your Emacs
configuration and bind a key to
(defun gh-issue-new-url (project title body) (concat "https://github.com/" project "/issues/new?title=" (url-hexify-string title) "&body=" (url-hexify-string body))) (defun gh-issue-new-browse (project title body) (browse-url (gh-issue-new-url project title body))) (defun gh-issue-get-project () (org-entry-get (point) "GH-PROJECT" t)) (defun gh-issue-create () (interactive) (gh-issue-new-browse (gh-issue-get-project) (org-get-heading) (org-export-as 'gfm t)))
I love Emacs and Org mode for making such things possible with so little code.
In this blog post, the interaction with Github is done with the browser, not the
Github API. The advantage of this is that our Emacs Lisp code does not have to handle
authentication. But using the API would be much more powerful.
So I am working on an Emacs package that uses the
gh.el implementation to talk with the Github API
for importing and syncing your Org mode elements directly from within Emacs.
So if you are interested, stay tuned on this blog or follow me on Twitter (@m_breit) to get notified when I release this package.