Archive for the ‘emacs’ Category

Uniquify and ido

Monday, June 16th, 2008

A very quick post today to mention a couple of core modules I’ve included in my config that make using emacs even nicer.

I’ve been using uniquify for a while as the default emacs behaviour when loading two identically named files is a bit naff1. I found this tip here.

(requireuniquify)

(setq uniquify-buffer-name-style ‘reverse)
(setq uniquify-separator “|”)
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re “^\\*”)

I’ve just added ido to my config today based on a post I found at the emacs blog. It might take a bit of getting used to but we’ll see how that goes.

(ido-mode t)
(setq ido-enable-flex-matching t)

I’ve added those sections to a file I have called my-defaults.el that contains my really short pieces of configuration.

my-defaults.el

;; ———————————————————————- ;;

(requireido)
(requireuniquify)

;; ———————————————————————- ;;

(global-font-lock-mode 1)
(setq inhibit-splash-screen t)
(setq font-lock-maximum-decoration 3)
(fset ‘yes-or-no-p ‘y-or-n-p)
(tool-bar-mode -1)
(show-paren-mode 1)
(transient-mark-mode t)
(customize-set-variable ’scroll-bar-mode ‘right)
(setq case-fold-search t)

(line-number-mode 1)
(column-number-mode 1)

;; ———————————————————————- ;;

(setq backup-directory-alist
      (cons ‘(“.” . “~/.emacs-backups”) backup-directory-alist))

;; ———————————————————————- ;;

(ido-mode t)
(setq ido-enable-flex-matching t)

;; ———————————————————————- ;;

(setq uniquify-buffer-name-style ‘reverse)
(setq uniquify-separator “|”)
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re “^\\*”)

;; ———————————————————————- ;;

(providemy-defaults)

;; ———————————————————————- ;;

1. It appends an integer in angle brackets to the second file with a non-unique name, e.g. file.cpp<2>.

Emacs templates

Sunday, June 15th, 2008

Currently I use muse with htmlize to write my blog posts but I’m always on the lookout for something better e.g. nxml-mode supported by templates. I tried it out recently to see if it was a worthy replacement. The conclusion in my case, for those with short attention spans, is not so far.

Muse provides a mechanism for including files, including the result of running emacs-lisp, perl or python code, entering verbatim html and a whole bunch of other stuff, but the two most important features for me are that with a bare minimum of markup I can layout my post as I want1 and include syntax highlighted code. A possible alternative to minimal markup would be to have a bunch of templates that I can enter with a few keysrokes.

The emacs wiki has a nice set of links to templating engines. After a few minutes experimentation, I decided that skeleton mode2 was the one for me. One of the most verbose html constructs is the table. A basic two by two table will include the following html tags.

<table>
  <tr>
    <td></td>
    <td></td>
  </tr>

  <tr>
    <td></td>
    <td></td>
  </tr>
</table>

Here is the code to define a skeleton that inserts a 1 by 1 table.

(define-skeleton sk-html-table-1-1
  “Insert a 1 by 1 html table”
  “”
  “<table>”            > “\n”
  ”  <tr>”             > “\n”
  ”    <td>” _ “</td>” > “\n”
  ”  </tr>”            > “\n”
  “</table>” >)

That is pretty inflexible. What if we want to have a different number of rows and columns? Unfortunately, define-skeleton doesn’t work quite like you might want. Rather than inserting the tags five times, the following code doesn’t insert anything.

(define-skeleton sk-test
  “xyz”
  “”
  (dotimes (rep 5)
     “<a>”     > “\n”
     “<b></b>” > “\n”
     “</a>”    > “\n”))

To surround each string with (insert ...) would be far too verbose. What we want is a function that handles strings and > in the same way as define-skeleton. Unfortunately that is not possible. If you call a function like this (func >) then > will be evaluated before the function is called. In this case it will try to find a variable called > which probably won’t exist and then emacs will complain

let: Symbol's value as variable is void: >

To avoid the evaluation, what we need is a macro.

(defmacro skeleton-list (&rest params)
  (dolist (elem params)
    (if (eq elem ‘>)
        (indent-according-to-mode)
      (insert elem))))

