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