Refill some copyright headers.
[bpt/emacs.git] / lisp / shell.el
index b4b3dbf..5d27efa 100644 (file)
@@ -1,7 +1,8 @@
 ;;; shell.el --- specialized comint.el for running the shell
 
-;; Copyright (C) 1988, 1993, 1994, 1995, 1996, 1997, 2000, 2001,
-;;   2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 1988, 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2002,
+;;   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+;;   Free Software Foundation, Inc.
 
 ;; Author: Olin Shivers <shivers@cs.cmu.edu>
 ;;     Simon Marshall <simon@gnu.org>
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +22,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -72,7 +71,7 @@
 ;; c-c c-c comint-interrupt-subjob        ^c
 ;; c-c c-z comint-stop-subjob             ^z
 ;; c-c c-\ comint-quit-subjob             ^\
-;; c-c c-o comint-kill-output             Delete last batch of process output
+;; c-c c-o comint-delete-output                   Delete last batch of process output
 ;; c-c c-r comint-show-output             Show last batch of process output
 ;; c-c c-l comint-dynamic-list-input-ring  List input history
 ;;         send-invisible                  Read line w/o echo & send to proc
   :group 'shell)
 
 ;;;###autoload
-(defcustom shell-dumb-shell-regexp "cmd\\(proxy\\)?\\.exe"
+(defcustom shell-dumb-shell-regexp (purecopy "cmd\\(proxy\\)?\\.exe")
   "Regexp to match shells that don't save their command history, and
 don't handle the backslash as a quote character.  For shells that
 match this regexp, Emacs will write out the command history when the
@@ -174,7 +173,7 @@ This is a fine thing to set in your `.emacs' file.")
 (defvar shell-file-name-quote-list
   (if (memq system-type '(ms-dos windows-nt))
       nil
-    (append shell-delimiter-argument-list '(?\s ?\* ?\! ?\" ?\' ?\` ?\# ?\\)))
+    (append shell-delimiter-argument-list '(?\s ?$ ?\* ?\! ?\" ?\' ?\` ?\# ?\\)))
   "List of characters to quote when in a file name.
 This variable is used to initialize `comint-file-name-quote-list' in the
 shell buffer.  The value may depend on the operating system or shell.
@@ -186,6 +185,7 @@ This is a fine thing to set in your `.emacs' file.")
     shell-dynamic-complete-environment-variable
     shell-dynamic-complete-command
     shell-replace-by-expanded-directory
+    shell-dynamic-complete-filename
     comint-dynamic-complete-filename)
   "List of functions called to perform completion.
 This variable is used to initialize `comint-dynamic-complete-functions' in the
@@ -335,28 +335,28 @@ Thus, this does not include the shell's current directory.")
 (defvar shell-dirstack-query nil
   "Command used by `shell-resync-dirs' to query the shell.")
 
-(defvar shell-mode-map nil)
-(cond ((not shell-mode-map)
-       (setq shell-mode-map (nconc (make-sparse-keymap) comint-mode-map))
-       (define-key shell-mode-map "\C-c\C-f" 'shell-forward-command)
-       (define-key shell-mode-map "\C-c\C-b" 'shell-backward-command)
-       (define-key shell-mode-map "\t" 'comint-dynamic-complete)
-       (define-key shell-mode-map "\M-?"
-        'comint-dynamic-list-filename-completions)
-       (define-key shell-mode-map [menu-bar completion]
-        (cons "Complete"
-              (copy-keymap (lookup-key comint-mode-map [menu-bar completion]))))
-       (define-key-after (lookup-key shell-mode-map [menu-bar completion])
-        [complete-env-variable] '("Complete Env. Variable Name" .
-                                  shell-dynamic-complete-environment-variable)
-        'complete-file)
-       (define-key-after (lookup-key shell-mode-map [menu-bar completion])
-        [expand-directory] '("Expand Directory Reference" .
-                             shell-replace-by-expanded-directory)
-        'complete-expand)))
+(defvar shell-mode-map
+  (let ((map (nconc (make-sparse-keymap) comint-mode-map)))
+    (define-key map "\C-c\C-f" 'shell-forward-command)
+    (define-key map "\C-c\C-b" 'shell-backward-command)
+    (define-key map "\t" 'comint-dynamic-complete)
+    (define-key map (kbd "M-RET") 'shell-resync-dirs)
+    (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
+    (define-key map [menu-bar completion]
+      (cons "Complete"
+           (copy-keymap (lookup-key comint-mode-map [menu-bar completion]))))
+    (define-key-after (lookup-key map [menu-bar completion])
+      [complete-env-variable] '("Complete Env. Variable Name" .
+                               shell-dynamic-complete-environment-variable)
+      'complete-file)
+    (define-key-after (lookup-key map [menu-bar completion])
+      [expand-directory] '("Expand Directory Reference" .
+                          shell-replace-by-expanded-directory)
+      'complete-expand)
+    map))
 
 (defcustom shell-mode-hook '()
-  "*Hook for customising Shell mode."
+  "Hook for customizing Shell mode."
   :type 'hook
   :group 'shell)
 
@@ -368,6 +368,17 @@ Thus, this does not include the shell's current directory.")
 
 ;;; Basic Procedures
 
+(defcustom shell-dir-cookie-re nil
+  "Regexp matching your prompt, including some part of the current directory.
+If your prompt includes the current directory or the last few elements of it,
+set this to a pattern that matches your prompt and whose subgroup 1 matches
+the directory part of it.
+This is used by `shell-dir-cookie-watcher' to try and use this info
+to track your current directory.  It can be used instead of or in addition
+to `dirtrack-mode'."
+  :group 'shell
+  :type '(choice (const nil) regexp))
+
 (put 'shell-mode 'mode-class 'special)
 
 (define-derived-mode shell-mode comint-mode "Shell"
@@ -441,10 +452,9 @@ buffer."
   (make-local-variable 'shell-last-dir)
   (setq shell-last-dir nil)
   (setq comint-input-autoexpand shell-input-autoexpand)
+  (shell-dirtrack-mode 1)
   ;; This is not really correct, since the shell buffer does not really
   ;; edit this directory.  But it is useful in the buffer list and menus.
-  (make-local-variable 'list-buffers-directory)
-  (shell-dirtrack-mode 1)
   (setq list-buffers-directory (expand-file-name default-directory))
   ;; shell-dependent assignments.
   (when (ring-empty-p comint-input-ring)
@@ -473,6 +483,10 @@ buffer."
       (when (string-equal shell "bash")
         (add-hook 'comint-output-filter-functions
                   'shell-filter-ctrl-a-ctrl-b nil t)))
+    (when shell-dir-cookie-re
+      ;; Watch for magic cookies in the output to track the current dir.
+      (add-hook 'comint-output-filter-functions
+               'shell-dir-cookie-watcher nil t))
     (comint-read-input-ring t)))
 
 (defun shell-filter-ctrl-a-ctrl-b (string)
@@ -551,12 +565,19 @@ Otherwise, one argument `-i' is passed to the shell.
                          (generate-new-buffer-name "*shell*"))
           (if (file-remote-p default-directory)
               ;; It must be possible to declare a local default-directory.
+               ;; FIXME: This can't be right: it changes the default-directory
+               ;; of the current-buffer rather than of the *shell* buffer.
               (setq default-directory
                     (expand-file-name
                      (read-file-name
                       "Default directory: " default-directory default-directory
                       t nil 'file-directory-p))))))))
-  (setq buffer (get-buffer-create (or buffer "*shell*")))
+  (require 'ansi-color)
+  (setq buffer (if (or buffer (not (derived-mode-p 'shell-mode))
+                       (comint-check-proc (current-buffer)))
+                   (get-buffer-create (or buffer "*shell*"))
+                 ;; If the current buffer is a dead shell buffer, use it.
+                 (current-buffer)))
   ;; Pop to buffer, so that the buffer's window will be correctly set
   ;; when we call comint (so that comint sets the COLUMNS env var properly).
   (pop-to-buffer buffer)
@@ -577,7 +598,7 @@ Otherwise, one argument `-i' is passed to the shell.
   buffer)
 
 ;; Don't do this when shell.el is loaded, only while dumping.
-;;;###autoload (add-hook 'same-window-buffer-names "*shell*")
+;;;###autoload (add-hook 'same-window-buffer-names (purecopy "*shell*"))
 
 ;;; Directory tracking
 ;;
@@ -619,6 +640,20 @@ Otherwise, one argument `-i' is passed to the shell.
 ;; replace it with a process filter that watches for and strips out
 ;; these messages.
 
+(defun shell-dir-cookie-watcher (text)
+  ;; This is fragile: the TEXT could be split into several chunks and we'd
+  ;; miss it.  Oh well.  It's a best effort anyway.  I'd expect that it's
+  ;; rather unusual to have the prompt split into several packets, but
+  ;; I'm sure Murphy will prove me wrong.
+  (when (and shell-dir-cookie-re (string-match shell-dir-cookie-re text))
+    (let ((dir (match-string 1 text)))
+      (cond
+       ((file-name-absolute-p dir) (shell-cd dir))
+       ;; Let's try and see if it seems to be up or down from where we were.
+       ((string-match "\\`\\(.*\\)\\(?:/.*\\)?\n\\(.*/\\)\\1\\(?:/.*\\)?\\'"
+                     (setq text (concat dir "\n" default-directory)))
+       (shell-cd (concat (match-string 2 text) dir)))))))
+
 (defun shell-directory-tracker (str)
   "Tracks cd, pushd and popd commands issued to the shell.
 This function is called on each input passed to the shell.
@@ -701,7 +736,7 @@ Environment variables are expanded, see function `substitute-in-file-name'."
 (defun shell-process-popd (arg)
   (let ((num (or (shell-extract-num arg) 0)))
     (cond ((and num (= num 0) shell-dirstack)
-          (shell-cd (car shell-dirstack))
+          (shell-cd (shell-prefixed-directory-name (car shell-dirstack)))
           (setq shell-dirstack (cdr shell-dirstack))
           (shell-dirstack-message))
          ((and num (> num 0) (<= num (length shell-dirstack)))
@@ -929,7 +964,7 @@ Copy Shell environment variable to Emacs: ")))
   "Move forward across ARG shell command(s).  Does not cross lines.
 See `shell-command-regexp'."
   (interactive "p")
-  (let ((limit (save-excursion (end-of-line nil) (point))))
+  (let ((limit (line-end-position)))
     (if (re-search-forward (concat shell-command-regexp "\\([;&|][\t ]*\\)+")
                           limit 'move arg)
        (skip-syntax-backward " "))))
@@ -965,7 +1000,8 @@ Returns t if successful."
             (save-match-data (not (string-match "[~/]" filename)))
             (eq (match-beginning 0)
                 (save-excursion (shell-backward-command 1) (point))))
-       (prog2 (message "Completing command name...")
+       (prog2 (unless (window-minibuffer-p (selected-window))
+                (message "Completing command name..."))
            (shell-dynamic-complete-as-command)))))
 
 
@@ -1009,6 +1045,19 @@ See `shell-dynamic-complete-filename'.  Returns t if successful."
          (insert " "))
       success)))
 
+(defun shell-dynamic-complete-filename ()
+  "Dynamically complete the filename at point.
+This completes only if point is at a suitable position for a
+filename argument."
+  (interactive)
+  (let ((opoint (point))
+       (beg (comint-line-beginning-position)))
+    (when (save-excursion
+           (goto-char (if (re-search-backward "[;|&]" beg t)
+                          (match-end 0)
+                        beg))
+           (re-search-forward "[^ \t][ \t]" opoint t))
+      (comint-dynamic-complete-as-filename))))
 
 (defun shell-match-partial-variable ()
   "Return the shell variable at point, or nil if none is found."
@@ -1022,7 +1071,6 @@ See `shell-dynamic-complete-filename'.  Returns t if successful."
        (re-search-forward "\\$?{?[A-Za-z0-9_]*}?" limit)
        (buffer-substring (match-beginning 0) (match-end 0))))))
 
-
 (defun shell-dynamic-complete-environment-variable ()
   "Dynamically complete the environment variable at point.
 Completes if after a variable, i.e., if it starts with a \"$\".
@@ -1040,7 +1088,8 @@ Returns non-nil if successful."
   (interactive)
   (let ((variable (shell-match-partial-variable)))
     (if (and variable (string-match "^\\$" variable))
-       (prog2 (message "Completing variable name...")
+       (prog2 (unless (window-minibuffer-p (selected-window))
+                (message "Completing variable name..."))
            (shell-dynamic-complete-as-environment-variable)))))
 
 
@@ -1098,5 +1147,4 @@ Returns t if successful."
 
 (provide 'shell)
 
-;; arch-tag: bcb5f12a-c1f4-4aea-a809-2504bd5bd797
 ;;; shell.el ends here