sk-test then becomes trivial.

(define-skeleton sk-test
  “xyz”
  “”
  (dotimes (rep 5)
    (skeleton-list “<a>”     > “\n”
                   “<b></b>” > “\n”
                   “</a>”    > “\n”)))

We can call skeleton-read to ask for the number of rows and columns we would like our table to have. Then, using skeleton-list, the code for a basic table is the following3.

(define-skeleton sk-html-table
  “Insert an html table”
  “”
  ‘(setq v1 (skeleton-read “table rows: “))
  ‘(setq v2 (skeleton-read “table columns: “))
  “<table>”            > “\n”

  ”  <tr>”             > “\n”
  ”    <td>” _ “</td>” > “\n”
  (dotimes (col (1- (string-to-int v2)))
    (skeleton-list “<td></td>” > “\n”))
  ”  </tr>”             > “\n”

  (dotimes (row (1- (string-to-int v1)))
    (skeleton-list “\n” “<tr>” > “\n”)
    (dotimes (col (string-to-int v2))
      (skeleton-list “<td></td>” > “\n”))
    (skeleton-list “</tr>” > “\n”))

  “</table>” >)

This does make creating tables quite a bit easier, but for me, even if I had a full set of templates, muse is still a superior option for at least the following reasons:

  1. The file with minimal mark-up is easier to work on.
  2. Source code colouring is nicely integrated into muse with htmlize
  3. Anything that html can do, so can muse with the use of the <verbatim> tag.

JG


1. For example, I simply enter a paragraph normally, separated by carriage returns and it is automatically surrounded by <p> tags when I generate the html.

2. It isn’t actually a mode in the emacs sense.

3. There is probably a better solution lurking within skeleton mode that doesn’t require any extensions. If anyone knows of one, I’d appreciate hearing about it.

Emacs keymaps - prefix keys

Wednesday, June 4th, 2008

If you look at all the key-bindings with (describe-bindings) you will see that there are not very many single control key combinations remaining. For this reason, emacs allows prefix keys and the guidelines recommend that user-defined bindings are prefixed with C-c1.

After extensive customisation, you may find that you start to run out of two-key-combos prefixed with C-c. Is it possible to have a three-key-combo? Of course it is - take C-x-4-[something] or C-x-5-[something] for example.

I have a number of bindings beginning with C-ci2 for inserting things. For example, I use C-cid for inserting today’s date in YYYYMMDD format3. However, if you try (global-set-key "\C-cid" 'my-insert-date) emacs complains that C-c i is not a prefix key.

Debugger entered--Lisp error: (error "Key sequence C-c i d starts with non-prefix key C-c i")
  define-key([snip] …) “\Cid” my-insert-date)
  global-set-key(”\Cid” my-insert-date)
  eval((global-set-key “\Cid” (quote my-insert-date)))
  eval-last-sexp-1(nil)
  eval-last-sexp(nil)
  call-interactively(eval-last-sexp)

You first have to define C-ci as a prefix key by setting it to a keymap.

(defvar my-insert-map nil)
(setq my-insert-map (make-sparse-keymap))

(global-set-key “\C-ci” my-insert-map)
(global-set-key “\C-cid” ‘my-insert-date)

