]> code.delx.au - gnu-emacs/blobdiff - lisp/shell.el
* lisp/international/mule-cmds.el (read-char-by-name): Move let-binding
[gnu-emacs] / lisp / shell.el
index f7a530102ff342298921b2cdc4e57d04be8eb19e..d79ca363a0fc27694c32d9cc069f02c2c14cf470 100644 (file)
@@ -1,6 +1,7 @@
 ;;; shell.el --- specialized comint.el for running the shell -*- lexical-binding: t -*-
 
 ;;; shell.el --- specialized comint.el for running the shell -*- lexical-binding: t -*-
 
-;; Copyright (C) 1988, 1993-1997, 2000-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1988, 1993-1997, 2000-2013 Free Software Foundation,
+;; Inc.
 
 ;; Author: Olin Shivers <shivers@cs.cmu.edu>
 ;;     Simon Marshall <simon@gnu.org>
 
 ;; Author: Olin Shivers <shivers@cs.cmu.edu>
 ;;     Simon Marshall <simon@gnu.org>
@@ -46,7 +47,7 @@
 
 ;; YOUR .EMACS FILE
 ;;=============================================================================
 
 ;; YOUR .EMACS FILE
 ;;=============================================================================
-;; Some suggestions for your .emacs file.
+;; Some suggestions for your init file.
 ;;
 ;; ;; Define M-# to run some strange command:
 ;; (eval-after-load "shell"
 ;;
 ;; ;; Define M-# to run some strange command:
 ;; (eval-after-load "shell"
@@ -96,7 +97,6 @@
 
 ;;; Code:
 
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
 (require 'comint)
 (require 'pcomplete)
 
 (require 'comint)
 (require 'pcomplete)
 
@@ -137,9 +137,7 @@ how Shell mode treats paragraphs.
 
 The pattern should probably not match more than one line.  If it does,
 Shell mode may become confused trying to distinguish prompt from input
 
 The pattern should probably not match more than one line.  If it does,
 Shell mode may become confused trying to distinguish prompt from input
-on lines which don't start with a prompt.
-
-This is a fine thing to set in your `.emacs' file."
+on lines which don't start with a prompt."
   :type 'regexp
   :group 'shell)
 
   :type 'regexp
   :group 'shell)
 
@@ -147,9 +145,7 @@ This is a fine thing to set in your `.emacs' file."
   "List of suffixes to be disregarded during file/command completion.
 This variable is used to initialize `comint-completion-fignore' in the shell
 buffer.  The default is nil, for compatibility with most shells.
   "List of suffixes to be disregarded during file/command completion.
 This variable is used to initialize `comint-completion-fignore' in the shell
 buffer.  The default is nil, for compatibility with most shells.
-Some people like (\"~\" \"#\" \"%\").
-
-This is a fine thing to set in your `.emacs' file."
+Some people like (\"~\" \"#\" \"%\")."
   :type '(repeat (string :tag "Suffix"))
   :group 'shell)
 
   :type '(repeat (string :tag "Suffix"))
   :group 'shell)
 
@@ -159,31 +155,29 @@ This variable is used to initialize `comint-delimiter-argument-list' in the
 shell buffer.  The value may depend on the operating system or shell."
   :type '(choice (const nil)
                 (repeat :tag "List of characters" character))
 shell buffer.  The value may depend on the operating system or shell."
   :type '(choice (const nil)
                 (repeat :tag "List of characters" character))
-  ;; Reverted.
-;;  :version "24.1"                    ; changed to nil (bug#8027)
   :group 'shell)
 
   :group 'shell)
 
-(defvar shell-file-name-chars
+(defcustom shell-file-name-chars
   (if (memq system-type '(ms-dos windows-nt cygwin))
       "~/A-Za-z0-9_^$!#%&{}@`'.,:()-"
     "[]~/A-Za-z0-9+@:_.$#%,={}-")
   "String of characters valid in a file name.
 This variable is used to initialize `comint-file-name-chars' in the
   (if (memq system-type '(ms-dos windows-nt cygwin))
       "~/A-Za-z0-9_^$!#%&{}@`'.,:()-"
     "[]~/A-Za-z0-9+@:_.$#%,={}-")
   "String of characters valid in a file name.
 This variable is used to initialize `comint-file-name-chars' in the
-shell buffer.  The value may depend on the operating system or shell.
-
-This is a fine thing to set in your `.emacs' file.")
+shell buffer.  The value may depend on the operating system or shell."
+  :type 'string
+  :group 'shell)
 
 
-(defvar shell-file-name-quote-list
+(defcustom shell-file-name-quote-list
   (if (memq system-type '(ms-dos windows-nt))
       nil
     (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
   (if (memq system-type '(ms-dos windows-nt))
       nil
     (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.
-
-This is a fine thing to set in your `.emacs' file.")
+shell buffer.  The value may depend on the operating system or shell."
+  :type '(repeat character)
+  :group 'shell)
 
 
-(defvar shell-dynamic-complete-functions
+(defcustom shell-dynamic-complete-functions
   '(comint-c-a-p-replace-by-expanded-history
     shell-environment-variable-completion
     shell-command-completion
   '(comint-c-a-p-replace-by-expanded-history
     shell-environment-variable-completion
     shell-command-completion
@@ -193,9 +187,9 @@ This is a fine thing to set in your `.emacs' file.")
     comint-filename-completion)
   "List of functions called to perform completion.
 This variable is used to initialize `comint-dynamic-complete-functions' in the
     comint-filename-completion)
   "List of functions called to perform completion.
 This variable is used to initialize `comint-dynamic-complete-functions' in the
-shell buffer.
-
-This is a fine thing to set in your `.emacs' file.")
+shell buffer."
+  :type '(repeat function)
+  :group 'shell)
 
 (defcustom shell-command-regexp "[^;&|\n]+"
   "Regexp to match a single command within a pipeline.
 
 (defcustom shell-command-regexp "[^;&|\n]+"
   "Regexp to match a single command within a pipeline.
@@ -372,8 +366,57 @@ Thus, this does not include the shell's current directory.")
 
 ;;; Basic Procedures
 
 
 ;;; Basic Procedures
 
-(defun shell-parse-pcomplete-arguments ()
+(defun shell--unquote&requote-argument (qstr &optional upos)
+  (unless upos (setq upos 0))
+  (let* ((qpos 0)
+         (dquotes nil)
+         (ustrs '())
+         (re (concat
+              "[\"']"
+              "\\|\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)"
+              "\\|{\\(?1:[^{}]+\\)}\\)"
+              (when (memq system-type '(ms-dos windows-nt))
+                "\\|%\\(?1:[^\\\\/]*\\)%")
+              (when comint-file-name-quote-list
+                "\\|\\\\\\(.\\)")))
+         (qupos nil)
+         (push (lambda (str end)
+                 (push str ustrs)
+                 (setq upos (- upos (length str)))
+                 (unless (or qupos (> upos 0))
+                   (setq qupos (if (< end 0) (- end) (+ upos end))))))
+         match)
+    (while (setq match (string-match re qstr qpos))
+      (funcall push (substring qstr qpos match) match)
+      (cond
+       ((match-beginning 2) (funcall push (match-string 2 qstr) (match-end 0)))
+       ((match-beginning 1) (funcall push (getenv (match-string 1 qstr))
+                                     (- (match-end 0))))
+       ((eq (aref qstr match) ?\") (setq dquotes (not dquotes)))
+       ((eq (aref qstr match) ?\')
+        (cond
+         (dquotes (funcall push "'" (match-end 0)))
+         ((< match (1+ (length qstr)))
+          (let ((end (string-match "'" qstr (1+ match))))
+            (funcall push (substring qstr (1+ match) end)
+                     (or end (length qstr)))))
+         (t nil)))
+       (t (error "Unexpected case in shell--unquote&requote-argument!")))
+      (setq qpos (match-end 0)))
+    (funcall push (substring qstr qpos) (length qstr))
+    (list (mapconcat #'identity (nreverse ustrs) "")
+          qupos #'comint-quote-filename)))
+
+(defun shell--unquote-argument (str)
+  (car (shell--unquote&requote-argument str)))
+(defun shell--requote-argument (upos qstr)
+  ;; See `completion-table-with-quoting'.
+  (let ((res (shell--unquote&requote-argument qstr upos)))
+    (cons (nth 1 res) (nth 2 res))))
+
+(defun shell--parse-pcomplete-arguments ()
   "Parse whitespace separated arguments in the current region."
   "Parse whitespace separated arguments in the current region."
+  ;; FIXME: share code with shell--unquote&requote-argument.
   (let ((begin (save-excursion (shell-backward-command 1) (point)))
        (end (point))
        begins args)
   (let ((begin (save-excursion (shell-backward-command 1) (point)))
        (end (point))
        begins args)
@@ -394,14 +437,15 @@ Thus, this does not include the shell's current directory.")
             (cond
              ((match-beginning 3)       ;Backslash escape.
               (push (cond
             (cond
              ((match-beginning 3)       ;Backslash escape.
               (push (cond
-                     ((null pcomplete-arg-quote-list)
+                     ((null comint-file-name-quote-list)
                       (goto-char (match-beginning 3)) "\\")
                      ((= (match-beginning 3) (match-end 3)) "\\")
                      (t (match-string 3)))
                     arg))
              ((match-beginning 2)       ;Double quote.
                       (goto-char (match-beginning 3)) "\\")
                      ((= (match-beginning 3) (match-end 3)) "\\")
                      (t (match-string 3)))
                     arg))
              ((match-beginning 2)       ;Double quote.
-              (push (replace-regexp-in-string
-                     "\\\\\\(.\\)" "\\1" (match-string 2))
+              (push (if (null comint-file-name-quote-list) (match-string 2)
+                      (replace-regexp-in-string
+                       "\\\\\\(.\\)" "\\1" (match-string 2)))
                     arg))
              ((match-beginning 1)       ;Single quote.
               (push (match-string 1) arg))
                     arg))
              ((match-beginning 1)       ;Single quote.
               (push (match-string 1) arg))
@@ -429,10 +473,10 @@ Shell buffers.  It implements `shell-completion-execonly' for
        shell-file-name-quote-list)
   (set (make-local-variable 'comint-dynamic-complete-functions)
        shell-dynamic-complete-functions)
        shell-file-name-quote-list)
   (set (make-local-variable 'comint-dynamic-complete-functions)
        shell-dynamic-complete-functions)
+  (setq-local comint-unquote-function #'shell--unquote-argument)
+  (setq-local comint-requote-function #'shell--requote-argument)
   (set (make-local-variable 'pcomplete-parse-arguments-function)
   (set (make-local-variable 'pcomplete-parse-arguments-function)
-       #'shell-parse-pcomplete-arguments)
-  (set (make-local-variable 'pcomplete-arg-quote-list)
-       comint-file-name-quote-list)
+       #'shell--parse-pcomplete-arguments)
   (set (make-local-variable 'pcomplete-termination-string)
        (cond ((not comint-completion-addsuffix) "")
              ((stringp comint-completion-addsuffix)
   (set (make-local-variable 'pcomplete-termination-string)
        (cond ((not comint-completion-addsuffix) "")
              ((stringp comint-completion-addsuffix)
@@ -1193,7 +1237,7 @@ Returns non-nil if successful."
              (variables (mapcar (lambda (x)
                                   (substring x 0 (string-match "=" x)))
                                 process-environment))
              (variables (mapcar (lambda (x)
                                   (substring x 0 (string-match "=" x)))
                                 process-environment))
-             (suffix (case (char-before start) (?\{ "}") (?\( ")") (t ""))))
+             (suffix (pcase (char-before start) (?\{ "}") (?\( ")") (_ ""))))
         (list start end variables
               :exit-function
               (lambda (s finished)
         (list start end variables
               :exit-function
               (lambda (s finished)