Teaching Emacs to recognize Jira tickets and show them in a browser using Hyperbole implicit buttons

November 9, 2023

It’s difficult to describe the Emacs package Hyperbole succinctly, so I’m not even going to try 😅. I was looking for a quick way of opening Jira tickets from an Org mode file, and Hyperbole was the way. There are YouTube videos and articles on Hyperbole, but I found very little describing in one place what I wanted to do. Hopefully others might find this useful.

As well as developing my own apps, such as beorg, I work as a freelancer for a number of clients; three of my clients use Jira. The way I work is that each month I create a new file for that client and list tickets I’m to work on, record time against said tickets for the purposes of billing and make notes. This reduces the amount of time I need to spend in Jira itself.

A Jira ticket has the format PROJECT-ID. For example, CORE-1234 is a ticket in the project CORE.

I wanted to be able to put the cursor on a ticket reference, and open the ticket in my browser. I could have added a URL for the ticket, but then I wouldn’t have had all this Hyperbole fun 🎢.

Hyperbole allows you to define implicit buttons. These turn any text into a button. Emacs does some pattern matching to extract the needed information and then does something. In my case I wanted Emacs to identify that the cursor is on a Jira ticket ID and then construct a URL for that ticket and open in my web browser. To support Jira tickets I need to define a new type of implicit button. When I started doing this I got confused and added the Emacs Lisp to my personal button file - this is not the place however if you want to define a new implicit button type!

The Emacs Lisp to define my new implicit button type is as follows:

(defvar my/jira-cs-browse-url "https://example.atlassian.net/browse/")

(defun my/jira-cs-reference (jira-id)
  "Open ticket in CS Jira"
  (let ((url (concat my/jira-cs-browse-url jira-id)))
    (browse-url-default-browser url)))

(defib my/jira-cs ()
  "Get the Jira ticket identifier at point and load ticket in browser"
  (let ((case-fold-search t)
        (jira-id nil)
        (jira-regex "\\(CORE-[0-9]+\\)"))
    (if (or (looking-at jira-regex)
            (save-excursion
              (skip-chars-backward "0-9")
              (skip-chars-backward "-")
              (skip-chars-backward "CORE")
              (looking-at jira-regex)))
        (progn (setq jira-id (match-string-no-properties 1))
               (ibut:label-set jira-id
                               (match-beginning 1)
                               (match-end 1))
               (hact 'my/jira-cs-reference jira-id)))))

I’m not going to go over exactly what the above code does, there are lots of introductions to Emacs Lisp available, however here are the important points to note:

To test the button make sure you are in a buffer with the mode emacs-lisp-mode enabled, and then do M-x eval-buffer. This will run the code and create the implicit button. Pressing M-RET on text such as CORE-1234, in any buffer (not just an Org mode file), will open the Jira ticket in a web browser. Being able to do this from any buffer is a glimpse as to why Hyperbole can be so powerful.

You’ll now need to permanently install your new button type. I’m using the Doom Emacs framework, so I’m going to add my implicit button type to ~/.doom.d/config.el. If you are using vanilla Emacs or another framework you’ll know where to add this for your setup. Here is how the relevant section of my config.el file looks:

(use-package! hyperbole
  :init
  (hyperbole-mode)
  :config
  (defvar my/jira-cs-browse-url "https://example.atlassian.net/browse/")
  (defun my/jira-cs-reference (jira-id)
    "Open ticket in CS Jira"
    (let ((url (concat my/jira-cs-browse-url jira-id)))
      (browse-url-default-browser url)))
  (defib my/jira-cs ()
    "Get the Jira ticket identifier at point and load ticket in browser"
    (let ((case-fold-search t)
          (jira-id nil)
          (jira-regex "\\(CORE-[0-9]+\\)"))
      (if (or (looking-at jira-regex)
              (save-excursion
                (skip-chars-backward "0-9")
                (skip-chars-backward "-")
                (skip-chars-backward "CORE")
                (looking-at jira-regex)))
          (progn (setq jira-id (match-string-no-properties 1))
                 (ibut:label-set jira-id
                                 (match-beginning 1)
                                 (match-end 1))
                 (hact 'my/jira-cs-reference jira-id)))))
  )

I enjoy writing Emacs Lisp, but do it very rarely - so the above took me some time to get right. The main sticking points were ensuring that hact was used to run the button action - and my mistake in adding the Emacs Lisp to the personal button file. Once I’d worked this out the rest was easy.

The Hyperbole documentation recommends looking at the file hibtypes.el. I managed to load this by bringing up the Emacs help for Hyperbole (SPC h f hyperbole), activating the link hui-mini.el and then using find-file (SPC . in Doom) to get to hibtypes.el (I’m sure there is an easier way!) There is lots to look at here, but for me I would have found a shorter example, such as I’ve detailed in this article, a great help in defining my first button!

Installing Hyperbole

If, like me, you are using the Doom framework then just add the following to your ~/.doom.d/packages.el file:

(package! hyperbole)

and this to your ~/.doom.d/config.el file:

(use-package! hyperbole
  :init
  (hyperbole-mode))

If you are using vanilla Emacs then check out the installation guide in the Hyperbole documentation itself.

Download now for iPhone and iPad

Follow us on Mastodon

Privacy Policy | Press Kit