Note that setting the prefix key passes the keymap rather than a symbol with the name of the keymap. I first tried (global-set-key "\C-ci" 'my-insert-map) and wondered why it wasn’t working.

And if you were at all curious about the definition of my-insert-date, here it is.

(defun my-insert-date ()
  (interactive)
  (insert (format-time-string “%Y%m%d”)))

JG


1. That is emacs parlance for CTRL-c.

2. I think I saw an article about using C-ci as in insert prefix before but I don’t remember where so apologies for the lack of attribution.

3. Yes, it doesn’t save many keystrokes but for a guy like me who can’t remember what day it is, let alone the date, it lightens the cognitive load considerably.

Vertical buffer mode

Wednesday, May 28th, 2008

Something I’ve heard and read more than a few times is "why should I use emacs when I have notepad / wordpad / textpad". At least notepad and wordpad are not serious contenders as a programmers editor, but textpad has a few nice features such as the ability to edit multiple files and source code coloring. Of course, emacs can do both of these so the most recent time a developer said it I challenged them as to why they prefer Textpad to emacs.

The first complaint, quite predictably, was that Ctrl-C / Ctrl-V doesn’t work as they are used to. Emacs 22 ships with a mode that fixes this - cua-mode. The developer was pleasantly surprised to discover that activating this mode makes mark/cut/copy/paste work as they would expect1.

(cua-mode t)

The next thing he mentioned that he liked was the list of all currently opened files. Hmmm… C-x C-b (list-buffers) gives the same thing right? Actually this wasn’t quite what he wants. Textpad keeps a permanent list of opened files in a noarrow frame at the left of the window. This strikes me as a waste of space, but I guess the customer is always right. Let me hack something up quickly.

(defun vertical-buffer-list ()
  (interactive)
  (list-buffers)
  (delete-other-windows)
  (split-window-horizontally 30)
  (switch-to-buffer “*Buffer List*”)
  (toggle-read-only 0)
  (sort-lines nil (point-min) (point-max))
  (toggle-read-only t)
  (other-window 1)
  (move-beginning-of-line 1))

(global-set-key [f3] ‘vertical-buffer-list)

He was relatively happy with how that looked, but clicking on the file opened the buffer in the buffer-list frame, not in the main frame. The function that runs when the mouse is clicked is Buffer-menu-mouse-select. The obvious solution is to flip the buffers after it finishes running which can be implemented simply using advice.

(defun buffer-flip-windows ()
  (let ((b (current-buffer)))
    (switch-to-buffer “*Buffer List*”)
    (other-window 1)
    (switch-to-buffer b)))

(defadvice Buffer-menu-mouse-select (after buffer-other-window)
  “Open the new buffer in the other window”
  (funcall ‘buffer-flip-windows))

(ad-activate ‘Buffer-menu-mouse-select)

Of course, this is a pretty ugly/fragile/unconfigurable hack, but it shows what can be mocked-up in a few lines of emacs-lisp. If you want to see what emacs is really capable of along these lines check out ECB.

After I finished, I used it myself briefly but found I was always closing the buffer-list window to maximise real-estate. I modified the advice to the following:

(defadvice Buffer-menu-mouse-select (after buffer-other-window)
  “Delete the other windows after opening the new buffer”
  (delete-other-windows))

Why should a developer use emacs rather than Textpad? In emacs, everything is infinitely changeable and extendable. I have written many tiny functions to save me time while working and also a couple of medium applications that sit nicely within the emacs framework. Fantastically useful tools such as org-mode, muse and fly-make have been implemented and are simple to install and use. With emacs, there are no real limits. That is why I use emacs. However, it might not be a good reason for you.

JG


1. I almost said correctly there, but of course the emacs way is the correct way.

Quiz mode

Monday, May 26th, 2008

Here is a tiny emacs app just for fun. Someone asked me for a program that would test their subject knowledge prior to an exam. I could have implemented this in a million different ways and I chose emacs to make a simple keyboard driven interface.

The questions are along the lines of given a case who is the claimant and who is the defendant. The obvious data structure is a list of triples containing the name of the case and the parties. As most of emacs editing functionality is not required it makes sense to make a major mode which I will call quiz.

Specifying all of the questions took most of the time…

(defconst *quiz-questions*
  ‘(
    ["Invitation to treat" "Fisher" "Bell"]
    ["Offer" "Carlill" "Carbolic Smoke"]
    ;; … More cases
    ))

(defconst *quiz-number-of-questions*
  (length *quiz-questions*))

(defvar quiz-current-question 0)

A bunch of accessors is just good style in case I decide on an alternative data structure.

(defsubst quiz-get-case (q) (aref q 0))
(defsubst quiz-get-claimant (q) (aref q 1))
(defsubst quiz-get-defendant (q) (aref q 2))
(defsubst quiz-get-number-of-parties (q) (- (length q) 1))

I store the number of the current question and the current question in quiz-current question.

(defun quiz-choose-question ()
  (let ((n (random *quiz-number-of-questions*)))
    (setq quiz-current-question (cons n (nth n *quiz-questions*)))))

This user would probably like to see all of the options available at all times. Therefore asking a question should look like this:

(n)ext | (a)nswer | (q)uit

Offer --> ?

…and selecting the answer should say:

(n)ext | (a)nswer | (q)uit

Offer --> Carlill vs Carbolic Smoke
(defun quiz-init-buffer ()
  (erase-buffer)
  (insert “(n)ext | (a)nswer | (q)uit\n\n”))

(defun quiz-display-question ()
  (let ((q (quiz-get-case (cdr quiz-current-question))))
    (insert (format “%s –> ?” q))
    (backward-char)))

(defun quiz-parties-to-string (c)
  (let ((p (quiz-get-number-of-parties c))
        (claimant (quiz-get-claimant c)))
    (if (= p 1) (format “%s” claimant)
      (format “%s vs %s” claimant (quiz-get-defendant c)))))

The actual interface is provided by quiz-next-question quiz-get-answer and quiz-quit. get-answer could do with a bit of a tidy-up but for a quick hack it will do.

(defun quiz-next-question ()
  (interactive)
  (quiz-init-buffer)
  (quiz-choose-question)
  (quiz-display-question))

(defun quiz-get-answer ()
  (interactive)
  (let* ((q (cdr quiz-current-question))
         (c (quiz-get-case q))
         (p (quiz-parties-to-string q)))
    (quiz-init-buffer)
    (insert (format “%s –> %s” c p))))

(defun quiz-quit ()
  (interactive)
  (when (y-or-n-p “Really quit? “)
    (save-buffers-kill-emacs)))

Now set up the major mode. It is a lot easier than you might think with define-derived-mode.

A quick note about the keymap: I want to disable other key-bindings. There are a couple of ways to do this: (suppress-keymap quiz-mode-map) or (define-key quiz-mode-map [t] ‘undefined). I used the second of these which disables every key you haven’t defined but I add back in M-x as I’m always scared of being trapped in a mode with no way out.

(defvar quiz-mode-map nil
  “Keymap for quiz major mode.”)

(define-derived-mode quiz-mode text-mode “Quiz”
  “Major mode for interacting with databases.
Special commands:
\\{quiz-mode-map}”)

(if quiz-mode-map
    nil
  (setq quiz-mode-map (make-sparse-keymap))
  (define-key quiz-mode-map “n” ‘quiz-next-question)
  (define-key quiz-mode-map “a” ‘quiz-get-answer)
  (define-key quiz-mode-map “q” ‘quiz-quit)
  (define-key quiz-mode-map [t] ‘undefined)
  (define-key quiz-mode-map “\M-x” ‘execute-extended-command))

(defun quiz-enter-quiz-mode ()
  (interactive)
  (switch-to-buffer “*quiz*” t)
  (random t)
  (quiz-mode)
  (quiz-next-question))

Finally, load up the defaults and enter the mode when we start.

(load “c:/packages/emacs-files/my-vars.el”)
(add-to-list ‘load-path *elisp-dir*)

(requiremy-defaults)

;; 
(quiz-enter-quiz-mode)

The complete file looks like this:

;; ———————————————————————- ;;

(load “c:/packages/emacs-files/my-vars.el”)
(add-to-list ‘load-path *elisp-dir*)

(requiremy-defaults)

;; ———————————————————————- ;;

(defconst *quiz-questions*
  ‘(
    ["Invitation to treat" "Fisher" "Bell"]
    ["Offer" "Carlill" "Carbolic Smoke"]
    ;; … More cases
    ))

(defconst *quiz-number-of-questions*
  (length *quiz-questions*))

(defvar quiz-current-question 0)

(defsubst quiz-get-case (q) (aref q 0))
(defsubst quiz-get-claimant (q) (aref q 1))
(defsubst quiz-get-defendant (q) (aref q 2))
(defsubst quiz-get-number-of-parties (q) (- (length q) 1))

;; ———————————————————————- ;;

(defun quiz-choose-question ()
  (let ((n (random *quiz-number-of-questions*)))
    (setq quiz-current-question (cons n (nth n *quiz-questions*)))))

(defun quiz-display-question ()
  (let ((q (quiz-get-case (cdr quiz-current-question))))
    (insert (format “%s –> ?” q))
    (backward-char)))

(defun quiz-init-buffer ()
  (erase-buffer)
  (insert “(n)ext | (a)nswer | (q)uit\n\n”))

(defun quiz-next-question ()
  (interactive)
  (quiz-init-buffer)
  (quiz-choose-question)
  (quiz-display-question))

(defun quiz-parties-to-string (c)
  (let ((p (quiz-get-number-of-parties c))
        (claimant (quiz-get-claimant c)))
    (if (= p 1) (format “%s” claimant)
      (format “%s vs %s” claimant (quiz-get-defendant c)))))

(defun quiz-get-answer ()
  (interactive)
  (let* ((q (cdr quiz-current-question))
         (c (quiz-get-case q))
         (p (quiz-parties-to-string q)))
    (quiz-init-buffer)
    (insert (format “%s –> %s” c p))))

(defun quiz-quit ()
  (interactive)
  (when (y-or-n-p “Really quit? “)
    (save-buffers-kill-emacs)))

;; ———————————————————————- ;;

(defvar quiz-mode-map nil
  “Keymap for quiz major mode.”)

(define-derived-mode quiz-mode text-mode “Quiz”
  “Major mode for interacting with databases.
Special commands:
\\{quiz-mode-map}”)

(if quiz-mode-map
    nil
  (setq quiz-mode-map (make-sparse-keymap))
  (define-key quiz-mode-map “n” ‘quiz-next-question)
  (define-key quiz-mode-map “a” ‘quiz-get-answer)
  (define-key quiz-mode-map “q” ‘quiz-quit)
  (define-key quiz-mode-map [t] ‘undefined)
  (define-key quiz-mode-map “\M-x” ‘execute-extended-command))

(defun quiz-enter-quiz-mode ()
  (interactive)
  (switch-to-buffer “*quiz*” t)
  (random t)
  (quiz-mode)
  (quiz-next-question))

;; ———————————————————————- ;;

(quiz-enter-quiz-mode)

;; ———————————————————————- ;;

This is the batchfile to start quiz mode.

set EMACS_FILES=c:/packages/emacs-files
set EMACS_DIR=c:/packages/emacs-22.1

%EMACS_DIR%/bin/runemacs.exe -q --load %EMACS_FILES%/quiz.el

Emacs file uploader

Friday, May 23rd, 2008

I recently received a couple of emails from people saying something along the lines of muse, htmlize and some cobbled-together file uploading functions may be sufficient for a guy with a text-only blog such as myself. However, for a blog with images, emacs isn’t really a practical solution. They clearly don’t know about the excellent dired mode.

dired displays a directory listing within a buffer and allows you to easily perform all sorts of operations on those files or a selection of files including deleting, renaming, chmoding, compressing, performing regex replacements… well, you get the picture. It is a pretty cool file manager. Not only that, but it allows you to hook your own code into it.

If you recall I already have a file upload function (ftp-upload-file) defined from here. That being the case we can easily add a binding to call that function when we press a key in a dired buffer.

;; ———————————————————————- ;;

(defun ftp-dired-upload-file ()
  (interactive)
  (ftp-upload-file (expand-file-name (dired-get-file-for-visit))))

(define-key dired-mode-map “\C-c\C-u” ‘ftp-dired-upload-file)

;; ———————————————————————- ;;

I also added a keybinding to open my images directory. Who knows, maybe one day I’ll even use it :)

