1 ;;; mew-complete.el --- Completion magic for Mew
3 ;; Author: Kazu Yamamoto <Kazu@Mew.org>
4 ;; Created: May 30, 1997
5 ;; Revised: Aug 30, 1999
9 (defconst mew-complete-version "mew-complete.el version 0.04")
13 ;; hoping the functions here are free from marker problem
14 ;; because it inserts any chars before \n. It is important
15 ;; to use the position's color.
16 (fset 'mew-complete-insert (symbol-function 'insert))
18 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 ;;; Low level functions
23 (defun mew-draft-on-field-p ()
29 (if (looking-at ".*,[ \t]?$") nil t)))
33 (if (looking-at mew-lwsp)
35 (if (search-forward ":" pos t) nil t))))))
37 (defun mew-draft-on-value-p (switch)
40 (while (and (< (point-min) (point)) (looking-at mew-lwsp))
42 (if (looking-at "\\([^:]*:\\)")
43 (mew-assoc-match (mew-match 1) switch 0)
44 nil))) ;; what a case reachs here?
47 ;; Window management for completion candidates
50 (defvar mew-complete-candidates nil)
52 (defun mew-complete-window-delete ()
53 (if (null mew-complete-window-config)
55 ;; mew-complete-window-config remains when the last completion
56 ;; finished with multiple candidates.
57 ;; (e.g. foo<RET> when foo and foobar are displayed.)
58 ;; In this case, this function is called in another
59 ;; completion thread but setting window configuration is not
60 ;; desired. If we set window configuration with the old
61 ;; mew-complete-window-config, the cursor jumps to mini buffer.
62 ;; This was a stupid bug of Mew. So, let's see if the complete
63 ;; buffer is displayed or not.
64 (if (get-buffer-window mew-buffer-completions)
65 (set-window-configuration mew-complete-window-config))
66 (setq mew-complete-window-config nil))
67 (and (get-buffer mew-buffer-completions)
68 (kill-buffer mew-buffer-completions))
69 (setq mew-complete-candidates nil))
71 (defun mew-complete-window-show (all)
72 (or mew-complete-window-config
73 (setq mew-complete-window-config (current-window-configuration)))
74 (if (and (get-buffer-window mew-buffer-completions)
75 (equal mew-complete-candidates all))
76 (let ((win (get-buffer-window mew-buffer-completions)))
78 (set-buffer mew-buffer-completions)
79 (if (pos-visible-in-window-p (point-max) win)
80 (set-window-start win 1)
81 (scroll-other-window))))
82 (setq mew-complete-candidates all)
83 (with-output-to-temp-buffer
84 mew-buffer-completions
85 (display-completion-list all))))
87 (defun mew-complete-backscroll ()
88 "Backscroll the *Completion* buffer."
90 (let* ((win (get-buffer-window mew-buffer-completions))
91 (height (and win (window-height win))))
92 (and win (scroll-other-window (- 3 height)))))
94 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
96 ;;; Completion function: C-i
99 (defun mew-draft-header-comp ()
100 "Complete and expand address short names.
101 First, a short name is completed. When completed solely or the @ character
102 is inserted before the cursor, the short name is expanded to its address."
104 (if (mew-draft-on-field-p)
106 (let ((func (mew-draft-on-value-p mew-field-completion-switch)))
109 (tab-to-tab-stop))))) ;; default keybinding
111 (defun mew-complete-field ()
112 "Field complete function."
114 (let ((word (mew-delete-key))) ;; capitalized
116 (mew-complete-window-show mew-fields)
119 (mapcar (function (lambda (x) (list (concat (capitalize x) " "))))
124 (defun mew-complete-folder ()
125 "Folder complete function for Fcc:."
127 (let ((word (mew-delete-backward-char)))
130 (mew-complete-window-show (list "+" "=" "%"))
131 (mew-complete-window-show (list "+" "=")))
132 (mew-complete word mew-folder-alist "folder" nil))))
134 (defun mew-complete-address ()
135 "Complete and expand an address short name.
136 First alias key is completed. When completed solely or the @ character
137 is inserted before the cursor, the short name is expanded to its address."
139 (let ((word (mew-delete-backward-char)))
142 (if (string-match "@." word)
143 (mew-complete-insert (or (mew-alias-next word) word))
145 word mew-addrbook-alist "alias" ?@ nil nil
146 (function mew-addrbook-alias-get)
147 (function mew-addrbook-alias-hit))))))
149 (defun mew-complete-config ()
150 "Complete function for Config:."
152 (let ((word (or (mew-delete-value ",") "")))
155 (mew-slide-pair mew-config-list)
159 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
161 ;;; Circular completion: C-cC-i
164 (defun mew-draft-circular-comp ()
165 "Switch function for circular complete functions."
167 (let ((func (mew-draft-on-value-p mew-field-circular-completion-switch)))
170 (message "No circular completion here"))))
172 (defun mew-circular-complete-domain ()
173 "Circular completion of domains for To:, Cc:, etc.
174 If the @ character does not exist, the first value of
175 mew-mail-domain-list is inserted. If exists, the next value of
176 mew-mail-domain-list concerned with the string between @ and
177 the cursor is inserted."
179 (let ((word (mew-delete-backward-char "@"))
180 (completion-ignore-case t))
182 ((equal word nil) ;; @ doesn't exist.
183 (if (null mew-mail-domain-list)
184 (message "For domain circular completion, set mew-mail-domain-list")
185 (mew-complete-insert "@")
186 (mew-complete-insert (car mew-mail-domain-list))
187 (mew-complete-window-delete)))
188 ((equal word t) ;; just after @
189 (if (null mew-mail-domain-list)
190 (message "For domain circular completion, set mew-mail-domain-list")
191 (mew-complete-insert (car mew-mail-domain-list))
192 (mew-complete-window-delete)))
194 ;; can't use mew-get-next since completion is necessary sometime.
197 (mew-slide-pair mew-mail-domain-list)
202 (defun mew-circular-complete (msg clist cname &optional here)
203 "General circular complete function to call mew-complete."
205 (let ((str (mew-delete-value here)))
208 (mew-complete-insert (car clist))
209 (message "For circular completion, set %s" cname))
212 (mew-slide-pair clist)
216 (defun mew-circular-complete-from ()
217 "Circular complete function for From:."
219 (mew-circular-complete "from" mew-from-list "mew-from-list"))
221 (defun mew-circular-complete-config ()
222 "Circular complete function for Config:."
224 (mew-circular-complete "config" mew-config-list "mew-config-list" ","))
226 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
228 ;;; Expansion : M-C-i
231 (defun mew-draft-expand ()
232 "Switch function for expand functions."
234 (let ((func (mew-draft-on-value-p mew-field-expansion-switch)))
237 (message "No expansion here"))))
239 (defun mew-expand-address ()
240 "Address expansion function for To:, Cc:, etc.
241 'user@domain' will be expands 'name <user@domain>' if
244 (let ((word (mew-delete-backward-char)) func name)
246 (message "No address here")
247 (setq func (mew-addrbook-func mew-addrbook-for-address-expansion))
249 (mew-complete-insert word)
250 (setq name (funcall func word))
251 (mew-complete-insert (if name (format "%s <%s>" name word) word))))))
253 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
255 ;;; Other completion stuff
259 (defvar mew-ext-host "")
260 (defvar mew-ext-user "")
262 (defun mew-complete-rfile ()
263 "Complete a remote file."
265 (let* ((path-file (mew-delete-file))
266 (path (car path-file))
267 (file (cdr path-file))
269 (setq rpath (format "/%s@%s:%s" mew-ext-user mew-ext-host path))
275 (function mew-ext-file-name-completion)
276 (function mew-ext-file-name-all-completions))))
278 (defun mew-complete-pick-pattern ()
279 "Complete pick patterns."
281 (let* ((pat (mew-delete-pattern))
282 (clist (append '("(" "!")
284 (mapcar (function car) mew-pick-macro-alist))))
286 (mew-complete-window-show clist)
289 (mapcar (function list) clist)
293 (defun mew-complete-sort-key ()
294 "Complete sort keys."
296 (let* ((word (mew-delete-line))
298 (if (string-match ":" word)
300 ;; If WORD contains ':', change alist for completion.
301 (setq field (car (mew-split word ?:)))
303 (mapcar (function (lambda (str) (list (concat field ":" str))))
305 ;; Otherwise, alist is mew-sort-key-alist itself.
306 (setq alist mew-sort-key-alist))
307 (mew-complete word alist "sort key" nil)))
309 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
311 ;;; Hart function for completions
314 (fset 'mew-complete-hit (symbol-function 'assoc))
316 (defun mew-complete-get (key alist)
317 (cdr (mew-complete-hit key alist)))
319 (defun mew-complete (WORD ALIST MSG EXPAND-CHAR &optional TRY ALL GET HIT)
320 (let* ((ftry (or TRY (function try-completion)))
321 (fall (or ALL (function all-completions)))
322 (fget (or GET (function mew-complete-get)))
323 (fhit (or HIT (function mew-complete-hit)))
324 (cmp (funcall ftry WORD ALIST))
325 (all (funcall fall WORD ALIST))
331 (if EXPAND-CHAR ;; may be "t"
332 (mew-complete-insert (funcall fget WORD ALIST)) ;; use cdr
333 (mew-complete-insert WORD)) ;; use car
334 (mew-complete-window-delete))
336 ((and (mew-characterp EXPAND-CHAR)
337 (char-equal (aref WORD (1- len)) EXPAND-CHAR)
338 (setq subkey (substring WORD 0 (1- len)))
339 (funcall fhit subkey ALIST))
340 (mew-complete-insert (funcall fget subkey ALIST)) ;; use cdr
341 (mew-complete-window-delete))
342 ;; just one candidate
343 ((equal 1 (length all))
344 (mew-complete-insert cmp)
345 (mew-complete-window-delete)
346 (if (window-minibuffer-p (get-buffer-window (current-buffer)))
347 (mew-temp-minibuffer-message " [Sole completion]")
348 (message "Sole completion")))
349 ;; two or more candidates
350 ((stringp cmp) ;; (length all) > 1
351 (mew-complete-insert cmp)
352 (mew-complete-window-show all)
353 (if (and EXPAND-CHAR (funcall fhit cmp ALIST))
355 (substitute-command-keys
356 "To expand '%s', type '%c' then '\\<mew-draft-header-map>\\[mew-draft-header-comp]'.")
360 (mew-complete-insert WORD)
361 ;;(mew-complete-window-delete)
362 (if (window-minibuffer-p (get-buffer-window (current-buffer)))
363 (mew-temp-minibuffer-message (concat " No matching " MSG))
364 (message "No matching %s" MSG))))))
366 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
371 (defun mew-temp-minibuffer-message (m)
372 (let ((savemax (point-max)))
374 (goto-char (point-max))
375 (mew-complete-insert m))
376 (let ((inhibit-quit t))
378 (delete-region savemax (point-max))
379 (if quit-flag (setq quit-flag nil unread-command-events 7)))))
382 ;; Extracting completion key
385 (defun mew-delete-backward-char (&optional here)
386 "Delete appropriate preceding word and return it."
388 (let ((case-fold-search t)
391 (regex (concat "[^" mew-address-separator "]")))
393 (while (and (not (bobp))
394 (string-match regex (mew-buffer-substring
395 (1- (point)) (point))))
397 (if (and here (not (re-search-forward (regexp-quote here) end t)))
398 nil ;; "here" doesn't exist.
401 (if here t nil) ;; just after "here", just after separator
403 (mew-buffer-substring start end)
404 (delete-region start end)))))))
406 (defun mew-delete-file ()
407 (if (search-backward mew-path-separator nil t)
411 (cons (mew-buffer-substring (point-min) (point))
412 (mew-buffer-substring (point) (point-max)))
413 (delete-region (point) (point-max))))
415 (defun mew-delete-pattern ()
417 (if (re-search-backward " \\|(\\|&\\||\\|!" nil t)
421 (mew-buffer-substring (point) pos)
422 (delete-region (point) pos))))
424 (defun mew-delete-line ()
428 (mew-buffer-substring (point) pos)
429 (delete-region (point) pos))))
431 (defun mew-delete-key ()
435 (capitalize (mew-buffer-substring (point) pos))
436 (delete-region (point) pos))))
438 (defun mew-delete-value (&optional here)
440 (if (not (looking-at "[^:]+:"))
442 (goto-char (match-end 0))
443 (if (looking-at "[ \t]")
445 (mew-complete-insert " "))
448 (let ((start (point)) ret)
450 (if (and here (re-search-backward (regexp-quote here) start t))
452 (setq start (1+ (point)))
454 (setq ret (mew-buffer-substring start (point)))
455 (delete-region start (point))
462 (defun mew-slide-pair (x)
467 ((eq x 1) (cons first first))
470 (setq ret (cons (cons (nth 0 x) (nth 1 x)) ret))
472 (setq ret (cons (cons (car x) first) ret))
475 (provide 'mew-complete)
477 ;;; Copyright Notice:
479 ;; Copyright (C) 1997, 1998, 1999 Mew developing team.
480 ;; All rights reserved.
482 ;; Redistribution and use in source and binary forms, with or without
483 ;; modification, are permitted provided that the following conditions
486 ;; 1. Redistributions of source code must retain the above copyright
487 ;; notice, this list of conditions and the following disclaimer.
488 ;; 2. Redistributions in binary form must reproduce the above copyright
489 ;; notice, this list of conditions and the following disclaimer in the
490 ;; documentation and/or other materials provided with the distribution.
491 ;; 3. Neither the name of the team nor the names of its contributors
492 ;; may be used to endorse or promote products derived from this software
493 ;; without specific prior written permission.
495 ;; THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
496 ;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
497 ;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
498 ;; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
499 ;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
500 ;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
501 ;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
502 ;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
503 ;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
504 ;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
505 ;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
507 ;;; mew-complete.el ends here