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

11 Years of Writing About Emacs

does md5 creates more randomness?

Google Code shutting down, future of ErgoEmacs