Emacs Lisp: Writing a make-citation Command

Advertise Here

, , …,

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

Problem Description

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 Cascading Style Sheet (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.

If you are curious, the CSS used is this:

cite {color:#822222}
cite:before,cite:after {color:black;font-style:normal}
cite:before {content:"〈"}
cite:after {content:"〉"}
cite.book:before {content:"《"}
cite.book:after {content:"》"}

See: CSS Tutorial. The angle brackets is Asian convention for book/article titles. (➲ Intro to Chinese Punctuation with Computer Language Syntax Perspectives)

Solution

Here's the outline of steps:

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 inputText myList ξtitle ξauthor ξdate ξurl )

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

    (setq inputText (replace-regexp-in-string "^[[:space:]]*" "" inputText)) ; remove white space in front

    (setq myList (split-string inputText "[[:space:]]*\n[[:space:]]*" 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 ξinputText (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 ξinputText "[[:space:]]*\n[[:space:]]*" 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:

See: Emacs Lisp: Writing a Date Time String Parsing Function.

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 symbol name for easy distinction from builtin symbols. You can just ignore it. (➲ Programing Style: Variable Naming: English Words Considered Harmful)

Emacs Lisp is fantastic!

blog comments powered by Disqus