comparison lisp/prim/cmdloop.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; cmdloop.el --- support functions for the top-level command loop.
2
3 ;; Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
4
5 ;; This file is part of XEmacs.
6
7 ;; XEmacs is free software; you can redistribute it and/or modify it
8 ;; under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation; either version 2, or (at your option)
10 ;; any later version.
11
12 ;; XEmacs is distributed in the hope that it will be useful, but
13 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ;; General Public License for more details.
16
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with XEmacs; see the file COPYING. If not, write to the Free
19 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 ;;; Synched up with: FSF 19.30. (Some of the stuff below is in FSF's subr.el.)
22
23 ;; Written by Richard Mlynarik 8-Jul-92
24
25 (defun recursion-depth ()
26 "Return the current depth in recursive edits."
27 (+ command-loop-level (minibuffer-depth)))
28
29 (defun top-level ()
30 "Exit all recursive editing levels."
31 (interactive)
32 (throw 'top-level nil))
33
34 (defun exit-recursive-edit ()
35 "Exit from the innermost recursive edit or minibuffer."
36 (interactive)
37 (if (> (recursion-depth) 0)
38 (throw 'exit nil))
39 (error "No recursive edit is in progress"))
40
41 (defun abort-recursive-edit ()
42 "Abort the command that requested this recursive edit or minibuffer input."
43 (interactive)
44 (if (> (recursion-depth) 0)
45 (throw 'exit t))
46 (error "No recursive edit is in progress"))
47
48 ;; (defun keyboard-quit ()
49 ;; "Signal a `quit' condition."
50 ;; (interactive)
51 ;; (deactivate-mark)
52 ;; (signal 'quit nil))
53
54 ;; moved here from pending-del.
55 (defun keyboard-quit ()
56 "Signal a `quit' condition.
57 If this character is typed while lisp code is executing, it will be treated
58 as an interrupt.
59 If this character is typed at top-level, this simply beeps.
60 If `zmacs-regions' is true, and the zmacs region is active, then this
61 key deactivates the region without beeping or signalling."
62 (interactive)
63 (if (and zmacs-regions (zmacs-deactivate-region))
64 ;; pseudo-zmacs compatibility: don't beep if this ^G is simply
65 ;; deactivating the region. If it is inactive, beep.
66 nil
67 (signal 'quit nil)))
68
69 (defvar buffer-quit-function nil
70 "Function to call to \"quit\" the current buffer, or nil if none.
71 \\[keyboard-escape-quit] calls this function when its more local actions
72 \(such as cancelling a prefix argument, minibuffer or region) do not apply.")
73
74 (defun keyboard-escape-quit ()
75 "Exit the current \"mode\" (in a generalized sense of the word).
76 This command can exit an interactive command such as `query-replace',
77 can clear out a prefix argument or a region,
78 can get out of the minibuffer or other recursive edit,
79 cancel the use of the current buffer (for special-purpose buffers),
80 or go back to just one window (by deleting all but the selected window)."
81 (interactive)
82 (cond ((eq last-command 'mode-exited) nil)
83 ((> (minibuffer-depth) 0)
84 (abort-recursive-edit))
85 (current-prefix-arg
86 nil)
87 ((region-active-p)
88 (zmacs-deactivate-region))
89 (buffer-quit-function
90 (funcall buffer-quit-function))
91 ((not (one-window-p t))
92 (delete-other-windows))))
93
94 ;;#### This should really be a ring of last errors.
95 (defvar last-error nil
96 "#### Document me.")
97
98 (defun command-error (error-object)
99 (let ((inhibit-quit t)
100 (debug-on-error nil)
101 (etype (car-safe error-object)))
102 (setq quit-flag nil)
103 (setq standard-output t)
104 (setq standard-input t)
105 (setq executing-kbd-macro nil)
106 (zmacs-deactivate-region)
107 (discard-input)
108
109 (setq last-error error-object)
110
111 (message nil)
112 (ding nil (cond ((eq etype 'undefined-keystroke-sequence)
113 (if (and (vectorp (nth 1 error-object))
114 (/= 0 (length (nth 1 error-object)))
115 (button-event-p (aref (nth 1 error-object) 0)))
116 'undefined-click
117 'undefined-key))
118 ((eq etype 'quit)
119 'quit)
120 ((memq etype '(end-of-buffer beginning-of-buffer))
121 'buffer-bound)
122 ((eq etype 'buffer-read-only)
123 'read-only)
124 (t 'command-error)))
125 (display-error error-object t)
126
127 (if (noninteractive)
128 (progn
129 (message "XEmacs exiting.")
130 (kill-emacs -1)))
131 t))
132
133 (defun describe-last-error ()
134 "Redisplay the last error-message. See the variable `last-error'."
135 (interactive)
136 (with-displaying-help-buffer
137 (princ "Last error was:\n" standard-output)
138 (display-error last-error standard-output)))
139
140
141 ;;#### Must be done later in the loadup sequence
142 ;(define-key (symbol-function 'help-command) "e" 'describe-last-error)
143
144
145 (defun truncate-command-history-for-gc ()
146 (let ((tail (nthcdr 30 command-history)))
147 (if tail (setcdr tail nil)))
148 (let ((tail (nthcdr 30 values)))
149 (if tail (setcdr tail nil)))
150 )
151
152 (add-hook 'pre-gc-hook 'truncate-command-history-for-gc)
153
154
155 ;;;; Object-oriented programming at its finest
156
157 (defun display-error (error-object stream) ;(defgeneric report-condition ...)
158 "Display `error-object' on `stream' in a user-friendly way."
159 (funcall (or (let ((type (car-safe error-object)))
160 (catch 'error
161 (and (consp error-object)
162 (symbolp type)
163 ;;(stringp (get type 'error-message))
164 (consp (get type 'error-conditions))
165 (let ((tail (cdr error-object)))
166 (while (not (null tail))
167 (if (consp tail)
168 (setq tail (cdr tail))
169 (throw 'error nil)))
170 t)
171 ;; (check-type condition condition)
172 (get type 'error-conditions)
173 ;; Search class hierarchy
174 (let ((tail (get type 'error-conditions)))
175 (while (not (null tail))
176 (cond ((not (and (consp tail)
177 (symbolp (car tail))))
178 (throw 'error nil))
179 ((get (car tail) 'display-error)
180 (throw 'error (get (car tail)
181 'display-error)))
182 (t
183 (setq tail (cdr tail)))))
184 ;; Default method
185 #'(lambda (error-object stream)
186 (let ((type (car error-object))
187 (tail (cdr error-object))
188 (first t)
189 (print-message-label 'error))
190 (if (eq type 'error)
191 (progn (princ (car tail) stream)
192 (setq tail (cdr tail)))
193 (princ (or (gettext (get type 'error-message)) type)
194 stream))
195 (while tail
196 (princ (if first ": " ", ") stream)
197 (prin1 (car tail) stream)
198 (setq tail (cdr tail)
199 first nil))))))))
200 #'(lambda (error-object stream)
201 (princ (gettext "Peculiar error ") stream)
202 (prin1 error-object stream)))
203 error-object stream))
204
205 (put 'file-error 'display-error
206 #'(lambda (error-object stream)
207 (let ((tail (cdr error-object))
208 (first t))
209 (princ (car tail) stream)
210 (while (setq tail (cdr tail))
211 (princ (if first ": " ", ") stream)
212 (princ (car tail) stream)
213 (setq first nil)))))
214
215 (put 'undefined-keystroke-sequence 'display-error
216 #'(lambda (error-object stream)
217 (princ (key-description (car (cdr error-object))) stream)
218 ;; #### I18N3: doesn't localize properly.
219 (princ (gettext " not defined.") stream) ; doo dah, doo dah.
220 ))
221
222
223 (defvar teach-extended-commands-p t
224 "*If true, then `\\[execute-extended-command]' will teach you keybindings.
225 Any time you execute a command with \\[execute-extended-command] which has a
226 shorter keybinding, you will be shown the alternate binding before the
227 command executes. There is a short pause after displaying the binding,
228 before executing it; the length can be controlled by
229 `teach-extended-commands-timeout'.")
230
231 (defvar teach-extended-commands-timeout 2
232 "*How long to pause after displaying a keybinding before executing.
233 The value is measured in seconds. This only applies if
234 `teach-extended-commands-p' is true.")
235
236 ;That damn RMS went off and implemented something differently, after
237 ;we had already implemented it. We can't support both properly until
238 ;we have Lisp magic variables.
239 ;(defvar suggest-key-bindings t
240 ; "*FSFmacs equivalent of `teach-extended-commands-*'.
241 ;Provided for compatibility only.
242 ;Non-nil means show the equivalent key-binding when M-x command has one.
243 ;The value can be a length of time to show the message for.
244 ;If the value is non-nil and not a number, we wait 2 seconds.")
245 ;
246 ;(make-obsolete-variable 'suggest-key-bindings 'teach-extended-commands-p)
247
248 (defun execute-extended-command (prefix-arg)
249 "Read a command name from the minibuffer using 'completing-read'.
250 Then call the specified command using 'command-execute' and return its
251 return value. If the command asks for a prefix argument, supply the
252 value of the current raw prefix argument, or the value of PREFIX-ARG
253 when called from Lisp."
254 (interactive "P")
255 ;; Note: This doesn't hack "this-command-keys"
256 (let ((prefix-arg prefix-arg))
257 (setq this-command (read-command
258 ;; Note: this has the hard-wired
259 ;; "C-u" and "M-x" string bug in common
260 ;; with all GNU Emacs's.
261 ;; (i.e. it prints C-u and M-x regardless of
262 ;; whether some other keys were actually bound
263 ;; to `execute-extended-command' and
264 ;; `universal-argument'.
265 (cond ((eq prefix-arg '-)
266 "- M-x ")
267 ((equal prefix-arg '(4))
268 "C-u M-x ")
269 ((integerp prefix-arg)
270 (format "%d M-x " prefix-arg))
271 ((and (consp prefix-arg)
272 (integerp (car prefix-arg)))
273 (format "%d M-x " (car prefix-arg)))
274 (t
275 "M-x ")))))
276
277 (if (and teach-extended-commands-p (interactive-p))
278 (let ((keys (where-is-internal this-command)))
279 (if keys
280 (progn
281 (message "M-x %s (bound to key%s: %s)"
282 this-command
283 (if (cdr keys) "s" "")
284 (mapconcat 'key-description
285 (sort keys #'(lambda (x y)
286 (< (length x) (length y))))
287 ", "))
288 (sit-for teach-extended-commands-timeout)))))
289
290 (command-execute this-command t))
291
292
293 ;;; C code calls this; the underscores in the variable names are to avoid
294 ;;; cluttering the specbind namespace (lexical scope! lexical scope!)
295 ;;; Putting this in Lisp instead of C slows kbd macros by 50%.
296 ;(defun command-execute (_command &optional _record-flag)
297 ; "Execute CMD as an editor command.
298 ;CMD must be a symbol that satisfies the `commandp' predicate.
299 ;Optional second arg RECORD-FLAG non-nil
300 ;means unconditionally put this command in `command-history'.
301 ;Otherwise, that is done only if an arg is read using the minibuffer."
302 ; (let ((_prefix prefix-arg)
303 ; (_cmd (indirect-function _command)))
304 ; (setq prefix-arg nil
305 ; this-command _command
306 ; current-prefix-arg _prefix
307 ; zmacs-region-stays nil)
308 ; ;; #### debug_on_next_call = 0;
309 ; (cond ((and (symbolp _command)
310 ; (get _command 'disabled))
311 ; (run-hooks disabled-command-hook))
312 ; ((or (stringp _cmd) (vectorp _cmd))
313 ; ;; If requested, place the macro in the command history.
314 ; ;; For other sorts of commands, call-interactively takes
315 ; ;; care of this.
316 ; (if _record-flag
317 ; (setq command-history
318 ; (cons (list 'execute-kbd-macro _cmd _prefix)
319 ; command-history)))
320 ; (execute-kbd-macro _cmd _prefix))
321 ; (t
322 ; (call-interactively _command _record-flag)))))
323
324 (defun y-or-n-p-minibuf (prompt)
325 "Ask user a \"y or n\" question. Return t if answer is \"y\".
326 Takes one argument, which is the string to display to ask the question.
327 It should end in a space; `y-or-n-p' adds `(y or n) ' to it.
328 No confirmation of the answer is requested; a single character is enough.
329 Also accepts Space to mean yes, or Delete to mean no."
330 (save-excursion
331 (let* ((pre "")
332 (yn (gettext "(y or n) "))
333 ;; we need to translate the prompt ourselves because of the
334 ;; strange way we handle it.
335 (prompt (gettext prompt))
336 event)
337 (while (stringp yn)
338 (if (let ((cursor-in-echo-area t)
339 (inhibit-quit t))
340 (message "%s%s%s" pre prompt yn)
341 (setq event (next-command-event event))
342 (prog1
343 (or quit-flag (eq 'keyboard-quit (key-binding event)))
344 (setq quit-flag nil)))
345 (progn
346 (message "%s%s%s%s" pre prompt yn (single-key-description event))
347 (setq quit-flag nil)
348 (signal 'quit '())))
349 (let* ((keys (events-to-keys (vector event)))
350 (def (lookup-key query-replace-map keys)))
351 (cond ((eq def 'skip)
352 (message "%s%sNo" prompt yn)
353 (setq yn nil))
354 ((eq def 'act)
355 (message "%s%sYes" prompt yn)
356 (setq yn t))
357 ((eq def 'recenter)
358 (recenter))
359 ((or (eq def 'quit) (eq def 'exit-prefix))
360 (signal 'quit '()))
361 ((button-release-event-p event) ; ignore them
362 nil)
363 (t
364 (message "%s%s%s%s" pre prompt yn
365 (single-key-description event))
366 (ding nil 'y-or-n-p)
367 (discard-input)
368 (if (= (length pre) 0)
369 (setq pre (gettext "Please answer y or n. ")))))))
370 yn)))
371
372 (defun yes-or-no-p-minibuf (prompt)
373 "Ask user a yes-or-no question. Return t if answer is yes.
374 Takes one argument, which is the string to display to ask the question.
375 It should end in a space; `yes-or-no-p' adds `(yes or no) ' to it.
376 The user must confirm the answer with RET,
377 and can edit it until it has been confirmed."
378 (save-excursion
379 (let ((p (concat (gettext prompt) (gettext "(yes or no) ")))
380 (ans ""))
381 (while (stringp ans)
382 (setq ans (downcase (read-string p nil t))) ;no history
383 (cond ((string-equal ans (gettext "yes"))
384 (setq ans 't))
385 ((string-equal ans (gettext "no"))
386 (setq ans 'nil))
387 (t
388 (ding nil 'yes-or-no-p)
389 (discard-input)
390 (message "Please answer yes or no.")
391 (sleep-for 2))))
392 ans)))
393
394 ;; these may be redefined later, but make the original def easily encapsulable
395 (define-function 'yes-or-no-p 'yes-or-no-p-minibuf)
396 (define-function 'y-or-n-p 'y-or-n-p-minibuf)
397
398
399 (defun read-char ()
400 "Read a character from the command input (keyboard or macro).
401 If a mouse click or non-ASCII character is detected, an error is
402 signalled. The character typed is returned as an ASCII value. This
403 is most likely the wrong thing for you to be using: consider using
404 the `next-command-event' function instead."
405 (save-excursion
406 (let ((inhibit-quit t)
407 (event (next-command-event)))
408 (prog1 (or (event-to-character event)
409 ;; Kludge. If the event we read was a mouse-release,
410 ;; discard it and read the next one.
411 (if (button-release-event-p event)
412 (event-to-character (next-command-event event)))
413 (error "Key read has no ASCII equivalent %S" event))
414 ;; this is not necessary, but is marginally more efficient than GC.
415 (deallocate-event event)))))
416
417 (defun read-char-exclusive ()
418 "Read a character from the command input (keyboard or macro).
419 If a mouse click or non-ASCII character is detected, it is discarded.
420 The character typed is returned as an ASCII value. This is most likely
421 the wrong thing for you to be using: consider using the
422 `next-command-event' function instead."
423 (let ((inhibit-quit t)
424 event ch)
425 (while (progn
426 (setq event (next-command-event))
427 (setq ch (event-to-character event))
428 (deallocate-event event)
429 (null ch)))
430 ch))
431
432 (defun read-quoted-char (&optional prompt)
433 "Like `read-char', except that if the first character read is an octal
434 digit, we read up to two more octal digits and return the character
435 represented by the octal number consisting of those digits.
436 Optional argument PROMPT specifies a string to use to prompt the user."
437 (save-excursion
438 (let ((count 0) (code 0)
439 (prompt (and prompt (gettext prompt)))
440 char event)
441 (while (< count 3)
442 (let ((inhibit-quit (zerop count))
443 ;; Don't let C-h get the help message--only help function keys.
444 (help-char nil)
445 (help-form
446 "Type the special character you want to use,
447 or three octal digits representing its character code."))
448 (and prompt (display-message 'prompt (format "%s-" prompt)))
449 (setq event (next-command-event)
450 char (or (event-to-character event nil nil t)
451 (error "key read cannot be inserted in a buffer: %S"
452 event)))
453 (if inhibit-quit (setq quit-flag nil)))
454 (cond ((null char))
455 ((and (<= ?0 char) (<= char ?7))
456 (setq code (+ (* code 8) (- char ?0))
457 count (1+ count))
458 (and prompt (display-message
459 'prompt
460 (setq prompt (format "%s %c" prompt char)))))
461 ((> count 0)
462 (setq unread-command-event event
463 count 259))
464 (t (setq code char count 259))))
465 ;; Turn a meta-character into a character with the 0200 bit set.
466 (logior (if (/= (logand code ?\M-\^@) 0) 128 0)
467 (logand 255 code)))))
468
469 (defun momentary-string-display (string pos &optional exit-char message)
470 "Momentarily display STRING in the buffer at POS.
471 Display remains until next character is typed.
472 If the char is EXIT-CHAR (optional third arg, default is SPC) it is swallowed;
473 otherwise it is then available as input (as a command if nothing else).
474 Display MESSAGE (optional fourth arg) in the echo area.
475 If MESSAGE is nil, instructions to type EXIT-CHAR are displayed there."
476 (or exit-char (setq exit-char ?\ ))
477 (let ((buffer-read-only nil)
478 ;; Don't modify the undo list at all.
479 (buffer-undo-list t)
480 (modified (buffer-modified-p))
481 (name buffer-file-name)
482 insert-end)
483 (unwind-protect
484 (progn
485 (save-excursion
486 (goto-char pos)
487 ;; defeat file locking... don't try this at home, kids!
488 (setq buffer-file-name nil)
489 (insert-before-markers (gettext string))
490 (setq insert-end (point))
491 ;; If the message end is off frame, recenter now.
492 (if (> (window-end) insert-end)
493 (recenter (/ (window-height) 2)))
494 ;; If that pushed message start off the frame,
495 ;; scroll to start it at the top of the frame.
496 (move-to-window-line 0)
497 (if (> (point) pos)
498 (progn
499 (goto-char pos)
500 (recenter 0))))
501 (message (or message (gettext "Type %s to continue editing."))
502 (single-key-description exit-char))
503 (let ((event (save-excursion (next-command-event))))
504 (or (eq (event-to-character event) exit-char)
505 (setq unread-command-event event))))
506 (if insert-end
507 (save-excursion
508 (delete-region pos insert-end)))
509 (setq buffer-file-name name)
510 (set-buffer-modified-p modified))))
511
512 ;;; cmdloop.el ends here