comparison lisp/list-mode.el @ 209:41ff10fd062f r20-4b3

Import from CVS: tag r20-4b3
author cvs
date Mon, 13 Aug 2007 10:04:58 +0200
parents
children ca9a9ec9c1c1
comparison
equal deleted inserted replaced
208:f427b8ec4379 209:41ff10fd062f
1 ;;; list-mode.el --- Major mode for buffers containing lists of items
2
3 ;; Copyright (C) 1992-4, 1997 Free Software Foundation, Inc.
4 ;; Copyright (C) 1996 Ben Wing.
5
6 ;; Maintainer: XEmacs Development Team
7 ;; Keywords: extensions, dumped
8
9 ;; This file is part of XEmacs.
10
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Synched up with: Not synched
27
28 ;;; Commentary:
29
30 ;; This file is dumped with XEmacs.
31
32 ;; Cleanup, merging with FSF by Ben Wing, January 1996
33
34 ;;; Code:
35
36 (defvar list-mode-extent nil)
37 (make-variable-buffer-local 'list-mode-extent)
38
39 (defvar list-mode-map nil
40 "Local map for buffers containing lists of items.")
41 (or list-mode-map
42 (let ((map (setq list-mode-map (make-sparse-keymap 'list-mode-map))))
43 (suppress-keymap map)
44 (define-key map 'button2up 'list-mode-item-mouse-selected)
45 (define-key map 'button2 'undefined)
46 (define-key map "\C-m" 'list-mode-item-keyboard-selected)
47 (substitute-key-definition 'forward-char 'next-list-mode-item map
48 global-map)
49 (substitute-key-definition 'backward-char 'previous-list-mode-item map
50 global-map)))
51
52 (defun list-mode ()
53 "Major mode for buffer containing lists of items."
54 (interactive)
55 (kill-all-local-variables)
56 (use-local-map list-mode-map)
57 (setq mode-name "List")
58 (setq major-mode 'list-mode)
59 (make-local-hook 'post-command-hook)
60 (add-hook 'post-command-hook 'set-list-mode-extent nil t)
61 (make-local-hook 'pre-command-hook)
62 (add-hook 'pre-command-hook 'list-mode-extent-pre-hook nil t)
63 (make-local-variable 'next-line-add-newlines)
64 (setq next-line-add-newlines nil)
65 (setq list-mode-extent nil)
66 (set-specifier text-cursor-visible-p nil (current-buffer))
67 (setq buffer-read-only t)
68 (goto-char (point-min))
69 (run-hooks 'list-mode-hook))
70
71 ;; List mode is suitable only for specially formatted data.
72 (put 'list-mode 'mode-class 'special)
73
74 (defvar list-mode-extent-old-point nil
75 "The value of point when pre-command-hook is called.
76 Used to determine the direction of motion.")
77 (make-variable-buffer-local 'list-mode-extent-old-point)
78
79 (defun list-mode-extent-pre-hook ()
80 (setq list-mode-extent-old-point (point))
81 ;(setq atomic-extent-goto-char-p nil)
82 )
83
84 (defun set-list-mode-extent ()
85 "Move to the closest list item and set up the extent for it.
86 This is called from `post-command-hook'."
87 (cond ((get-char-property (point) 'list-mode-item))
88 ((and (> (point) (point-min))
89 (get-char-property (1- (point)) 'list-mode-item))
90 (goto-char (1- (point))))
91 (t
92 (let ((pos (point))
93 dirflag)
94 ;this fucks things up more than it helps.
95 ;atomic-extent-goto-char-p as currently defined is all broken,
96 ;since it will be triggered if the command *ever* runs goto-char!
97 ;(if atomic-extent-goto-char-p
98 ; (setq dirflag 1)
99 (if (and list-mode-extent-old-point
100 (> pos list-mode-extent-old-point))
101 (setq dirflag 1)
102 (setq dirflag -1))
103 (next-list-mode-item dirflag)
104 (or (get-char-property (point) 'list-mode-item)
105 (next-list-mode-item (- dirflag))))))
106 (or (and list-mode-extent
107 (eq (current-buffer) (extent-object list-mode-extent)))
108 (progn
109 (setq list-mode-extent (make-extent nil nil (current-buffer)))
110 (set-extent-face list-mode-extent 'list-mode-item-selected)))
111 (let ((ex (extent-at (point) nil 'list-mode-item nil 'at)))
112 (if ex
113 (progn
114 (set-extent-endpoints list-mode-extent
115 (extent-start-position ex)
116 (extent-end-position ex))
117 (auto-show-make-region-visible (extent-start-position ex)
118 (extent-end-position ex)))
119 (detach-extent list-mode-extent))))
120
121 (defun previous-list-mode-item (n)
122 "Move to the previous item in list-mode."
123 (interactive "p")
124 (next-list-mode-item (- n)))
125
126 (defun next-list-mode-item (n)
127 "Move to the next item in list-mode.
128 With prefix argument N, move N items (negative N means move backward)."
129 (interactive "p")
130 (while (and (> n 0) (not (eobp)))
131 (let ((prop (get-char-property (point) 'list-mode-item))
132 (end (point-max)))
133 ;; If in a completion, move to the end of it.
134 (if prop
135 (goto-char (next-single-property-change (point) 'list-mode-item
136 nil end)))
137 ;; Move to start of next one.
138 (goto-char (next-single-property-change (point)
139 'list-mode-item nil end)))
140 (setq n (1- n)))
141 (while (and (< n 0) (not (bobp)))
142 (let ((prop (get-char-property (1- (point)) 'list-mode-item))
143 (end (point-min)))
144 ;; If in a completion, move to the start of it.
145 (if prop
146 (goto-char (previous-single-property-change
147 (point) 'list-mode-item nil end)))
148 ;; Move to end of the previous completion.
149 (goto-char (previous-single-property-change (point) 'list-mode-item
150 nil end))
151 ;; Move to the start of that one.
152 (goto-char (previous-single-property-change (point) 'list-mode-item nil
153 end)))
154 (setq n (1+ n))))
155
156 (defun list-mode-item-selected-1 (extent event)
157 (let ((func (extent-property extent 'list-mode-item-activate-callback))
158 (user-data (extent-property extent 'list-mode-item-user-data)))
159 (if func
160 (funcall func event extent user-data))))
161
162 ;; we could make these two be just one function, but we want to be
163 ;; able to refer to them in DOC strings.
164
165 (defun list-mode-item-keyboard-selected ()
166 (interactive)
167 (list-mode-item-selected-1 (extent-at (point) (current-buffer)
168 'list-mode-item nil 'at)
169 nil))
170
171 (defun list-mode-item-mouse-selected (event)
172 (interactive "e")
173 ;; Sometimes event-closest-point returns nil.
174 ;; So beep instead of bombing.
175 (let ((point (event-closest-point event)))
176 (if point
177 (list-mode-item-selected-1 (extent-at point
178 (event-buffer event)
179 'list-mode-item nil 'at)
180 event)
181 (ding))))
182
183 (defun add-list-mode-item (start end &optional buffer activate-callback
184 user-data)
185 "Add a new list item in list-mode, from START to END in BUFFER.
186 BUFFER defaults to the current buffer.
187 This works by creating an extent for the span of text in question.
188 If ACTIVATE-CALLBACK is non-nil, it should be a function of three
189 arguments (EVENT EXTENT USER-DATA) that will be called when button2
190 is pressed on the extent. USER-DATA comes from the optional
191 USER-DATA argument."
192 (let ((extent (make-extent start end buffer)))
193 (set-extent-property extent 'list-mode-item t)
194 (set-extent-property extent 'start-open t)
195 (if activate-callback
196 (progn
197 (set-extent-property extent 'mouse-face 'highlight)
198 (set-extent-property extent 'list-mode-item-activate-callback
199 activate-callback)
200 (set-extent-property extent 'list-mode-item-user-data user-data)))
201 extent))
202
203
204 ;; Define the major mode for lists of completions.
205
206
207 (defvar completion-highlight-first-word-only nil
208 "*Completion will only highlight the first blank delimited word if t.
209 If the variable in not t or nil, the string is taken as a regexp to match for end
210 of highlight")
211
212 (defvar completion-setup-hook nil
213 "Normal hook run at the end of setting up the text of a completion buffer.")
214
215 ; Unnecessary FSFmacs crock. We frob the extents directly in
216 ; display-completion-list, so no "heuristics" like this are necessary.
217 ;(defvar completion-fixup-function nil
218 ; "A function to customize how completions are identified in completion lists.
219 ;`completion-setup-function' calls this function with no arguments
220 ;each time it has found what it thinks is one completion.
221 ;Point is at the end of the completion in the completion list buffer.
222 ;If this function moves point, it can alter the end of that completion.")
223
224 (defvar completion-default-help-string
225 '(concat
226 (if (device-on-window-system-p)
227 (substitute-command-keys
228 "Click \\<list-mode-map>\\[list-mode-item-mouse-selected] on a completion to select it.\n") "")
229 (substitute-command-keys
230 "Type \\<minibuffer-local-completion-map>\\[advertised-switch-to-completions] or \\[switch-to-completions] to move to this buffer, for keyboard selection.\n\n"))
231 "Form the evaluate to get a help string for completion lists.
232 This string is inserted at the beginning of the buffer.
233 See `display-completion-list'.")
234
235 (defun display-completion-list (completions &rest cl-keys)
236 "Display the list of completions, COMPLETIONS, using `standard-output'.
237 Each element may be just a symbol or string or may be a list of two
238 strings to be printed as if concatenated.
239 Frob a mousable extent onto each completion. This extent has properties
240 'mouse-face (so it highlights when the mouse passes over it) and
241 'list-mode-item (so it can be located).
242
243 Keywords:
244 :activate-callback (default is `default-choose-completion')
245 See `add-list-mode-item'.
246 :user-data
247 Value passed to activation callback.
248 :window-width
249 If non-nil, width to use in displaying the list, instead of the
250 actual window's width.
251 :help-string (default is the value of `completion-default-help-string')
252 Form to evaluate to get a string to insert at the beginning of
253 the completion list buffer. This is evaluated when that buffer
254 is the current buffer and after it has been put into
255 completion-list-mode.
256 :reference-buffer (default is the current buffer)
257 This specifies the value of `completion-reference-buffer' in
258 the completion buffer. This specifies the buffer (normally a
259 minibuffer) that `default-choose-completion' will insert the
260 completion into.
261
262 At the end, run the normal hook `completion-setup-hook'.
263 It can find the completion buffer in `standard-output'.
264 If `completion-highlight-first-word-only' is non-nil, then only the start
265 of the string is highlighted."
266 ;; #### I18N3 should set standard-output to be (temporarily)
267 ;; output-translating.
268 (cl-parsing-keywords
269 ((:activate-callback 'default-choose-completion)
270 :user-data
271 :reference-buffer
272 (:help-string completion-default-help-string)
273 :window-width)
274 ()
275 (let ((old-buffer (current-buffer))
276 (bufferp (bufferp standard-output)))
277 (if bufferp
278 (set-buffer standard-output))
279 (if (null completions)
280 (princ (gettext
281 "There are no possible completions of what you have typed."))
282 (let ((win-width
283 (or cl-window-width
284 (if bufferp
285 ;; This needs fixing for the case of windows
286 ;; that aren't the same width's the frame.
287 ;; Sadly, the window it will appear in is not known
288 ;; until after the text has been made.
289
290 ;; We have to use last-nonminibuf-frame here
291 ;; and not selected-frame because if a
292 ;; minibuffer-only frame is being used it will
293 ;; be the selected-frame at the point this is
294 ;; run. We keep the selected-frame call around
295 ;; just in case.
296 (frame-width (or (last-nonminibuf-frame)
297 (selected-frame)))
298 80))))
299 (let ((count 0)
300 (max-width 0))
301 ;; Find longest completion
302 (let ((tail completions))
303 (while tail
304 (let* ((elt (car tail))
305 (len (cond ((stringp elt)
306 (length elt))
307 ((and (consp elt)
308 (stringp (car elt))
309 (stringp (car (cdr elt))))
310 (+ (length (car elt))
311 (length (car (cdr elt)))))
312 (t
313 (signal 'wrong-type-argument
314 (list 'stringp elt))))))
315 (if (> len max-width)
316 (setq max-width len))
317 (setq count (1+ count)
318 tail (cdr tail)))))
319
320 (setq max-width (+ 2 max-width)) ; at least two chars between cols
321 (let ((rows (let ((cols (min (/ win-width max-width) count)))
322 (if (<= cols 1)
323 count
324 (progn
325 ;; re-space the columns
326 (setq max-width (/ win-width cols))
327 (if (/= (% count cols) 0) ; want ceiling...
328 (1+ (/ count cols))
329 (/ count cols)))))))
330 (princ (gettext "Possible completions are:"))
331 (let ((tail completions)
332 (r 0)
333 (regexp-string
334 (if (eq t
335 completion-highlight-first-word-only)
336 "[ \t]"
337 completion-highlight-first-word-only)))
338 (while (< r rows)
339 (terpri)
340 (let ((indent 0)
341 (column 0)
342 (tail2 tail))
343 (while tail2
344 (let ((elt (car tail2)))
345 (if (/= indent 0)
346 (if bufferp
347 (indent-to indent 2)
348 (while (progn (write-char ?\ )
349 (setq column (1+ column))
350 (< column indent)))))
351 (setq indent (+ indent max-width))
352 (let ((start (point))
353 end)
354 ;; Frob some mousable extents in there too!
355 (if (consp elt)
356 (progn
357 (princ (car elt))
358 (princ (car (cdr elt)))
359 (or bufferp
360 (setq column
361 (+ column
362 (length (car elt))
363 (length (car (cdr elt)))))))
364 (progn
365 (princ elt)
366 (or bufferp
367 (setq column (+ column (length
368 elt))))))
369 (add-list-mode-item
370 start
371 (progn
372 (setq end (point))
373 (or
374 (and completion-highlight-first-word-only
375 (goto-char start)
376 (re-search-forward regexp-string end t)
377 (match-beginning 0))
378 end))
379 nil cl-activate-callback cl-user-data)
380 (goto-char end)))
381 (setq tail2 (nthcdr rows tail2)))
382 (setq tail (cdr tail)
383 r (1+ r)))))))))
384 (if bufferp
385 (set-buffer old-buffer)))
386 (save-excursion
387 (let ((mainbuf (or cl-reference-buffer (current-buffer))))
388 (set-buffer standard-output)
389 (completion-list-mode)
390 (make-local-variable 'completion-reference-buffer)
391 (setq completion-reference-buffer mainbuf)
392 ;;; The value 0 is right in most cases, but not for file name completion.
393 ;;; so this has to be turned off.
394 ;;; (setq completion-base-size 0)
395 (goto-char (point-min))
396 (let ((buffer-read-only nil))
397 (insert (eval cl-help-string)))
398 ;; unnecessary FSFmacs crock
399 ;;(forward-line 1)
400 ;;(while (re-search-forward "[^ \t\n]+\\( [^ \t\n]+\\)*" nil t)
401 ;; (let ((beg (match-beginning 0))
402 ;; (end (point)))
403 ;; (if completion-fixup-function
404 ;; (funcall completion-fixup-function))
405 ;; (put-text-property beg (point) 'mouse-face 'highlight)
406 ;; (put-text-property beg (point) 'list-mode-item t)
407 ;; (goto-char end)))))
408 ))
409 (run-hooks 'completion-setup-hook)))
410
411 (defvar completion-display-completion-list-function 'display-completion-list
412 "Function to set up the list of completions in the completion buffer.
413 The function is called with one argument, the sorted list of completions.
414 Particular minibuffer interface functions (e.g. `read-file-name') may
415 want to change this. To do that, set a local value for this variable
416 in the minibuffer; that ensures that other minibuffer invocations will
417 not be affected.")
418
419 (defun minibuffer-completion-help ()
420 "Display a list of possible completions of the current minibuffer contents.
421 The list of completions is determined by calling `all-completions',
422 passing it the current minibuffer contents, the value of
423 `minibuffer-completion-table', and the value of
424 `minibuffer-completion-predicate'. The list is displayed by calling
425 the value of `completion-display-completion-list-function' on the sorted
426 list of completions, with the standard output set to the completion
427 buffer."
428 (interactive)
429 (message "Making completion list...")
430 (let ((completions (all-completions (buffer-string)
431 minibuffer-completion-table
432 minibuffer-completion-predicate)))
433 (message nil)
434 (if (null completions)
435 (progn
436 (ding nil 'no-completion)
437 (temp-minibuffer-message " [No completions]"))
438 (with-output-to-temp-buffer "*Completions*"
439 (funcall completion-display-completion-list-function
440 (sort completions #'string-lessp))))))
441
442 (define-derived-mode completion-list-mode list-mode
443 "Completion List"
444 "Major mode for buffers showing lists of possible completions.
445 Type \\<completion-list-mode-map>\\[choose-completion] in the completion list\
446 to select the completion near point.
447 Use \\<completion-list-mode-map>\\[mouse-choose-completion] to select one\
448 with the mouse."
449 (make-local-variable 'completion-base-size)
450 (setq completion-base-size nil))
451
452 (let ((map completion-list-mode-map))
453 (define-key map "\e\e\e" 'delete-completion-window)
454 (define-key map "\C-g" 'minibuffer-keyboard-quit)
455 (define-key map "q" 'abort-recursive-edit)
456 (define-key map " " (lambda () (interactive)
457 (select-window (minibuffer-window))))
458 (define-key map "\t" (lambda () (interactive)
459 (select-window (minibuffer-window)))))
460
461 (defvar completion-reference-buffer nil
462 "Record the buffer that was current when the completion list was requested.
463 This is a local variable in the completion list buffer.
464 Initial value is nil to avoid some compiler warnings.")
465
466 (defvar completion-base-size nil
467 "Number of chars at beginning of minibuffer not involved in completion.
468 This is a local variable in the completion list buffer
469 but it talks about the buffer in `completion-reference-buffer'.
470 If this is nil, it means to compare text to determine which part
471 of the tail end of the buffer's text is involved in completion.")
472
473 (defun delete-completion-window ()
474 "Delete the completion list window.
475 Go to the window from which completion was requested."
476 (interactive)
477 (let ((buf completion-reference-buffer))
478 (delete-window (selected-window))
479 (if (get-buffer-window buf)
480 (select-window (get-buffer-window buf)))))
481
482 (defun completion-do-in-minibuffer ()
483 (interactive "_")
484 (save-excursion
485 (set-buffer (window-buffer (minibuffer-window)))
486 (call-interactively (key-binding (this-command-keys)))))
487
488 (defun default-choose-completion (event extent buffer)
489 "Click on an alternative in the `*Completions*' buffer to choose it."
490 (and (button-event-p event)
491 ;; Give temporary modes such as isearch a chance to turn off.
492 (run-hooks 'mouse-leave-buffer-hook))
493 (or buffer (setq buffer (symbol-value-in-buffer
494 'completion-reference-buffer
495 (or (and (button-event-p event)
496 (event-buffer event))
497 (current-buffer)))))
498 (save-selected-window
499 (and (button-event-p event)
500 (select-window (event-window event)))
501 (if (and (one-window-p t 'selected-frame)
502 (window-dedicated-p (selected-window)))
503 ;; This is a special buffer's frame
504 (iconify-frame (selected-frame))
505 (or (window-dedicated-p (selected-window))
506 (bury-buffer))))
507 (choose-completion-string (extent-string extent)
508 buffer
509 completion-base-size))
510
511 ;; Delete the longest partial match for STRING
512 ;; that can be found before POINT.
513 (defun choose-completion-delete-max-match (string)
514 (let ((len (min (length string)
515 (- (point) (point-min)))))
516 (goto-char (- (point) (length string)))
517 (if completion-ignore-case
518 (setq string (downcase string)))
519 (while (and (> len 0)
520 (let ((tail (buffer-substring (point)
521 (+ (point) len))))
522 (if completion-ignore-case
523 (setq tail (downcase tail)))
524 (not (string= tail (substring string 0 len)))))
525 (setq len (1- len))
526 (forward-char 1))
527 (delete-char len)))
528
529 ;; Switch to BUFFER and insert the completion choice CHOICE.
530 ;; BASE-SIZE, if non-nil, says how many characters of BUFFER's text
531 ;; to keep. If it is nil, use choose-completion-delete-max-match instead.
532 (defun choose-completion-string (choice &optional buffer base-size)
533 (let ((buffer (or buffer completion-reference-buffer)))
534 ;; If BUFFER is a minibuffer, barf unless it's the currently
535 ;; active minibuffer.
536 (if (and (string-match "\\` \\*Minibuf-[0-9]+\\*\\'" (buffer-name buffer))
537 (or (not (active-minibuffer-window))
538 (not (equal buffer
539 (window-buffer (active-minibuffer-window))))))
540 (error "Minibuffer is not active for completion")
541 ;; Insert the completion into the buffer where completion was requested.
542 (set-buffer buffer)
543 (if base-size
544 (delete-region (+ base-size (point-min)) (point))
545 (choose-completion-delete-max-match choice))
546 (insert choice)
547 (remove-text-properties (- (point) (length choice)) (point)
548 '(highlight nil))
549 ;; Update point in the window that BUFFER is showing in.
550 (let ((window (get-buffer-window buffer t)))
551 (set-window-point window (point)))
552 ;; If completing for the minibuffer, exit it with this choice.
553 (and (equal buffer (window-buffer (minibuffer-window)))
554 minibuffer-completion-table
555 (exit-minibuffer)))))
556
557 (define-key minibuffer-local-completion-map [prior]
558 'switch-to-completions)
559 (define-key minibuffer-local-must-match-map [prior]
560 'switch-to-completions)
561 (define-key minibuffer-local-completion-map "\M-v"
562 'advertised-switch-to-completions)
563 (define-key minibuffer-local-must-match-map "\M-v"
564 'advertised-switch-to-completions)
565
566 (defalias 'advertised-switch-to-completions 'switch-to-completions)
567 (defun switch-to-completions ()
568 "Select the completion list window."
569 (interactive)
570 ;; Make sure we have a completions window.
571 (or (get-buffer-window "*Completions*")
572 (minibuffer-completion-help))
573 (if (not (get-buffer-window "*Completions*"))
574 nil
575 (select-window (get-buffer-window "*Completions*"))
576 (goto-char (next-single-property-change (point-min) 'list-mode-item nil
577 (point-max)))))
578
579 ;;; list-mode.el ends here