;; ———————————————————————- ;;

(defconst *ftp-images-directory* (concat *ftp-WEB* “blog/images”))

(defun ftp-dired-get-images-dir ()
  (interactive)
  (dired *ftp-images-directory*))

(global-set-key “\C-Ci” ‘ftp-dired-get-images-dir)

;; ———————————————————————- ;;

PHP, emacs-lisp and wrapping

Thursday, May 22nd, 2008

I’ve come to the conclusion (like many people before me no doubt) that there are two ways to make a blog popular. The first of these is to write smart things that your readers will learn from. The second of these is to write patently absurd claims1 and wait for the defenders of the truth to show up and assault your comments section. Obviously, the first of these options is far harder than the second.

One thing you can be sure of is Jeff "Coding Horror" Atwood will often provoke a good discussion. Fairly frequently this will be because of how wrong his posts are. For example, take his most recent post PHP sucks, but it doesn’t matter. Not since Giles Bowkett touted lack of debugger support as a feature can I remember such a ridiculous article. Yahoo and Facebook aren’t using PHP because it is available on all the cheap shared hosting platforms. They are using it because it solves some problems that other popular languages do not.

In many ways, PHP is in a similar boat to emacs-lisp. It isn’t the greatest language (although it isn’t that bad), but a whole bunch of things have been written for it/in it that no-one wants to write for themselves. In PHP’s case, that includes great database access and integration with apache. In emacs-lisp that includes emacs itself, org-mode, muse, tramp, ange-ftp, etc. etc.

