‹ . local author .



Use citekeys to consistently and uniformly correlate in-text references with original source materials, not just in final submissions but also in notes and drafts.

——————————————————————————————

For long research projects, it is necessary not only to keep straight a variety of sources but also to ensure that in-text references to those sources are unambiguous. This allows readers to trace all lines of thought to their original contexts. This is obviously a requirement of all formal publications.

But a clear and consistent citation practice should not be neglected in the writing that leads up to a final submission, such as notes, outlines, drafts, etc. When you are still the project’s primary (or only) reader, you must still be able to trace lines of thought to their origins.

In many cases, this may not be difficult. In the middle of a project, when everything is fresh in your working memory, you might be able to make sense of all kinds of non-standard forms of citation, no matter how informal: “cite book on national identity formation,” “quote comes from that article we read during week 3,” “see Foucault 257.”

But the larger a project is and the more time that passes between note-taking and final submission the more effort will be involved in tracing such citations to their sources.

In the case of a career researcher, informal citations like the above will inevitably lose their meaning as their originary contexts recede into the past. Which book on national identity formation was that? Which class? Which book by Foucault, and which edition?

The use of citekeys can greatly reduce the effort involved in mainlining clear lines of connection between citations and sources—not just in final products but in notes and drafts as well, and not just in single projects but in a lifetime of research.

Citekey Basics

A citekey (or citation key) is a unique code assigned to a particular piece of source material that can be used to quickly and consistently refer to that source.

A citekey is typically composed of some combination of the source’s year of publication, its primary author’s surname, and/or a short version of its title.

Here are three different citekeys, in three common styles, that could be used for the same source, the book Imagined Communities by Benedict Anderson. Each offers a far more precise indication of what source is being referred to than the comparatively ambiguous “cite book on national identity formation”:

Anderson2006
andersonImaginedCommunities2006
anderson2006_ImaginedCommunities

It is important to note that citekeys are not expected to be clearly understandable to anyone other than the researcher who assigns them. Consider that “Anderson2006” in the example above could refer to any number of publications from the year 2006 by anyone with the surname Anderson.

In this way citekeys are unlike ISBN or DOI numbers: they are not universal indicators—they do not point directly and unambiguously to particular sources themselves.

Rather, citekeys point to a researcher’s own individual collection of bibliographic entries (more on this ahead). Citekeys offer, as a result, a simple, private system of shorthand for encoding reference information into any written document, all without the work entailed in writing out full formal citations and bibliographic entries in every instance.

Given their simplicity and ease of use, citekeys help to lower any barriers that might otherwise have impeded the inclusion of clear, reliable citations in even the most informal of written documents, including and especially those that are not intended for publication. They thereby help to eliminate any temptation to insert ambiguous and cryptic notes-to-self that risk baffling more than benefiting.

Your Bibliographic Database

For texts that are intended to be published, citekeys offer great flexibility, as they can be easily replaced with formal citations in whatever citation style is appropriate to the intended context, such as Chicago, MLA, Harvard, APA, etc.

This requires maintaining a centralized repository of bibliographic information wherein all citekeys are listed with relevant information about the associated source. This may sound more complicated than it is.

While it is possible to manage bibliographic information with one of the many popular reference management programs—such as Zotero, Endnote, or Mandeley—it is equally possible to maintain nothing more than a simple text document of entries like the following:

@book{Anderson2006,
        title = "Imagined Communities: Reflections on the Origin and Spread of Nationalism",
        author = Anderson, Benedict",
        year = "2006",
        publisher = "Verso",
        address = "London; New York",
}

The above entry happens to be written in a standardized format called BibTeX, often used or at least supported by reference management software. For the sake of future-proofing your bibliography, it might be worthwhile to use such a standardized format.1

But such entries can easily be written and maintained in any format and without such software, using nothing more than a plaintext editing program like TextEdit (MacOS) or Notepad (Windows). The most important thing is to include all the relevant information about the source and associate it with a unique citekey.

