Emacs Lisp: Writing a make-citation Command

Perm url with updates: http://xahlee.org/emacs/elisp_make-citation.html

Emacs Lisp: Writing a make-citation Command

Xah Lee, 2011-08-15

This page shows you how to write a emacs lisp command that transforms a text block under cursor into a specific citation format.

Problem

Summary

Write a elisp command so that when called, and if cursor is somewhere in a text like this:

Defective C++
By Yossi Kreinin
2007
http://yosefk.com/c++fqa/defective.html

It becomes this:

<cite>Defective C++</cite> (2007) By Yossi Kreinin. @ <a class="sorc" href="http://yosefk.com/c++fqa/defective.html" title="accessed:2011-08-15">Source yosefk.com</a>

Detail

I write many blogs. When i make a link, i like to also include the article title, author, date. This would help solving the link rot problem. (when a link is dead, at least the reader still knows the title, author, date.) For example, here's a typical link:

<a href="http://yosefk.com/c++fqa/defective.html">http://yosefk.com/c++fqa/defective.html</a>

I would like it to be like this:

<cite>Defective C++</cite> (2007) By Yossi Kreinin. @ <a class="sorc" href="http://yosefk.com/c++fqa/defective.html" title="accessed:2011-08-15">Source yosefk.com</a>

With proper CSS, it is rendered in browsers like this:

Defective C++ (2007) By Yossi Kreinin. @ Source yosefk.com

It is quite tedious to get the title, author, date, from a site. But once i got these info manually, i can automate the part of formatting. So, i start with this text:

Defective C++
By Yossi Kreinin
2007
http://yosefk.com/c++fqa/defective.html

Then, pressing a button, the text will be transformed to the desired format.

Solution

Here's the outline of steps:

  • Get the input text. (get their boundary positions)
  • Split the input text by line break.
  • Process each line into proper format.
  • Delete the input text.
  • Insert new next.

Here's the code:

(defun make-citation ()
  "Reformat current text block or selection into a canonical citation format.

For example, place cursor somewhere in the following block:

Circus Maximalist
By PAUL GRAY
Monday, Sep. 12, 1994
http://www.time.com/time/magazine/article/0,9171,981408,00.html

After execution, the lines will become

<cite>Circus Maximalist</cite> (1994-09-12) By Paul Gray. @ <a href=\"http://www.time.com/time/magazine/article/0,9171,981408,00.html\">Source www.time.com</a>

If there's a text selection, use it for input, otherwise the input is a text block between empty lines."
  (interactive)
  (let (bds p1 p2 ξmeat mylist ξtitle ξauthor ξdate ξurl )

    (setq bds (get-selection-or-unit 'block))
    (setq ξmeat (elt bds 0) )
    (setq p1 (elt bds 1) )
    (setq p2 (elt bds 2) )

    (setq mylist (split-string ξmeat " *\n *" t) )

    (setq ξtitle (elt mylist 0))
    (setq ξauthor (elt mylist 1))
    (setq ξdate (elt mylist 2))
    (setq ξurl (elt mylist 3))

    (setq ξauthor (replace-regexp-in-string "\\. " " " ξauthor)) ; remove period in Initals 
    (setq ξauthor (replace-regexp-in-string "By +" "" ξauthor))
    (setq ξauthor (upcase-initials (downcase ξauthor)))
    (setq ξdate (fix-timestamp-string ξdate))

    (setq ξurl (with-temp-buffer (insert ξurl) (source-linkify) (buffer-string)))

    (delete-region p1 p2 )
    (insert (concat "<cite>" ξtitle "</cite>") " " "(" ξdate ")"  " By " ξauthor ". @ " ξurl)
    ))

The code is pretty simple. Grabbing the text is done by:

    (setq bds (get-selection-or-unit 'block))
    (setq ξmeat (elt bds 0) )
    (setq p1 (elt bds 1) )
    (setq p2 (elt bds 2) )

The “get-selection-or-unit” is my custom function as a replacement for elisp's “thing-at-point”. It returns a vector [‹text› ‹begin boundary› ‹end boundary›]. (See: Emacs Lisp: Using thing-at-point for detail.)

Then, we split the text into lines:

    (setq mylist (split-string ξmeat " *\n *" t) )

    (setq ξtitle (elt mylist 0))
    (setq ξauthor (elt mylist 1))
    (setq ξdate (elt mylist 2))
    (setq ξurl (elt mylist 3))

process each line:

    (setq ξauthor (replace-regexp-in-string "\\. " " " ξauthor)) ; remove period in Initals 
    (setq ξauthor (replace-regexp-in-string "By +" "" ξauthor))
    (setq ξauthor (upcase-initials (downcase ξauthor))) ; some site has author name in all caps
    (setq ξdate (fix-timestamp-string ξdate)) ; transform the date format to yyyy-mm-dd

The “fix-timestamp-string” transforms arbitrary datetime format into a canonical form yyyy-mm-dd. (ISO 8601) For examples:

  • Sat, 23 Jul 2011 08:13:51 +01002011-07-23
  • Jul 23, 20112011-07-23
  • 7/23/20112011-07-23

Try to write that yourself. I'll post a solution in 2 days.

Now, we change the url into a link:

    (setq ξurl (with-temp-buffer (insert ξurl) (source-linkify) (buffer-string)))

The “source-linkify” is a command i wrote to change url to a link into a special format for my own blogs. For example, it changes this:

http://yosefk.com/c++fqa/defective.html

into this:

<a class="sorc" href="http://yosefk.com/c++fqa/defective.html" title="accessed:2011-08-16">Source yosefk.com</a>

For detail of “source-linkify”, see: Emacs Lisp: Writing a url-linkify Command.

Finally, the code deletes the input text, and insert the new:

    (delete-region p1 p2 )
    (insert (concat "<cite>" ξtitle "</cite>") " " "(" ξdate ")"  " By " ξauthor ". @ " ξurl)

The weird ξ you see in my elisp code is Greek x. I use unicode char in variable name for experimental purposes. You can just ignore it. (See: Programing Style: Variable Naming: English Words Considered Harmful.)

Emacs Lisp is fantastic!

Popular posts from this blog

Browser User Agent Strings 2012

11 Years of Writing About Emacs

does md5 creates more randomness?