Monday, September 09, 2013

emacs function: humanifying URLs

Sometimes I need to share a long URL in an e-mail or document with the purpose of making it clear, rather than making it clickable. When I need to do this, I'll rewrite the URL by add newlines and whitespace to ? and & characters and URL-decode parameter values. That turns this:

http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CCwQFjAA&url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FEditor_war&ei=k9ktUuz2Kvaj4APCmoGIBw&usg=AFQjCNGrgP7q1TYCPmuQnkgcfMIJKpFHOg&sig2=a6GUXFARKXj6r1JyiNkQ8w&bvm=bv.51773540,d.dmg

Into this:

http://www.google.com/url?
  sa=t&
  rct=j&
  q=&
  esrc=s&
  source=web&
  cd=1&
  cad=rja&
  ved=0CCwQFjAA&
  url=http://en.wikipedia.org/wiki/Editor_war&
  ei=k9ktUuz2Kvaj4APCmoGIBw&
  usg=AFQjCNGrgP7q1TYCPmuQnkgcfMIJKpFHOg&
  sig2=a6GUXFARKXj6r1JyiNkQ8w&
  bvm=bv.51773540,d.dmg

This format makes discussing or catching issues with individual URL parameters much easier.

For years I've used a created-on-the-fly emacs macro to help speed up the above conversion. And while I love keyboard macros I was many years overdue in turning this into an elisp function. Last week I finally corrected this oversight. Here's the code:

(defun url-humanify ()
  "Take the URL at point and make it human readable."
  (interactive)
  (let* ((area (bounds-of-thing-at-point 'url))
         (num-params  (count-occurances-in-region "&" (car area) (cdr area)))
         (i 0))
    (beginning-of-thing 'url)
    (when (search-forward "?" (cdr area) t nil)
      (insert "\n  ")
      (while (< i num-params)
        (search-forward "&" nil t nil)
        (insert "\n  ")
        (save-excursion
          (previous-line)
          (beginning-of-line)
          (let ((start (search-forward "="))
                (end (search-forward "&")))
            (url-decode-region start end)))
        (setq i (+ i 1))))))

(defun url-decode-region (start end)
  "Replace a region with the same contents, only URL decoded."
  (interactive "r")
  (let ((text (url-unhex-string (buffer-substring start end))))
    (delete-region start end)
    (insert text)))

(defun count-occurances-in-region (needle start end)
  (save-excursion
    (let ((found 0))
      (goto-char start)
      (while (search-forward needle end t nil)
        (setq found (+ found 1)))
      found)))

(The usual disclaimers apply: url-humanify is not the most efficient elisp implementation, but it should reliably work. That (set i (+ i 1)) code shows I was thinking PHP instead of lisp; but so be it. At least I depend on thing-at-point to do the URL parsing. Oh, and I've included url-decode-region as a bonus. It's another function I use all the time.)

Now, whenever I have an ugly URL, I just need to execute M-x url-humanify and it all becomes clear.

Hope this saves you some typing.

Update: D'oh - forgot to include the count-occurances-in-region. As @anonymous pointed out, I should probably be using count-matches.

5 comments:

  1. Anonymous11:26 AM

    To make this work for me I had to replace count-occurances-in-region with count-matches, as the first isn't known.

    ReplyDelete
  2. D'oh - nice catch. All fixed. Next time, I'll have to use count-matches.

    ReplyDelete
  3. Anonymous9:57 AM

    delete-and-extract-region shortens things too!

    ReplyDelete
  4. You oughta put this up on marmalade. Let me know I can send you a file to show you how it is really simple.

    Also, are you on planet emacs?

    ReplyDelete
  5. > You oughta put this up on marmalade. Let me know I can send you a file to show you how it is really simple.

    Sounds tasty. It would be awesome if you sent me a sample file.

    > Also, are you on planet emacs?

    I am.

    Thanks Grant!

    ReplyDelete