From: Xah Lee
Subject: Elisp Lesson: wrap-url
Date: 
Message-ID: <1160575330.228549.112550@e3g2000cwe.googlegroups.com>
Elisp Lesson: wrap-url. Xah Lee, 2006-10

Suppose you work in HTML a lot, and often need to make a url into
a link.

Suppose you have this url http://en.wikipedia.org/wiki/Emacs

and your cursor is on that line. You want to be able to, press a
button, and have the text changed to

<a href="http://en.wikipedia.org/wiki/Emacs">Emacs</a>.

In this page we show you how to write this in lisp.

First, our function will be of this form

(defun wrap-url ()
  "Wee! make you a link!"
  (interactive)
; • grab the url in the buffer
; • parse the url into a list, and grab the last element
; this will be our linktext
; • insert string “<a href="” url “">” linktext “</a>”
)

We will construct little lisp functions for each step, then
put them all together.

; this snippet grabs the current block of text delimited by space.
; the key lies in the versatile re-search-backward and
re-search-forward
; and buffer-substring
(defun ff ()
"This snippet grabs the sequence of chars delimited by space."
(interactive)
(let (x1 x2 url)
  (progn
    (re-search-backward " ")
    (setq x1 (re-search-forward " "))
    (re-search-forward " ")
    (setq x2 (re-search-backward " "))
    (setq url (buffer-substring x1 x2))
    (kill-region x1 x2)
    (insert url)
    )
  )
)

; Return the last part in a url separated by slash.
; e.g. http://en.wikipedia.org/wiki/Green_tea → Green_tea."
; the key here is the function split-string
(defun ff () ""
(interactive)
(let ((url "http://en.wikipedia.org/wiki/Green_tea") linktext)
(insert (car (last (split-string url "/"))) )
  )
)

Now, we have the url string and the linktext string. We need to
concatenate them into one string. The key function is “concat”

; concatenate strings
(defun ff () "" (interactive)
  (insert (concat "a" "b" "c"))
)

Now we know how to code all the major steps for our wrap url
function. We can put them together.

(defun wrap-url ()
  "Make thing at cursor point into a html link.\n
Example: http://en.wikipedia.org/wiki/Emacs
becomes
<a href=\"http://en.wikipedia.org/wiki/Emacs\">Emacs</a>"
  (interactive)
(let (x1 x2 url linktext)
  (progn
    (re-search-backward " ")
    (setq x1 (re-search-forward " "))
    (re-search-forward " ")
    (setq x2 (re-search-backward " "))
    (setq url (buffer-substring x1 x2))
    (setq linktext (car (last (split-string url "/"))) )
    )
  (delete-region x1 x2)
  (insert (concat "<a href=\"" url "\">" linktext "</a>"))
  )
)

Now, we can test this. For example, here is a
url http://en.wikipedia.org/wiki/Emacs and put your cursor on the
url, and do M-x wrap-url. The url will turn into a link.

Now we need to refine the code and do some clean up. For example,
if the url starts on a line by itself, the function won't work,
because it is looking for spaces as the url's delimiters. We can
fix this by adding more chars in argument of the re-search-*
functions.

Also, suppose, if the link is not a wikipedia link, but a generic
link such as http://yahoo.com/news/3829.php then it makes little
sense to have the link text shown as 3829.php So, we modify the
code so that if it is not a wikipedia link, the link text should
be the full url.

Also, some links contain percent encoded characters.
For example, http://en.wikipedia.org/wiki/Europa_%28mythical%29
The %28 and %29 is really ( and ).
The linktext should show them as the decoded chars.

Another thing is that, wrap-url may be a useful function that can
be called by other functions. So, it would be useful, to make
wrap-url take a string input and return the processed text
as output. Then write a wrapper function that calls wrap-url for
interactive use.

The following is the result of this clean up.

The wrap-url-string is a function that takes a string and return a
string.

(defun wrap-url-string (url &optional raw)
  "Make the string into a html link.\n
Example:
http://en.wikipedia.org/wiki/Emacs
becomes
<a href=\"http://en.wikipedia.org/wiki/Emacs\">Emacs↗</a>.
If the url is a wikipedia link, then the link text
is shortened to its title.
If the optional argument raw is not nil, no shortening will happen."
  (require 'gnus-util)
  (let ((linktext url))
    (setq linktext (gnus-url-unhex-string linktext nil))
    (if (and (not raw) (string-match "wikipedia.org" url))
        (progn
          (setq linktext (concat (car (last (split-string linktext
"/"))) "↗") )
          )
      )
    (concat "<a href=\"" url "\">" linktext "</a>" )
    )
  )

Note: in the above, the wrap-url-string takes a optional argument
“raw”. This is done with &option. If the argument list contains
&option, then parameter after it is optional. If not given, the
default value is nil. For detail, see:
http://xahlee.org/elisp/Argument-List.html or
(info "(elisp)Argument List")

Also, we needed to turn the percent encoded strings such as %28
%29 to ( ). Lucky for us, such function already exist, in the
package gnus-util as gnus-url-unhex-string. (it is bundled with
emacs) So, we load the package by (require 'gnus-util).

Now, the following wrap-url function is the interactive wrapper
that calls wrap-url-string.

(defun wrap-url (&optional raw)
  "Make thing at cursor point into a html link.\n
Example: http://en.wikipedia.org/wiki/Emacs
becomes
<a href=\"http://en.wikipedia.org/wiki/Emacs\">Emacs↗</a>

If the url is a wikipedia link, then the linktext is shortened to
the article name. If the optional argument raw is non-nil, then
no shortening is done."
  (interactive "P")
  (re-search-backward "[\n\t ()<>「」]" nil t)
  (looking-at "[\n\t ()<>「」]?\\([^\n\t ()<>「」]+\\)")
(let (
      (p1 (match-beginning 1))
      (p2 (match-end 1))
      (url (match-string 1))
      )
  (delete-region p1 p2)
  (goto-char p1)
  (insert (wrap-url-string url raw))
)
)

The code uses looking-at, instead of the numerous re-search-*
functions to grab the url. We used a set of characters for the
url delimiter, so that for example, url inside
parenthesis (http://xahlee.org/) will not include the parenthesis
as part of the url string.


What described above is the general process of writing code in
lisp. In fact, it is the general process in functional
programing. Start with a spec sketch. Then break it down to small
components. Code the components, then finally string them
together into a larger function. Or, collect functions together
into a coherent package.
----
This post is archived at:
http://xahlee.org/emacs/wrap-url.html

  Xah
  ···@xahlee.org
∑ http://xahlee.org/