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
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
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)
Archive
| February 2012 | ||||||
| S | M | T | W | T | F | S |
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | |||
March 2012
February 2012
November 2011
October 2011
April 2011
March 2011
January 2011
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
March 2008
February 2008
January 2008
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
February 2004
January 2004
December 2003
November 2003
October 2003
September 2003
August 2003
Blog Categories
software (40)..cocoa (21)
..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
Request notifications







