Emacs Lisp: Writing a Custom HTML Link Function

Master emacs+lisp, benefit for life. Testimonials. Thank you for support.
, , …,

This page shows a example of writing a emacs lisp function that creates a customized HTML link. If you don't know elisp, first take a gander at Emacs Lisp Basics.

Problem Description

Summary

I want to write a command that changes the current word into a HTML link. Examples:

The exact path of the link depends on the current file's location and existing file names and directory structure where the current file is.

Detail

I have a math dictionary website, with directory structure like this:

curves/circle/circle.html
curves/ellipse/ellipse.html
curves/parabola/parabola.html
curves/hyperbola/hyperbola.html
curves/spiral/spiral.html
curves/cycloid/cycloid.html
…

Each of the page has many cross-reference links to other pages.

Suppose am writing the circle page. Suppose i write: “circle is a special case of ellipse”. While the cursor is on the word “ellipse”, i want to press a button, and it changes to a link to the ellipse page.

Note that the path for the HTML link is not always the same. It depends on the current file's location.

For sample pages, see: Conic SectionsHyperboloid of One Sheet.

Solution

Here's the basic steps:

Grab the Word Under Cursor

Getting the word under cursor can be done like this:

(setq cursorWord
 (if (use-region-p)
  (buffer-substring-no-properties (region-beginning) (region-end))
   (thing-at-point 'word)
))

For detail, see: Emacs Lisp: Using thing-at-point.

Construct a List of Paths

Here's the part that constructs a list of all possible paths to check:

;; the paths to test
(setq testPaths
      (vector
       (concat "~/web/xahlee_org/SpecialPlaneCurves_dir/" (upcase-initials wordPath) "_dir/" wordPath ".html")
       (concat "~/web/xahlee_org/surface/" wordPath "/" wordPath ".html")))

Check File Existence

Now, we can check file existence like this:

;; loop thru the paths until a file is found
(setq ξfound-p nil)
(setq ξi 0)
(while (and (not ξfound-p) (< ξi (length testPaths)))
  (setq rPath (elt testPaths ξi))
  (setq ξfound-p (file-exists-p rPath))
  (setq ξi (1+ ξi)))

If found, then delete the word under cursor, and construct the link and insert it.

(if ξfound-p
        (progn
          (setq linkWord (replace-regexp-in-string "_" " " cursorWord))
          (delete-region p1 p2)
          (insert (concat "<a href=\"" (file-relative-name rPath) "\">" linkWord "</a>")))
      (progn (beep) (message "No file found")))

If not found, beep and print a message.

In elisp, function or variable names that's used for true/false usually ends in “p”, by convention. (“p” for “predicate”, from study of logic.) For example: integerp, vectorp, region-active-p, ….

Final Code

(defun xah-curve-linkify ()
  "Make the current word or text selection into a HTML link.

This function works on Xah Lee's website only.
 Example:
 “parabola” becomes
“<a href=\"../Parabola_dir/parabola.html\">parabola</a>”.

The directory to search includes:
“SpecialPlaneCurves_dir” and “surface”."
  (interactive)
  (let (bds p1 p2 cursorWord wordPath ξi testPaths ξfound-p rPath linkWord)

    (setq bds (get-selection-or-unit 'glyphs))
    (setq cursorWord (elt bds 0) )
    (setq p1 (aref bds 1) )
    (setq p2 (aref bds 2) )

    ;; word for constructing possible dir
    (setq wordPath (replace-regexp-in-string " " "_" (downcase cursorWord)))

    ;; the paths to test
    (setq testPaths
          (vector
           (concat "~/web/xahlee_org/SpecialPlaneCurves_dir/" (upcase-initials wordPath) "_dir/" wordPath ".html")
           (concat "~/web/xahlee_org/surface/" wordPath "/" wordPath ".html")))

    ;; loop thru the paths until a file is found
    (setq ξfound-p nil)
    (setq ξi 0)
    (while (and (not ξfound-p) (< ξi (length testPaths)))
      (setq rPath (elt testPaths ξi))
      (setq ξfound-p (file-exists-p rPath))
      (setq ξi (1+ ξi)))

    (if ξfound-p
        (progn
          (setq linkWord (replace-regexp-in-string "_" " " cursorWord))
          (delete-region p1 p2)
          (insert (concat "<a href=\"" (file-relative-name rPath) "\">" linkWord "</a>")))
      (progn (beep) (message "No file found")))))

In the code above, i used my own library get-selection-or-unit to grab the word under cursor, but you can just use thing-at-point.

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 ♥

Like what you read?
Buy Xah Emacs Tutorial
or share some
blog comments powered by Disqus