Quoth Daniel Schoepe on Jun 04 at 9:55 pm: > On Sat, 4 Jun 2011 11:32:15 -0400, Austin Clements <amdragon@mit.edu> wrote: > > Dynamic scoping is obnoxious, but I think programmed completion is > > steeped in the assumption that you'll use it. This code would be much > > simpler if notmuch-query-completions took only `string' and used the > > dynamically-bound all-compls (which should probably be renamed > > notmuch-completions or something if you do this). Then this could be > > just > > (minibuffer-completion-table (completion-table-dynamic > > #'notmuch-query-completions))) > > and there'd be no need for quasiquoting, comments, and fake lexical scoping. > > Sounds reasonable, I guess I really should stop fighting all those ugly > parts of elisp with unreadable constructs like that. I made it a global > variable though to avoid compilation warnings about notmuch-completion > being a free variable. Since it's contents are not dependent on > how/where notmuch-read-query is called, this shouldn't cause any > problems, except my personal discomfort arising from the use of side > effects for something as simple as this. :) Oh, sorry, I wasn't suggesting setq'ing a global. I agree that that's really ugly. Rather, something like (defun notmuch-query-completions (string) ... as you have it now ...) (defun notmuch-read-query (prompt) (let ((notmuch-completions (append (list "folder:" ...))) (keymap ...) (minibuffer-completion-table ...)) (read-from-minibuffer ...))) Unfortunately, you still need the global defvar to avoid compilation warnings, but this at least avoids the side-effects, and is probably how programmed completion was intended to be used. Alternatively, here's a completely different way to structure this that avoids globals and dynamic scoping entirely. This is how some of the standard completing read functions appear to work: (defun notmuch-read-query (prompt) "Read a notmuch-query from the minibuffer with completion. PROMPT is the string to prompt with." (lexical-let ((completions (append (list "folder:" "thread:" "id:" "date:" "from:" "to:" "subject:" "attachment:") (mapcar (lambda (tag) (concat "tag:" tag)) (process-lines "notmuch" "search-tags"))))) (let ((minibuffer-completion-table (completion-table-dynamic (lambda (string) (cond ;; this ugly regexp is used to get the last word of the ;; input possibly preceded by a '(' ((string-match "\\(^\\|.* (?\\)\\([^ ]*\\)$" string) (mapcar (lambda (compl) (concat (match-string-no-properties 1 string) compl)) (all-completions (match-string-no-properties 2 string) completions))) (t (list string)))))) (keymap (copy-keymap minibuffer-local-map))) ;; this was simpler than convincing completing-read to accept spaces: (define-key keymap (kbd "<tab>") 'minibuffer-complete) (read-from-minibuffer prompt nil keymap nil minibuffer-history nil nil)))) > > > + (define-key keymap (kbd "<tab>") 'minibuffer-complete) > > > > This probably deserves a comment about why you're doing so much work > > to avoid completing-read (which I assume is because it also binds SPC, > > even if require-match is nil, which is unfortunate). > > Yes, that was the reason. > > Another thing that bugs me, is that I did not find a better way of doing > the completion: Ideally I'd like to just specify a list of completions > for individual words and have emacs handle separating the input string > into individual words, but I couldn't find any options to accomplish > that. Yeah, I futzed with it for a bit, swearing that there had to be a better way, but didn't find one either.