Another thing that people seem to forget, is that once you have a way of making basic data structures and subroutines you can go a long way towards fixing many problems that a language has. E.g. if you don’t like the inconsistent naming of the subroutines in PHP, wrap them with your own better names. Make sure you follow a good example such as this one.

JG


1. Hey - maybe I should try that. Er, Vista is a great operating system! Vi is better than emacs!

Emacs, muse and htmlize

Tuesday, May 20th, 2008

It looks like I missed an interesting discussion on humane markup langages last week. Obviously I use emacs to write my blog posts with muse and htmlize. A tiny bit of Juan wrap makes it a pleasure. I wouldn’t write at all if I had to use raw HTML (although maybe it wouldn’t be so bad if I used nxml-mode).

I like the comments about normalizing HTML. Show me the code. Until there is something available I’ll stick to my regex based humane markup translator thank you very much. Of course I know that comments require a different level of trust than your own publishing system (unless for some reason it is not the case that you trust yourself but you don’t trust ‘them‘). However, you should still minimise the effort required or far fewer people will leave comments.

JG

Note: in the code below we could redefine muse-html-header and muse-html-footer to get the exact output that we want but in fact it is handy to have the html available so you can load it into a browser.

Update: I found that this guy said something similar to what I wanted to say, but he said it much more elegantly.

