1 ;;; vm-minibuf.el --- Minibuffer read functions for VM
3 ;; Copyright (C) 1993, 1994 Kyle E. Jones
4 ;; Copyright (C) 2003-2006 Robert Widhopf-Fenk
6 ;; This program is free software; you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation; either version 2 of the License, or
9 ;; (at your option) any later version.
11 ;; This program is distributed in the hope that it will be useful,
12 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;; GNU General Public License for more details.
16 ;; You should have received a copy of the GNU General Public License along
17 ;; with this program; if not, write to the Free Software Foundation, Inc.,
18 ;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 (defun vm-minibuffer-complete-word (&optional exiting)
24 (let ((opoint (point))
25 ;; In Emacs 21, during a minibuffer read the minibuffer
26 ;; contains the prompt as buffer text and that text is
27 ;; read only. So we can no longer assume that (point-min)
28 ;; is where the user-entered text starts and we must avoid
29 ;; modifying that prompt text. The value we want instead
30 ;; of (point-min) is (minibuffer-prompt-end).
31 (point-min (if (fboundp 'minibuffer-prompt-end)
32 (minibuffer-prompt-end)
34 (case-fold-search completion-ignore-case)
35 trimmed-c-list c-list beg end diff word word-prefix-regexp completion)
36 ;; find the beginning and end of the word we're trying to complete
37 (if (or (eobp) (memq (following-char) '(?\t ?\n ?\ )))
39 (skip-chars-backward " \t\n")
40 (and (not (eobp)) (forward-char))
42 (skip-chars-forward "^ \t\n")
44 ;; if there can't be multiple words in the input the beginning
45 ;; of the word must be at point-min.
46 (if (not vm-completion-auto-space)
48 (skip-chars-backward "^ \t\n")
51 ;; copy the word into a string
52 (setq word (buffer-substring beg end))
53 ;; trim the completion list down to just likely candidates
54 ;; then convert it to an alist.
55 (setq word-prefix-regexp (concat "^" (regexp-quote word))
56 trimmed-c-list (vm-delete-non-matching-strings
58 vm-minibuffer-completion-table)
59 trimmed-c-list (sort trimmed-c-list (function string-lessp))
60 trimmed-c-list (mapcar 'list trimmed-c-list)
61 c-list (mapcar 'list vm-minibuffer-completion-table))
62 ;; Try the word against the completion list.
64 (setq completion (try-completion word trimmed-c-list)))
65 ;; If completion is nil, figure out what prefix of the word would prefix
66 ;; something in the completion list... but only if the user is interested.
67 (if (and (null completion) vm-completion-auto-correct c-list)
69 (while (null (setq completion
70 (try-completion (substring word 0 i) c-list)))
72 (setq completion (substring word 0 i))))
73 ;; If completion is t, we had a perfect match already.
75 (cond (vm-completion-auto-space
80 (vm-minibuffer-completion-message "[Sole completion]"))))
81 ;; Compute the difference in length between the completion and the
82 ;; word. A negative difference means no match and the magnitude
83 ;; indicates the number of chars that need to be shaved off the end
84 ;; before a match will occur. A positive difference means a match
85 ;; occurred and the magnitude specifies the number of new chars that
86 ;; can be appended to the word as a completion.
88 ;; `completion' can be nil here, but the code works anyway because
89 ;; (length nil) still equals 0!
90 (setq diff (- (length completion) (length word)))
92 ;; We have some completion chars. Insert them.
94 (and completion (zerop diff) (not (string-equal completion word))))
96 (delete-char (- (length word)))
98 (if (and vm-completion-auto-space
99 (null (cdr trimmed-c-list)))
102 (vm-minibuffer-completion-message "[No completion available]"))
103 ;; The word prefixed more than one string, but we can't complete
104 ;; any further. Either give help or say "Ambiguous".
107 (cond ((> (length (car (car trimmed-c-list))) (length word))
108 (if (null completion-auto-help)
109 (vm-minibuffer-completion-message "[Ambiguous]")
110 (vm-minibuffer-show-completions (sort
114 ((not (eq last-command 'vm-minibuffer-complete-word))
115 (vm-minibuffer-completion-message
116 "[Complete, but not unique]"))
117 (vm-completion-auto-space
119 ;; The word didn't prefix anything... if vm-completion-auto-correct is
120 ;; non-nil strip the offending characters and try again.
121 (vm-completion-auto-correct
124 (vm-minibuffer-complete-word exiting))
125 ;; if we're not auto-correcting and we're doing
126 ;; multi-word, just let the user insert a space.
127 (vm-completion-auto-space
129 ;; completion utterly failed, tell the user so.
132 (vm-minibuffer-completion-message "[No match]")))))))
134 (defun vm-minibuffer-complete-word-and-exit ()
136 (vm-minibuffer-complete-word t)
139 (defun vm-minibuffer-completion-message (string &optional seconds)
140 "Briefly display STRING to the right of the current minibuffer input.
141 Optional second arg SECONDS specifies how long to keep the message visible;
142 the default is 2 seconds.
144 A keypress causes the immediate erasure of the STRING, and return of control
145 to the calling program."
146 (let (omax (inhibit-quit t))
148 (goto-char (point-max))
151 (sit-for (or seconds 2))
152 (delete-region omax (point-max))))
154 (defun vm-minibuffer-replace-word (word)
155 (goto-char (point-max))
156 (skip-chars-backward "^ \t\n")
157 (delete-region (point) (point-max))
160 (defun vm-minibuffer-show-completions (list)
161 "Display LIST in a multi-column listing in the \" *Completions*\" buffer.
162 LIST should be a list of strings."
164 (set-buffer (get-buffer-create " *Completions*"))
165 (setq buffer-read-only nil)
166 (use-local-map (make-sparse-keymap))
167 ;; ignore vm-mutable-* here. the user shouldn't mind
168 ;; because when they exit the minibuffer the windows will be
170 (display-buffer (current-buffer))
172 (insert "Possible completions are:\n")
173 (setq buffer-read-only t)
174 (vm-show-list list 'vm-minibuffer-replace-word
175 (list (current-local-map) minibuffer-local-map))
176 (goto-char (point-min))))
178 (defun vm-show-list (list &optional function keymaps)
179 "Display LIST in a multi-column listing in the current buffer at point.
180 The current buffer must be displayed in some window at the time
181 this function is called.
183 LIST should be a list of strings.
185 Optional second argument FUNCTION will be called if the mouse is
186 clicked on one of the strings in the current buffer. The string
187 clicked upon will be passed to FUNCTION as its sole argument.
189 Optional third argument KEYMAPS specifies a lists of keymaps
190 where the FUNCTION should be bound to the mouse clicks. By
191 default the local keymap of the current buffer is used."
192 (or keymaps (setq keymaps (and (current-local-map)
193 (list (current-local-map)))))
195 (let ((buffer-read-only nil)
197 tabs longest columns list-length q i w start command
199 (cond ((and function keymaps (vm-mouse-support-possible-p))
201 (list 'lambda '(e) '(interactive "e")
203 '((string (vm-mouse-get-mouse-track-string e)))
204 (list 'and 'string (list function 'string)))))
206 (setq keymap (car keymaps))
207 (cond ((vm-mouse-xemacs-mouse-p)
208 (define-key keymap 'button1 command)
209 (define-key keymap 'button2 command))
210 ((vm-mouse-fsfemacs-mouse-p)
211 (define-key keymap [down-mouse-1] 'ignore)
212 (define-key keymap [drag-mouse-1] 'ignore)
213 (define-key keymap [mouse-1] command)
214 (define-key keymap [drag-mouse-2] 'ignore)
215 (define-key keymap [down-mouse-2] 'ignore)
216 (define-key keymap [mouse-2] command)))
217 (setq keymaps (cdr keymaps)))))
218 (setq list (sort (copy-sequence list) (function string-lessp))
219 w (vm-get-buffer-window (current-buffer))
223 columns (1- (window-width w)))
225 (setq longest (max longest (length (car q)))
226 list-length (1+ list-length)
228 (setq tabs (/ list-length (/ columns (+ longest separation)))
230 (if (zerop (% list-length
231 (/ columns (+ longest separation))))
236 (setq q (nthcdr i list))
240 (and function (vm-mouse-set-mouse-track-highlight start (point)))
241 (insert-char ? (+ separation (- longest (length (car q)))))
242 (setq q (nthcdr tabs q)))
246 (defun vm-minibuffer-completion-help ()
248 (let ((opoint (point))
249 c-list beg end word word-prefix-regexp)
250 ;; find the beginning and end of the word we're trying to complete
251 (if (or (eobp) (memq (following-char) '(?\t ?\n ?\ )))
253 (skip-chars-backward " \t\n")
254 (and (not (eobp)) (forward-char))
256 (skip-chars-forward "^ \t\n")
258 (skip-chars-backward "^ \t\n")
261 ;; copy the word into a string
262 (setq word (buffer-substring beg end))
263 ;; trim the completion list down to just likely candidates
264 ;; then convert it to an alist.
265 (setq word-prefix-regexp (concat "^" (regexp-quote word))
266 c-list (vm-delete-non-matching-strings
268 vm-minibuffer-completion-table)
269 c-list (sort c-list (function string-lessp)))
271 (vm-minibuffer-show-completions c-list)
272 (vm-minibuffer-completion-message " [No match]"))))
274 (defun vm-keyboard-read-string (prompt completion-list &optional multi-word)
275 (let ((minibuffer-local-map (copy-keymap minibuffer-local-map))
276 (vm-completion-auto-space multi-word)
277 (vm-minibuffer-completion-table completion-list))
278 (define-key minibuffer-local-map "\t" 'vm-minibuffer-complete-word)
279 (define-key minibuffer-local-map " " 'vm-minibuffer-complete-word)
280 (define-key minibuffer-local-map "?" 'vm-minibuffer-completion-help)
282 (define-key minibuffer-local-map "\r"
283 'vm-minibuffer-complete-word-and-exit))
284 ;; evade the XEmacs dialog box, yeccch.
285 (let ((use-dialog-box nil))
286 (read-string prompt))))
288 (defvar last-nonmenu-event)
290 (defun vm-read-string (prompt completion-list &optional multi-word)
292 (if (consp (car completion-list))
293 (setq completion-list (nreverse (mapcar 'car completion-list))))
294 (if (and completion-list (vm-mouse-support-possible-here-p))
295 (cond ((and (vm-mouse-xemacs-mouse-p)
296 (or (button-press-event-p last-command-event)
297 (button-release-event-p last-command-event)
298 (menu-event-p last-command-event)))
299 (vm-mouse-read-string prompt completion-list multi-word))
300 ((and (vm-mouse-fsfemacs-mouse-p)
301 (listp last-nonmenu-event))
302 (vm-mouse-read-string prompt completion-list multi-word))
304 (vm-keyboard-read-string prompt completion-list multi-word)))
305 (vm-keyboard-read-string prompt completion-list multi-word)))
307 (defun vm-read-number (prompt)
311 (string-match "^[ \t]*-?[0-9]+" (setq result (read-string prompt)))))
312 (string-to-number result)))
314 (defun vm-keyboard-read-file-name (prompt &optional dir default
315 must-match initial history)
316 "Like read-file-name, except HISTORY's value is unaltered."
317 (let ((oldvalue (symbol-value history))
318 ;; evade the XEmacs dialog box, yeccch.
319 (use-dialog-box nil))
322 (read-file-name prompt dir default must-match initial history)
323 ((wrong-number-of-arguments void-function)
325 (let ((file-name-history (symbol-value history))
328 (read-file-name prompt dir default must-match initial))
330 (read-file-name prompt dir default must-match initial))))
331 (and history (set history oldvalue)))))
333 (defun vm-read-file-name (prompt &optional dir default
334 must-match initial history)
335 "Like read-file-name, except a mouse interface is used if a mouse
336 click mouse triggered the current command."
337 (if (vm-mouse-support-possible-here-p)
338 (cond ((and (vm-mouse-xemacs-mouse-p)
339 (or (button-press-event-p last-command-event)
340 (button-release-event-p last-command-event)
341 (menu-event-p last-command-event)))
342 (vm-mouse-read-file-name prompt dir default
343 must-match initial history))
344 ((and (vm-mouse-fsfemacs-mouse-p)
345 (listp last-nonmenu-event))
346 (vm-mouse-read-file-name prompt dir default
347 must-match initial history))
349 (vm-keyboard-read-file-name prompt dir default
350 must-match initial history)))
351 (vm-keyboard-read-file-name prompt dir default
352 must-match initial history)))
354 (defun vm-folder-list (&optional non-virtual)
356 (let ((buffers (buffer-list))
357 (modes (if non-virtual '(vm-mode) '(vm-mode vm-virtual-mode)))
360 (set-buffer (car buffers))
361 (if (member major-mode modes)
362 (setq folders (cons (buffer-name) folders)))
363 (setq buffers (cdr buffers)))
366 (defun vm-read-folder-name ()
369 (mapcar (lambda (f) (list f)) (vm-folder-list))
372 (provide 'vm-minibuf)
374 ;;; vm-minibuf.el ends here