From such information, it will be possible to produce citations and references in any of a seemingly endless array of standardized citation styles.2

Key Rule: Be Consistent

No matter what style of citekey or method of bibliography management is used, internal consistency is essential. Use a consistent citekey style—I use AuthorYEAR, like “Anderson2006”—so that each source is associated with a only single citekey. Do not use both “Anderson2006” and “andersonImaginedCommunities2006.”

Also, take care to ensure that each citekey is associated with only a single source. If you add a source by a different Anderson also written in 2006, assign it a unique citekey, like “Anderson2006a” or “Anderson2006-1.”

Note that using citekeys also makes it easy to include and distinguish between different editions of a single text. You might also have reason to cite the first edition of Imagined Communities (1983) alongside the reprint. Luckily, there is no confusing “Anderson1983” and “Anderson2006.”

Citekeys In Use

Choosing a citekey style, assigning citekeys to sources, and keeping them all matched up in a bibliography database is only the beginning.

Once you’ve done all that, how to go about putting them to use in your writings?

Here is an example from my reading notes:

Heise cites [#Anderson2006], who make a similar connection between representations and identity-formation.[5][#Heise2008]

I was not taking these notes for any specific project or a potential publication where accurate citations would be necessary. Nevertheless, it was so simple to include the citekeys and a page reference that I didn’t consider not doing it.

It was the use of citekeys, formatted in a distinct syntax—in brackets, preceed by the hash sign3—that made it effortless to find this reference (along with several others) to Anderson’s Imagined Communities in a file on my computer that is not obviously about the text in question and that I haven’t looked at or thought about since I wrote it. How about that?

Bonus: PDF Management

Aside from helping you keep your citations legible and locate deeply embedded notes in the recesses of your file system, citekeys can also help you keep track of PDFs and other digital documents. No longer worry over what to name that PDF. Enter its information into your bibliography file, assign it a citekey, and use the citekey as the name of the file. You’ll never have to guess what “Anderson2006.pdf” contains.


  1. https://www.bibtex.com/g/bibtex-format/ ↩︎

  2. https://citationstyles.org ↩︎

  3. These citations are in a syntax called MultiMarkdown. A similar syntax, called Pandoc, uses the @ prefix instead of the #, among other differences: [@Anderson2006, p 22-23]. ↩︎

\Cor"a*coid\, a.[Gr. ?; ko`rax crow + e’i^dos form.]

  1. Shaped like a crow’s beak.

  2. (Anat.) Pertaining to a bone of the shoulder girdle in most birds, reptiles, and amphibians, which is reduced to a process of the scapula in most mammals.

Webster’s Revised Unabridged Dictionary (1913)

This post describes how the Emacs package link-hint can be combined with ace-window to allow you to select, on-the-fly, which window a link will open in—instead of letting fate, or custom, or Emacs decide for you.

——————————————————————————————

Ace-window is (more than) a fancy window-switcher

The ace-window package, written by abo-abo, offers a handy, full-featured alternative to the built-in window-switching function other-window. The package’s primary function—the titular ace-window—overlays a different character (letter or number) over each visible Emacs window, so you can switch to the desired window by simply pressing its assigned character. Very useful, very sleek.

But ace-window is more than just a handy way to switch windows.

The package also offers the ability to perform an action in the selected window before switching to it. In this regard, it follows the design of its parent package avy—also by abo-abo. Instead of simply switching to a window, you can split it first, call switch-to-buffer in it, or even close it. For a demonstration of this aspect of ace-window, see this video from Protesilaos Stavrou.)

The greatness of ace-window truly comes through, however, when it is combined with other packages.

With ace-window and Embark, open files/buffers in specified windows

In his detailed write-up on using Embark, Karthik Chikmagalur showcases a killer mash-up of ace-window and Embark, demonstrating how ace-window can be used for more than just switching windows.