;; ———————————————————————- ;;

(requiremuse-mode)
(requiremuse-publish)
(requiremuse-html)

;; ———————————————————————- ;;

(defconst *muse-start* “Page published by Emacs Muse begins here”)
(defconst *muse-end* “Page published by Emacs Muse ends here”)

(defun my-muse-publish ()
  “Publish the current page as html and copy the html into the clipboard”
  (interactive)
  (let* ((cb (current-buffer))
         (filename (buffer-file-name))
         (extpos (string-match “.muse$” filename))
         (html-file (concat (substring filename 0 extpos) “.html”)))
    (muse-publish-file filename “html”)
    (let ((b (get-file-buffer html-file))
          (kill-buffer-query-functions nil))
      (when b (kill-buffer b)))
    (find-file html-file)

    (goto-char (point-min))
    (re-search-forward *muse-start*)
    (move-beginning-of-line 2)
    (let ((begin (point)))
      (re-search-forward *muse-end*)
      (move-beginning-of-line -1)
      (copy-region-as-kill begin (point)))

    (switch-to-buffer cb)
    (message “file published”)))

(define-key muse-mode-map [f7] ‘my-muse-publish)

;; ———————————————————————- ;;

(defun muse-minor-modes ()
  (longlines-mode 1)
  (font-lock-mode nil))

(add-hook ‘muse-mode-hook ‘muse-minor-modes)

;; ———————————————————————- ;;

(providemuse-config)

;; ———————————————————————- ;;

Reducing elisp bugs using the byte-code compiler

Thursday, April 17th, 2008

Did you know that you can compile elisp into byte-code? You did? Okay then, did you know that moderns emacs ship with the elisp compiler? Right, you knew that too? Well, in that case, do you compile your start-up configuration? If you don’t, this post could be for you.

First of all, what is the benefit of compiling? I can think of three.

  1. You might get a slight speed increase
  2. byte code loads faster than source code
  3. compilation picks up all sorts of errors

