(read_minibuf): Clean up the binding stack if
[bpt/emacs.git] / lisp / comint.el
index 8edd773..921b567 100644 (file)
@@ -1,6 +1,6 @@
 ;;; comint.el --- general command interpreter in a window stuff
 
-;; Copyright (C) 1988, 90, 92, 93, 94, 95, 96, 97, 98, 99, 2000
+;; Copyright (C) 1988, 90, 92, 93, 94, 95, 96, 97, 98, 99, 2000, 2001
 ;;     Free Software Foundation, Inc.
 
 ;; Author: Olin Shivers <shivers@cs.cmu.edu> then
@@ -207,21 +207,21 @@ This variable is buffer-local."
 
 (defcustom comint-highlight-input t
   "*If non-nil, highlight input; also allow choosing previous input with a mouse.
-See also `comint-highlight-face'."
+The face used is `comint-highlight-input'."
   :type 'boolean
   :group 'comint)
 
-(defface comint-highlight-input-face '((t (:bold t)))
+(defface comint-highlight-input '((t (:bold t)))
   "Face to use to highlight input when `comint-highlight-input' is non-nil."
   :group 'comint)
 
 (defcustom comint-highlight-prompt t
   "*If non-nil, highlight program prompts.
-See also `comint-highlight-face'."
+The face used is `comint-highlight-prompt'."
   :type 'boolean
   :group 'comint)
 
-(defface comint-highlight-prompt-face
+(defface comint-highlight-prompt
   '((((background dark)) (:foreground "cyan"))
     (t (:foreground "dark blue")))
   "Face to use to highlight prompt when `comint-highlight-prompt' is non-nil."
@@ -297,6 +297,9 @@ the function `comint-truncate-buffer' is on `comint-output-filter-functions'."
 (defvar comint-input-ring-separator "\n"
   "Separator between commands in the history file.")
 
+(defvar comint-input-history-ignore "^#"
+  "Regexp for history entries that should be ignored when comint initializes.")
+
 (defcustom comint-process-echoes nil
   "*If non-nil, assume that the subprocess echoes any input.
 If so, delete one copy of the input so that only one copy eventually
@@ -312,7 +315,7 @@ This variable is buffer-local."
 ;; ssh-add prints a prompt like `Enter passphrase: '.
 ;; Some implementations of passwd use "Password (again)" as the 2nd prompt.
 (defcustom comint-password-prompt-regexp
-  "\\(\\([Oo]ld \\|[Nn]ew \\|Kerberos \\|'s \\|login \\|^\\)\
+  "\\(\\([Oo]ld \\|[Nn]ew \\|Kerberos \\|'s \\|login \\|CVS \\|^\\)\
 [Pp]assword\\( (again)\\)?\\|pass phrase\\|Enter passphrase\\)\
 \\( for [^@ \t\n]+@[^@ \t\n]+\\)?:\\s *\\'"
   "*Regexp matching prompts for passwords in the inferior process.
@@ -512,7 +515,6 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
   (make-local-variable 'comint-scroll-to-bottom-on-input)
   (make-local-variable 'comint-scroll-to-bottom-on-output)
   (make-local-variable 'comint-scroll-show-maximum-output)
-  (make-local-hook 'pre-command-hook)
   (add-hook 'pre-command-hook 'comint-preinput-scroll-to-bottom t t)
   (make-local-hook 'comint-output-filter-functions)
   (make-local-hook 'comint-exec-hook)
@@ -522,7 +524,9 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
   (make-local-variable 'comint-file-name-quote-list)
   (make-local-variable 'comint-accum-marker)
   (setq comint-accum-marker (make-marker))
-  (set-marker comint-accum-marker nil))
+  (set-marker comint-accum-marker nil)
+  ;; This behavior is not useful in comint buffers, and is annoying
+  (set (make-local-variable 'next-line-add-newlines) nil))
 
 (if comint-mode-map
     nil
@@ -555,6 +559,7 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
   (define-key comint-mode-map "\C-c\C-n" 'comint-next-prompt)
   (define-key comint-mode-map "\C-c\C-p" 'comint-previous-prompt)
   (define-key comint-mode-map "\C-c\C-d" 'comint-send-eof)
+  (define-key comint-mode-map "\C-c\C-s" 'comint-write-output)
   ;; Mouse Buttons:
   (define-key comint-mode-map [mouse-2] 'comint-insert-clicked-input)
   ;; Menu bars:
@@ -574,6 +579,10 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
     (cons "In/Out" (make-sparse-keymap "In/Out")))
   (define-key comint-mode-map [menu-bar inout delete-output]
     '("Delete Current Output Group" . comint-delete-output))
+  (define-key comint-mode-map [menu-bar inout append-output-to-file]
+    '("Append Current Output Group to File" . comint-append-output-to-file))
+  (define-key comint-mode-map [menu-bar inout write-output]
+    '("Write Current Output Group to File" . comint-write-output))
   (define-key comint-mode-map [menu-bar inout next-prompt]
     '("Forward Output Group" . comint-next-prompt))
   (define-key comint-mode-map [menu-bar inout previous-prompt]
@@ -633,6 +642,28 @@ BUFFER can be either a buffer or the name of one."
   (let ((proc (get-buffer-process buffer)))
     (and proc (memq (process-status proc) '(open run stop)))))
 
+;;;###autoload
+(defun make-comint-in-buffer (name buffer program &optional startfile &rest switches)
+  "Make a comint process NAME in BUFFER, running PROGRAM.
+If BUFFER is nil, it defaults to NAME surrounded by `*'s.
+PROGRAM should be either a string denoting an executable program to create
+via `start-process', or a cons pair of the form (HOST . SERVICE) denoting a TCP
+connection to be opened via `open-network-stream'.  If there is already a
+running process in that buffer, it is not restarted.  Optional third arg
+STARTFILE is the name of a file to send the contents of to the process.
+
+If PROGRAM is a string, any more args are arguments to PROGRAM."
+  (or (fboundp 'start-process)
+      (error "Multi-processing is not supported for this system"))
+  (setq buffer (get-buffer-create (or buffer (concat "*" name "*"))))
+  ;; If no process, or nuked process, crank up a new one and put buffer in
+  ;; comint mode.  Otherwise, leave buffer and existing process alone.
+  (unless (comint-check-proc buffer)
+    (with-current-buffer buffer
+      (comint-mode)) ; Install local vars, mode, keymap, ...
+    (comint-exec buffer name program startfile switches))
+  buffer)
+
 ;;;###autoload
 (defun make-comint (name program &optional startfile &rest switches)
   "Make a comint process NAME in a buffer, running PROGRAM.
@@ -644,16 +675,7 @@ running process in that buffer, it is not restarted.  Optional third arg
 STARTFILE is the name of a file to send the contents of to the process.
 
 If PROGRAM is a string, any more args are arguments to PROGRAM."
-  (or (fboundp 'start-process)
-      (error "Multi-processing is not supported for this system"))
-  (let ((buffer (get-buffer-create (concat "*" name "*"))))
-    ;; If no process, or nuked process, crank up a new one and put buffer in
-    ;; comint mode.  Otherwise, leave buffer and existing process alone.
-    (unless (comint-check-proc buffer)
-      (with-current-buffer buffer
-       (comint-mode)) ; Install local vars, mode, keymap, ...
-      (comint-exec buffer name program startfile switches))
-    buffer))
+  (apply #'make-comint-in-buffer name nil program startfile switches))
 
 ;;;###autoload
 (defun comint-run (program)
@@ -813,7 +835,9 @@ failure to read the history file.
 This function is useful for major mode commands and mode hooks.
 
 The commands stored in the history file are separated by the
-`comint-input-ring-separator'.  The most recent command comes last.
+`comint-input-ring-separator', and entries that match
+`comint-input-history-ignore' are ignored.  The most recent command
+comes last.
 
 See also `comint-input-ignoredups' and `comint-write-input-ring'."
   (cond ((or (null comint-input-ring-file-name)
@@ -838,22 +862,22 @@ See also `comint-input-ignoredups' and `comint-write-input-ring'."
                 ;; Save restriction in case file is already visited...
                 ;; Watch for those date stamps in history files!
                 (goto-char (point-max))
-                (while (and (< count size)
-                            (re-search-backward "^[ \t]*\\([^#\n].*\\)[ \t]*$"
-                                                nil t))
-              (let (start end history)
-                (while (and (< count comint-input-ring-size)
-                            (re-search-backward comint-input-ring-separator nil t)
-                            (setq end (match-beginning 0))
-                            (re-search-backward comint-input-ring-separator nil t)
-                            (setq start (match-end 0))
-                            (setq history (buffer-substring start end))
-                            (goto-char start))
-                  (if (or (null comint-input-ignoredups)
-                          (ring-empty-p ring)
-                          (not (string-equal (ring-ref ring 0) history)))
-                      (ring-insert-at-beginning ring history)))
-                (setq count (1+ count)))))
+                (let (start end history)
+                  (while (and (< count comint-input-ring-size)
+                              (re-search-backward comint-input-ring-separator nil t)
+                              (setq end (match-beginning 0)))
+                    (if (re-search-backward comint-input-ring-separator nil t)
+                        (setq start (match-end 0))
+                      (setq start (point-min)))
+                    (setq history (buffer-substring start end))
+                    (goto-char start)
+                    (if (and (not (string-match comint-input-history-ignore history))
+                             (or (null comint-input-ignoredups)
+                                 (ring-empty-p ring)
+                                 (not (string-equal (ring-ref ring 0) history))))
+                        (progn
+                          (ring-insert-at-beginning ring history)
+                          (setq count (1+ count)))))))
             (kill-buffer history-buf))
           (setq comint-input-ring ring
                 comint-input-ring-index nil)))))
@@ -1087,14 +1111,15 @@ See `comint-magic-space' and `comint-replace-by-expanded-history-before-point'.
 Returns t if successful."
   (interactive)
   (if (and comint-input-autoexpand
-          (string-match "!\\|^\\^" (funcall comint-get-old-input))
           (if comint-use-prompt-regexp-instead-of-fields
               ;; Use comint-prompt-regexp
-              (save-excursion (beginning-of-line)
-                              (looking-at comint-prompt-regexp))
+              (save-excursion
+                (beginning-of-line)
+                (looking-at (concat comint-prompt-regexp "!\\|\\^")))
             ;; Use input fields.  User input that hasn't been entered
             ;; yet, at the end of the buffer, has a nil `field' property.
-            (null (get-char-property (point) 'field))))
+            (and (null (get-char-property (point) 'field))
+                 (string-match "!\\|^\\^" (field-string)))))
       ;; Looks like there might be history references in the command.
       (let ((previous-modified-tick (buffer-modified-tick)))
        (comint-replace-by-expanded-history-before-point silent start)
@@ -1111,6 +1136,7 @@ than the logical beginning of line."
   (save-excursion
     (let ((toend (- (line-end-position) (point)))
          (start (comint-line-beginning-position)))
+      (goto-char start)
       (while (progn
               (skip-chars-forward "^!^" (- (line-end-position) toend))
               (< (point) (- (line-end-position) toend)))
@@ -1311,6 +1337,16 @@ Argument 0 is the command name."
 ;;
 ;; Input processing stuff
 ;;
+(defun comint-add-to-input-history (cmd)
+  "Add CMD to the input history.
+Ignore duplicates if `comint-input-ignoredups' is non-nil."
+  (if (and (funcall comint-input-filter cmd)
+          (or (null comint-input-ignoredups)
+              (not (ring-p comint-input-ring))
+              (ring-empty-p comint-input-ring)
+              (not (string-equal (ring-ref comint-input-ring 0)
+                                 cmd))))
+      (ring-insert comint-input-ring cmd)))
 
 (defun comint-send-input ()
   "Send input to process.
@@ -1386,13 +1422,7 @@ Similarly for Soar, Scheme, etc."
              (delete-region pmark (point))
            (insert ?\n))
 
-         (if (and (funcall comint-input-filter history)
-                  (or (null comint-input-ignoredups)
-                      (not (ring-p comint-input-ring))
-                      (ring-empty-p comint-input-ring)
-                      (not (string-equal (ring-ref comint-input-ring 0)
-                                         history))))
-             (ring-insert comint-input-ring history))
+         (comint-add-to-input-history history)
 
          (run-hook-with-args 'comint-input-filter-functions
                              (concat input "\n"))
@@ -1408,17 +1438,15 @@ Similarly for Soar, Scheme, etc."
                  ;; input.  The terminating newline is put into a special
                  ;; `boundary' field to make cursor movement between input
                  ;; and output fields smoother.
-                 (overlay-put over 'field 'input)
-                 (overlay-put over 'front-sticky t))
+                 (overlay-put over 'field 'input))
                (when comint-highlight-input
-                 (overlay-put over 'face 'comint-highlight-input-face)
+                 (overlay-put over 'face 'comint-highlight-input)
                  (overlay-put over 'mouse-face 'highlight)
                  (overlay-put over 'evaporate t))))
            (unless comint-use-prompt-regexp-instead-of-fields
              ;; Make an overlay for the terminating newline
              (let ((over (make-overlay end (1+ end) nil t nil)))
                (overlay-put over 'field 'boundary)
-               (overlay-put over 'rear-nonsticky t)
                (overlay-put over 'evaporate t))))
 
          (comint-snapshot-last-prompt)
@@ -1560,8 +1588,6 @@ This variable is permanent-local.")
                (let ((over (make-overlay comint-last-output-start (point))))
                  (overlay-put over 'field 'output)
                  (overlay-put over 'inhibit-line-move-field-capture t)
-                 (overlay-put over 'front-sticky t)
-                 (overlay-put over 'rear-nonsticky t)
                  (overlay-put over 'evaporate t)
                  (setq comint-last-output-overlay over))))
 
@@ -1578,11 +1604,10 @@ This variable is permanent-local.")
                      (move-overlay comint-last-prompt-overlay
                                    prompt-start (point))
                    ;; Need to create the overlay
-                   (let ((over (make-overlay prompt-start (point))))
-                     (overlay-put over 'face 'comint-highlight-prompt-face)
-                     (overlay-put over 'front-sticky t)
-                     (overlay-put over 'rear-nonsticky t)
-                     (setq comint-last-prompt-overlay over))))))
+                   (setq comint-last-prompt-overlay
+                         (make-overlay prompt-start (point)))
+                   (overlay-put comint-last-prompt-overlay
+                                'face 'comint-highlight-prompt)))))
 
            (goto-char saved-point)
 
@@ -1697,8 +1722,10 @@ value of `comint-use-prompt-regexp-instead-of-fields'."
          (end-of-line)
          (buffer-substring beg (point))))
     ;; Return the contents of the field at the current point.
-    (field-string)))
-
+    (let ((pos (field-beginning (point))))
+      (unless (eq (get-char-property pos 'field) 'input)
+       (error "Not an input field"))
+      (field-string pos))))
 
 (defun comint-copy-old-input ()
   "Insert after prompt old input at point as new input to be edited.
@@ -1858,20 +1885,28 @@ This function could be in the list `comint-output-filter-functions'."
 
 (defun comint-send-string (process string)
   "Like `process-send-string', but also does extra bookkeeping for comint mode."
-  (with-current-buffer (process-buffer process)
+  (if process
+      (with-current-buffer (if (processp process)
+                              (process-buffer process)
+                            (get-buffer process))
+       (comint-snapshot-last-prompt))
     (comint-snapshot-last-prompt))
   (process-send-string process string))
 
 (defun comint-send-region (process start end)
   "Like `process-send-region', but also does extra bookkeeping for comint mode."
-  (with-current-buffer (process-buffer process)
+  (if process
+      (with-current-buffer (if (processp process)
+                              (process-buffer process)
+                            (get-buffer process))
+       (comint-snapshot-last-prompt))
     (comint-snapshot-last-prompt))
   (process-send-region process start end))
 \f
 ;; Random input hackage
 
 (defun comint-delete-output ()
-  "Kill all output from interpreter since last input.
+  "Delete all output from interpreter since last input.
 Does not delete the prompt."
   (interactive)
   (let ((proc (get-buffer-process (current-buffer)))
@@ -1890,6 +1925,41 @@ Does not delete the prompt."
 (defalias 'comint-kill-output 'comint-delete-output)
 (make-obsolete 'comint-kill-output 'comint-delete-output "21.1")
 
+(defun comint-write-output (filename &optional append mustbenew)
+  "Write output from interpreter since last input to FILENAME.
+Any prompt at the end of the output is not written.
+
+If the optional argument APPEND (the prefix argument when interactive)
+is non-nil, the output is appended to the file instead.
+
+If the optional argument MUSTBENEW is non-nil, check for an existing
+file with the same name.  If MUSTBENEW is `excl', that means to get an
+error if the file already exists; never overwrite.  If MUSTBENEW is
+neither nil nor `excl', that means ask for confirmation before
+overwriting, but do go ahead and overwrite the file if the user
+confirms.  When interactive, MUSTBENEW is nil when appending, and t
+otherwise."
+  (interactive
+   (list (read-file-name
+         (if current-prefix-arg
+             "Append output to file: "
+           "Write output to file: "))
+        current-prefix-arg
+        (not current-prefix-arg)))
+  (save-excursion
+    (goto-char (process-mark (get-buffer-process (current-buffer))))
+    (forward-line 0)
+    (write-region comint-last-input-end (point) filename
+                 append nil nil mustbenew)))
+
+;; This function exists for the benefit of the menu; from the keyboard,
+;; users can just use `comint-write-output' with a prefix arg.
+(defun comint-append-output-to-file (filename)
+  "Append output from interpreter since last input to FILENAME.
+Any prompt at the end of the output is not written."
+  (interactive "fAppend output to file: ")
+  (comint-write-output filename t))
+
 (defun comint-show-output ()
   "Display start of this batch of interpreter output at top of window.
 Sets mark to the value of point when this command is run."
@@ -2278,7 +2348,11 @@ This mirrors the optional behavior of tcsh."
 If a cons pair, it should be of the form (DIRSUFFIX . FILESUFFIX) where
 DIRSUFFIX and FILESUFFIX are strings added on unambiguous or exact completion.
 This mirrors the optional behavior of tcsh."
-  :type 'boolean
+  :type '(choice (const :tag "None" nil)
+                (const :tag "Add /" t)
+                (cons :tag "Suffix pair"
+                      (string :tag "Directory suffix")
+                      (string :tag "File suffix")))
   :group 'comint-completion)
 
 (defcustom comint-completion-recexact nil
@@ -2630,7 +2704,8 @@ from input that has not yet been sent."
   (let ((proc (or (get-buffer-process (current-buffer))
                  (error "Current buffer has no process"))))
     (goto-char (process-mark proc))
-    (message "Point is now at the process mark")))
+    (when (interactive-p)
+      (message "Point is now at the process mark"))))
 
 (defun comint-bol-or-process-mark ()
   "Move point to beginning of line (after prompt) or to the process mark.