Begin main content

Apple Museum

After a rejig of my office, there is finally room for the Apple museum. More pieces to come!

Apple Museum

Your eyes don't deceive you, that is a 5 1/4 inch drive - the processor direct slot has an Apple // card in it (which was designed for the LC, which my parents bought it with, but it works great in the Colour Classic).

09:10 PM, 22 Feb 2012 by Mark Aufflick Permalink | Comments (0)

Emacs Programmed Completion

Subtitle - how (completing-read) stole my afternoon.

I thought I'd post my experiences since completing-read is so poorly understood based on the forum and blog posts out there. When you write an interactive Elisp command, many will know that you can easily read a value from the user. Eg: this will ask you how you feel and log it:

(defun mood(mood)
"Logs your mood"
(interactive "sHow are you feeling? ")
(message mood))

But the string form of argument to interactive is just a shortcut. You can easily supply some code instead, with a static completion list:

(defun mood(mood)
"Logs your mood"
(interactive (list (completing-read "How are you feeling? " '("happy""sad")))
(insert arg))
(message mood))

If you want something more complex though, instead of supplying a completion list, you can supply a function and that function receives as one of it's arguments the string to be completed. But it's not as simple as that function just returning a list unfortunately, and that's where people come unstuck. Your function is now embedded in the guts of the Emacs completion mechanism and as such it has to answer a whole range of questions about completions. It is reasonably well documented (if a bit terse) in the Programmed Completion section of the Emacs manual. There is a second argument which contains an optional predicate function (I'm ignoring that), but the important bit is the third argument which effectively sets the mode in which your function is being called.

In fact, your function will usually be called multiple times for each completion tab press. Essentially, your function may be asked to provide the best completion string possible, if there string is an exact match, if there are no matches, or to provide a list of possible matches. There is one final thing that will be asked, and that is the substring your completion is for. So in the example you're about to see I am completing directories, so the completion being offered at any given time only applies to the part after the last forward slash.

An example is as good as a thousand or so words, so here's my interactive function cdsrc. Basically it's a convenience function to quickly change directory into one of my client projects, which may be one or two levels deep in my main company source directory. The hard work is done by cdsrc-completions and you can see in the (cond) at the end where it responds to the different modes discussed briefly above and in detail in Programmed Completion.

(setq cdsrc-completions-prefix "~/src/_Pumptheory/")

(defun safe-fill-common-string-prefix (s1 s2)
(if (and s1 s2)
(fill-common-string-prefix s1 s2) nil))

(defun cdsrc-completions (str pred mode)
(let ((dirname (replace-regexp-in-string "[^/]+$""" str))
(filename (replace-regexp-in-string "^.*/""" str))
(slashpos (or (string-match "/.*$" str) 0)))

(let ((completions (all-completions filename (delq nil
(mapcar (lambda (x)
(and (file-directory-p (concat cdsrc-completions-prefix dirname x))
(not (string-match "^\\." x))
(concat x "/")))
(directory-files (concat cdsrc-completions-prefix dirname)))))))

;; return differently based on what mode we were called in ;; see http://www.gnu.org/software/emacs/manual/html_node/elisp/Programmed-Completion.html
(cond
((not mode) (cond
((and (eq 0 (length completions)) (eq 0 (length filename))) 't);; in our case, only report exact match when no nested dirs left
((> (length completions) 1)
(concat dirname (or (reduce 'safe-fill-common-string-prefix completions) filename)))
((eq (length completions) 1)
(concat dirname (car completions)))
('t nil)))

((eq mode 't) completions)

((eq mode 'lambda) (member (concat filename "/") completions))

('t (cons (list 'boundaries slashpos) (length filename))));; let completion know our completions only apply after the last / )))

(defun cdsrc (path)
"Changes to a subdir of cdsrc-completions-prefix in the current active interactive shell buffer"
(interactive (list
(let ((completion-ignore-case 't))
(completing-read
(concat "Enter subdir of " cdsrc-completions-prefix ": ")
'cdsrc-completions))))
(let ((path2 (concat cdsrc-completions-prefix path)))
(cd path2)
(if (string-match mode-name "Shell")
(progn (end-of-buffer)
(comint-kill-input)
(comint-send-string (current-buffer) (concat "cd " path2))
(comint-send-input)))))

This is out of my emacs.d, specifically aufflick-shell-hooks.el.

05:36 AM, 21 Feb 2012 by Mark Aufflick Permalink | Comments (0)

Comments are broken

It seems that adding comments is broken after the server move. I'll get right on it...

Update: Comments are now fixed, both for registered and anonymous persons.

01:44 AM, 10 Feb 2012 by Mark Aufflick Permalink | Comments (1)

New Server

The server that used to host this site, and a few others, has finally had one of it's raid drives fail after over 10 years of use. That's like 100 in server years! At it's age it's time to put it to bed, so I've moved this site over to linode. I upgraded the underlying Postgres, OpenACS and AOLServer instances at the same time. There are a few styling issues that need fixing, but if you notice anything actually broken please let me know.

BTW, as well as the same hardware running for over 10 years, the same Linux installation (with security patches which eventually had to be backported after it stopped being supported) has been running that whole time. If I remember right, the maximum uptime was around 700 days - just short of two years. Unfortunately it had to be shut down then to move it to another power rail.

I felt a certain nostalgia poking around it's directory structure looking for anything I wanted to keep. It's probably the last server I've had with /usr/local/playground in a nod to russm. And certainly the last server I will ever run qmail on - it was so awesome, but the massively scalable queue based mail handling just doesn't work with today's spam probes.

07:09 AM, 09 Feb 2012 by Mark Aufflick Permalink | Comments (0)

XML

Blog Categories

software (41)
..cocoa (23)
  ..heads up 'tunes (5)
..ruby (6)
..lisp (4)
..perl (4)
..openacs (1)
mac (21)
embedded (2)
..microprocessor (2)
  ..avr (1)
electronics (3)
design (1)
photography (26)
..black and white (6)
..A day in Sydney (18)
..The Daily Shoot (6)
food (2)
Book Review (2)

Notifications

Icon of envelope Request notifications

Syndication Feed

XML

Recent Comments

  1. Mark Aufflick: Re: the go/Inbox go/Sent buttons
  2. Unregistered Visitor: How do make a button to jump to folder
  3. Unregistered Visitor: Note I've updated the gist
  4. Unregistered Visitor: umbrello is now an available port on macPorts
  5. Unregistered Visitor: Updated version on Github
  6. Unregistered Visitor: Modification request.
  7. Unregistered Visitor: Accents and labels with spaces
  8. Unregistered Visitor: Mel Kaye - additional info
  9. Unregistered Visitor: mmh
  10. Mark Aufflick: Thank you