In my opinion, the first two points are not particularly important. The speed increase is extremely small and, even with the fairly large packages and customisation I have done, emacs starts up in a couple of sections without compiling everything to byte code. The real benefit [for me] is the error checking.

There is also a downside. Emacs always loads a byte code if it available, even if the source code is newer. This is pretty annoying and has bitten me on a few occasions when I have been wondering why emacs was not picking up my changes at start-up.

What kind of errors does the compiler find? Here are some of the errors from a recent compile.

In run-sql-command:
db.el:38:30:Warning: reference to free variable `*db-cmd*'

In toplevel form:
ftp-file-upload.el:16:1:Warning: defcustom for `*ftp-upload-skip-prompt*'
fails to specify containing group

In toplevel form:
htmlize.el:89:22:Warning: reference to free variable `unresolved'

In end of data:
htmlize.el:1770:1:Warning: the following functions are not known to be
defined: byte-compiler-options, warnings, htmlize-next-change,
htmlize-locate-file, specifier-spec-list, face-property,
color-instance-name, face-foreground-instance, face-background-instance,
color-instance-rgb-components, make-color-instance, face-font-instance,
font-instance-properties, face-strikethru-p, map-extents, extent-face,
extent-at, htmlize-faces-at-point, lazy-lock-fontify-region,
lazy-shot-fontify-region, dired-get-marked-files

In toplevel form:
my-ftp.el:27:7:Warning: assignment to free variable `*ftp-proc*'
my-ftp.el:29:68:Warning: reference to free variable `*ftp-proc*'

In end of data:
my-ftp.el:31:1:Warning: the function `ftp-mode' is not known to be defined.

In toplevel form:
shell-wrappers.el:87:13:Warning: reference to free variable `term-rawp-map'

In end of data:
shell-wrappers.el:98:1:Warning: the function `term-set-excape-char' is not
known to be defined.

In copy-files:
wordpress-loader.el:42:45:Warning: file-readable-p called with 0 arguments,
but requires 1

As you can see from the errors from htmlize, when you are writing elisp to be portable between xemacs and emacs, the compiler will emit spurious warnings. However, for someone like me, it is great. Look at those basic errors such misspelling term-set-excape-char and calling file-readable-p with no arguments. For dynamic languages, a decent lint style tool including the emacs byte code compiler, use strict in perl and pylint/pychecker/pyflakes can really help find a lot of bugs.

How do we set up the compiler? I prefer a batch-file that compiles everything that has changed.

set EMACS_FILES=c:/packages/emacs-files
set EMACS=c:\packages\emacs-22.1\bin\emacs.exe

%EMACS% --batch ^
--eval “(load \”%EMACS_FILES%/my-load-paths.el\”)” ^
–eval “(batch-byte-compile-if-not-done)” *.el

(That last eval is prefixed with two minuses but it looks like WordPress has changed it).

Emacs needs to know the paths where the modules live so that it can see if functions or variables are defined in files that have been required.

;; ———————————————————————- ;;

(load “c:/packages/emacs-files/my-vars.el”)

(add-to-list ‘load-path *elisp-dir*)
(add-to-list ‘load-path (expand-file-name “icicles”      *elisp-dir*))
(add-to-list ‘load-path (expand-file-name “muse-3.12″    *elisp-dir*))
(add-to-list ‘load-path (expand-file-name “org-5.19a”    *elisp-dir*))
(add-to-list ‘load-path (expand-file-name “remember-1.9″ *elisp-dir*))

(providemy-load-paths)

;; ———————————————————————- ;;

Finally, I like to have as few files as possible referring to the top level directory so the emacs variable pointing to the top level gets placed in its own file.

;; ———————————————————————- ;;

(defvar *elisp-dir* “c:/packages/emacs-files/”)

(providemy-vars)

;; ———————————————————————- ;;

JG

Emulating Unix find with elisp

Saturday, April 12th, 2008

One Unix tool I miss when I’m on Windows is find(1). Apropos didn’t show any equivalents despite some promising functions called locate.* Fortunately it is easy to implement a basic version of find that walks a filesystem subtree:

