Sat, 16 Aug 2008

linum.el

I found a very useful Emacs minor mode today: linum.el, which puts line numbers at the beginning of all the lines in a buffer.


Fri, 15 Aug 2008

Fun with Emacs: eval-after-load*

Ok, suppose you want to evaluate a particular bit of code after emacs loads a particular emacs-lisp file, but you want to pass values of local variables into that code. The function eval-after-load makes you quote the expression and doesn't allow passing values into the expression. How about this?

(require 'cl)
(defmacro* eval-after-load* (file varlist &rest body)
  "Like `eval-after-load', but bind variables according to VARLIST in
the current environment of the `eval-after-load' expression, not the
environment when BODY is evaluated.  This allows easy passing of values
into BODY.
Each element of VARLIST is a symbol (which is bound to the current value
of that symbol) or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the
value of VALUEFORM in the environment of the `eval-after-load' expression."
  `(eval-after-load ,file
     '(let ,(loop for v in varlist
                  collect (if (symbolp v)
                              `(,v ,(eval v))
                            `(,(car v) ,(eval (cadr v))))
                  into new-varlist
                  finally return new-varlist) ,@body)))
(put 'eval-after-load* 'lisp-indent-function
     (1+ (get 'eval-after-load 'lisp-indent-function)))

Here's a contrived example which demonstrates when things happen.

(let ((f (make-temp-file "tkb-madness" nil ".el"))
      (x 1))
  (unwind-protect
      (progn
        (save-excursion
          (let ((buf (find-file f)))
            (insert (format "(y-or-n-p \"In the file '%s'! \")" f))
            (save-buffer)
            (kill-buffer buf)))
        (y-or-n-p "This happens before the eval-after-load*")
        (eval-after-load* f
            (x
             (y (y-or-n-p "This happens when the eval-after-load* is executed?"))
             (z 2))
          (y-or-n-p (format "x: %d y: %S z: %d" x y z))
          (y-or-n-p "This happends during the delayed expressions"))
        (y-or-n-p "This happens after the eval-after-load* expression")
        (load f))
    (when (file-exists-p f) (delete-file f))))

You should see something like:

Wrote /tmp/tkb-madness88647vuE.el
This happens before the eval-after-load*(y or n)
This happens when the eval-after-load* is executed?(y or n)
This happens after the eval-after-load* expression(y or n)
Loading /tmp/tkb-madness88647vuE.el (source)...
In the file '/tmp/tkb-madness88647vuE.el'! (y or n)
x: 1 y: t z: 2(y or n)
This happends during the delayed expressions(y or n)
Loading /tmp/tkb-madness88647vuE.el (source)...done

(y-or-n-p is used instead of message so you see each message when it happens.)

Does the eval-after-load* macro make sense?


Fun With Emacs: Unicode and #'describe-char

Ever use the emacs command describe-char? It's even more fun with proper unicode lookup data!

;; First, we'll bind it to a key.
(global-set-key "\C-cD" #'describe-char)

;; Now we'll download it if necessary.
(let ((udf-url "http://www.unicode.org/Public/UNIDATA/UnicodeData.txt")
      (udf-dest "~/tmp/UnicodeData.txt"))
  (if (file-readable-p udf-dest)
      ;; Let describe-char know it exists.
      (setq describe-char-unicodedata-file udf-dest)
    ;; It doesn't exist, and we need to download it!
    (when (y-or-n-p (format "You need to download %s ! Do it? " udf-url))
      ;; Really weird: wget -O 'file' complains that file doesn't exist.
      (let* ((cmd (format "cd ~/tmp/ && wget -O %s --progress=dot '%s'"
                          udf-dest udf-url))
             (buf (get-buffer-create (format " *wget '%s'*" udf-url)))
             (proc (start-process-shell-command "wget-unicode-Data"
                                                buf cmd)))
        (display-buffer buf)
        (set-process-sentinel
         proc
         `(lambda (proc event)
            (unless (string-match "^finished" event)
              (error "unexpected status '%s' getting '%s'" ,udf-url event))
            (setq describe-char-unicodedata-file ,udf-dest)
            (message "Try describe-char now! ☣☥☸▧◉✘✽☮⅙▧⚅☑☢☹☺♠♥♦♣♨♻⚔")))
        (message "Downloading... check describe-char later")
        nil))))

Once this is run and it tells you to try describe-char you can position your cursor over one of the Unicode characters in the message (“C-h e” to display the “*Messages*” buffer) and press “C-cD” and look for the “Name:” line. You'll see something like this:

      character: ♻ (299515, #o1110773, #x491fb, U+267B)
        charset: mule-unicode-2500-33ff (Unicode characters of the range U+2500..U+33FF.)
     code point: #x23 #x7B
         syntax: w    which means: word
    buffer code: #x9C #xF2 #xA3 #xFB
      file code: #xE2 #x99 #xBB (encoded by coding system mule-utf-8)
        display: terminal code #xE2 #x99 #xBB
   Unicode data:
           Name: BLACK UNIVERSAL RECYCLING SYMBOL
       Category: other symbol
Combining class: Spacing
  Bidi category: Other Neutrals