Briefly put, Karthik shows how to incorporate ace-window’s window-selection function into the opening of a file or buffer from the minibuffer so that, as he writes, “any buffer/file/bookmark I open is always placed exactly where I want it to be on the screen.”

An example usage would be:

  1. call find-file
  2. call embark-act on file you wish to open
  3. call my/embark-ace-action-find-file (this is the slightly unwieldy name Karthik gives to the embark/ace-window function)
  4. select desired window with ace-window

Inspired by what Karthik showed was possible, I wanted to incorporate ace-window with link-hint, so that I could choose which window a link would open in. The result is a function called link-hint-aw-select:

Using ’link-hint-aw-select’ to open each of the links in the top left window in a different window. First, the desired link is chosen via character overlay, courtsey of link-hint, then the desired window is chosen via character overlay, courtsey of ace-window.

Using ’link-hint-aw-select’ to open each of the links in the top left window in a different window. First, the desired link is chosen via character overlay, courtsey of link-hint, then the desired window is chosen via character overlay, courtsey of ace-window.

If you’re unfamiliar, link-hint is a package that is conceptually similar to ace-window: it puts a character overlay on visible links in a buffer so that you can choose a link to follow by typing its assigned character. (The conceptual similarity of link-hint and ace-window is not surprising, since both are based on avy.)

Link-hint can identify a lot of different type of links, such as urls, file paths, mailto links, org-links, buttons, and dired filenames. New link types can also be defined by the user. (More on that later.)

As with avy and ace-window, link-hint allows for different actions to performed on the chosen link. However, there are only two default actions for acting on links: open link and copy link.

Defining a new action is done by defining a new function, in this case the above-mentioned link-hint-aw-select, which will be entry-point command for all link types:

(defun link-hint-aw-select ()
  "Use avy to open a link in a window selected with ace-window."
  (interactive)
  (unless
      (avy-with link-hint-aw-select
        (link-hint--one :aw-select))
    (message "No visible links")))

Things get more complicated very quickly, however.

Each action must be tailored to each different link type. Take the case of the “open” action, for examples: urls open one way—in a browser—and filepaths open another way—in a relevant application—and email addresses open another way—in a new draft email. Each action is actually many different actions, all roughly similar.

When creating a new action, then, it is necessary to make sure that each link type is associated with a function that will perform the new action in the appropriate way.

The aw-select action for file links is quite simple:

(defun link-hint--aw-select-file-link (link)
  (with-demoted-errors "%s"
    (aw-switch-to-window (aw-select nil))
    (find-file link)))

To make the link-hint ecosystem aware of this new association of link-type, action, and function, there is a handy macro:

(link-hint-define-type 'file-link
  :aw-select #'link-hint--aw-select-file-link)

If different link-types behave similarly, it is possible to create a macro of our own that will do all of the above very efficiently. This is the case with buttons and dired-filenames, for example:

(defmacro define-link-hint-aw-select (link-type fn)
  `(progn
     (link-hint-define-type ',link-type
       :aw-select #',(intern (concat "link-hint--aw-select-"
                                     (symbol-name link-type))))
     (defun ,(intern (concat "link-hint--aw-select-"
                             (symbol-name link-type))) (_link)
       (with-demoted-errors "%s"
         (if (> (length (aw-window-list)) 1)
             (let ((window (aw-select nil))
                   (buffer (current-buffer))
                   (new-buffer))
               (,fn)
               (setq new-buffer (current-buffer))
               (switch-to-buffer buffer)
               (aw-switch-to-window window)
               (switch-to-buffer new-buffer))
           (link-hint-open-link-at-point))))))

(define-link-hint-aw-select button push-button)
(define-link-hint-aw-select dired-filename dired-find-file)

The same is almost the case with org-links as well, except that by default org-links are opened using find-file-other-window instead of find-file, meaning that the above macro wouldn’t work properly.

I prefer to change this setting globally, so that org-links are opened in the current window, by evaluating the following:

(setf (cdr (assoc 'file org-link-frame-setup)) 'find-file)

This allows me to use the macro:

(define-link-hint-aw-select org-link org-open-at-point)

To leave org’s default behavior in place globally, it is necessary to define a function specifically for org-links, in which the behavior is changed locally in a let-binding:

(defun link-hint--aw-select-org-link (_link)
  (let ((org-link-frame-setup
         '((file . find-file))))
    (with-demoted-errors "%s"
      (if (> (length (aw-window-list)) 1)
          (let ((window (aw-select nil))
                (buffer (current-buffer))
                (new-buffer))
            (org-open-at-point)
            (setq new-buffer
                  (current-buffer))
            (switch-to-buffer buffer)
            (aw-switch-to-window window)
            (switch-to-buffer new-buffer))
        (link-hint-open-link-at-point)))))

(link-hint-define-type 'org-link :aw-select #'link-hint--aw-select-org-link)

Things are actually even a bit more complicated than this with org-links.

Since org handles different link-types internally, the function link-hint--aw-select-org-link can also be made to handle certain org link-types differently. For example, if you want http/https links to be opened externally, there is no reason to call the aw-select behavior for that type of org-link. This could be accomplished by including something like the following in the function’s “if” conditional:

(not (member (org-element-property :type (org-element-context))
             '("http" "https")))

The whole function would be:

(defun link-hint--aw-select-org-link (_link)
  (let ((org-link-frame-setup
         '((file . find-file))))
    (with-demoted-errors "%s"
      (if (and (> (length (aw-window-list)) 1)
               (not (member (org-element-property
                             :type (org-element-context))
                       '("http" "https"))))
          (let ((window (aw-select nil))
                (buffer (current-buffer))
                (new-buffer))
            (org-open-at-point)
            (setq new-buffer
                  (current-buffer))
            (switch-to-buffer buffer)
            (aw-switch-to-window window)
            (switch-to-buffer new-buffer))
        (link-hint-open-link-at-point)))))

Example use-case: note-taking, zettelkasten, etc.

I find this functionality really useful in my note-taking environment, where each of my notes contains several links to other notes. I rarely want to follow a link in the same window as the note where the link appears. I typically want to leave that note where it is and open a link somewhere else. If I have several windows arrayed around my screen, I can use link-hint-aw-select to open the linked note exactly where I want it to be.

To use this with org, the above configuration is enough. If you use another note-taking environment, you may need to define a new link type.

For example, for use with my own Zettelkasten package zk, I define a new link type zk-link and a function zk-link-hint-aw-select, along with other functions necessary to fully incorporate a new link type into the link-hint system:

(defun zk-link-hint--zk-link-at-point-p ()
  "Return the ID for the zk-link at the point or nil."
  (and (zk--id-at-point)
       (thing-at-point-looking-at zk-link-regexp)))

(defun zk-link-hint--next-zk-link (bound)
  "Find the unext zk-link.
Only search the range between just after the point and BOUND."
  (link-hint--next-regexp zk-id-regexp bound))

(defun link-hint--aw-select-zk-link (id)
  (with-demoted-errors "%s"
    (if (> (length (aw-window-list)) 1)
        (let ((window (aw-select nil))
              (buffer (current-buffer))
              (new-buffer))
          (zk-follow-link-at-point id)
          (setq new-buffer
                (current-buffer))
          (switch-to-buffer buffer)
          (aw-switch-to-window window)
          (switch-to-buffer new-buffer))
      (link-hint-open-link-at-point))))

(link-hint-define-type 'zk-link
  :next #'zk-link-hint--next-zk-link
  :at-point-p #'zk-link-hint--zk-link-at-point-p
  :open #'zk-follow-link-at-point
  :copy #'kill-new
  :aw-select #'link-hint--aw-select-zk-link)

(push 'link-hint-zk-link link-hint-types)

Even though link-hint can be a bit complex under the hood, it is quite effortless, even elegant, when put in combination with ace-window. Give it a try and puzzle over what you had done so long without it.

« Older posts Newer posts »