(defun find-files (dir fn)
  (dolist (f (file-expand-wildcards (concat dir “/*”)))
    (sit-for 0)
    (if (file-directory-p f)
        (find-files f fn)
      (funcall fn f))))

The elisp (file-expand-wildcards (concat dir "/*")) is actually pretty similar to ls $dir/* in shell.

For each file that is a directory, find-files recursively calls itself with that new directory name. Otherwise it calls the function fn on the file. So far so elegant. What does (sit-for 0) do? It forces a screen redisplay. Without that line the screen won’t update until the function completes and, due to its single-threaded nature, emacs will seem to have hung until everything has been processed. If there are lot of files or the processing we are doing is expensive then there could be a long wait without any feedback.

How can we use this new function? Here are a few examples:

(find-files “~/dev” ‘message)

This is a simple one. It outputs each file under ~/dev to the mini-buffer and the *Messages* buffer.

(defun time-convertor (secs)
  (cond ((<= secs 60) (format “%1.2f seconds” secs))
        ((<= secs 3600) (format “%1.2f minutes” (/ secs 60)))
        (t (format “%1.2f hours” (/ secs 3600)))))

(find-files
 “c:/packages/emacs-files”
 (lambda (f)
   (let ((now (time-to-seconds (current-time)))
         (three-days (* 3 24 3600)))
     (when (string-match “\\.elc?$” f)
       (setq f (file-chase-links f 10))
       (let* ((attribs (file-attributes f))
              (lm (nth 6 attribs))
              (last-modified (if lm (time-to-seconds lm) 0))
              (diff (- now last-modified)))
         (when (<= diff three-days)
           (insert (format “%s modified in last three days (%s ago)\n”
                           f (time-convertor diff)))))))))

Which gave the following output when I ran it:

c:/packages/emacs-files/cc-config.el modified in last three days (22.14 hours ago)
c:/packages/emacs-files/cc-style.el modified in last three days (20.19 hours ago)
c:/packages/emacs-files/ftp-file-upload.el modified in last three days (16.37 hours ago)
c:/packages/emacs-files/my-utils.el modified in last three days (17.77 hours ago)

What about this one then?

(find-files “~/wordpress” ‘ftp-upload-file)

Using the ftp-upload-file function we implemented previously we have a convenient method for uploading a whole directory up to your webserver. By default, it will prompt for every file. Here is the same thing with prompts temporarily disabled.

(let ((*ftp-upload-skip-prompt* t))
  (find-files “~/wordpress” ‘ftp-upload-file))

Yay for dynamic scoping!

If you wish to skip certain files by name or modified time then you can use string-match or file-attributes as above. Unfortunately, it doesn’t quite work as the directories need to have been already created on the webserver.

Q: How can we fix that?
A: Amend the find-files function to take an optional function parameter to process any directories we see.

(defun find-files (dir fn &optional dir-fn)
  (dolist (f (file-expand-wildcards (concat dir “/*”)))
    (sit-for 0)
    (if (not (file-directory-p f))
        (funcall fn f)
      (when dir-fn
        (funcall dir-fn f))
      (find-files f fn dir-fn))))

We also need to split the ftp-upload-file into a function that translates the local filepath to the remote path in addition to the part that uploads the file.

(defun ftp-translate-file-path (file)
  (dolist (tuple *ftp-location-map*)
    (let ((regex (car tuple))
          (replace (cadr tuple)))
      (when (string-match (concat “^” regex) file)
        (setq file (replace-match replace t t file)))))
  file)

(defun ftp-upload-file (from)
  (let ((to (ftp-translate-file-path from)))
    (if (or *ftp-upload-skip-prompt*
            (y-or-n-p (format “Query: Upload %s to %s ? “ from to)))
        (ftp-copy-file from to)
      (message “Info: upload %s to %s aborted” from to))))

Then add a function that creates a directory on the remote server for each directory we enter and we’re done. If you have any questions you can ask them below.

(let ((*ftp-upload-skip-prompt* t))
  (find-files “~/wordpress”
              ‘ftp-upload-file
              (lambda (dir)
                (ftp-mkdir (ftp-translate-file-path dir)))))

JG