Mercurial > hg > xemacs-beta
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 |