LCOV - code coverage report
Current view: top level - lisp - shell.el (source / functions) Hit Total Coverage
Test: tramp-tests-after.info Lines: 110 534 20.6 %
Date: 2017-08-30 10:12:24 Functions: 7 44 15.9 %

          Line data    Source code
       1             : ;;; shell.el --- specialized comint.el for running the shell -*- lexical-binding: t -*-
       2             : 
       3             : ;; Copyright (C) 1988, 1993-1997, 2000-2017 Free Software Foundation,
       4             : ;; Inc.
       5             : 
       6             : ;; Author: Olin Shivers <shivers@cs.cmu.edu>
       7             : ;;      Simon Marshall <simon@gnu.org>
       8             : ;; Maintainer: emacs-devel@gnu.org
       9             : ;; Keywords: processes
      10             : 
      11             : ;; This file is part of GNU Emacs.
      12             : 
      13             : ;; GNU Emacs is free software: you can redistribute it and/or modify
      14             : ;; it under the terms of the GNU General Public License as published by
      15             : ;; the Free Software Foundation, either version 3 of the License, or
      16             : ;; (at your option) any later version.
      17             : 
      18             : ;; GNU Emacs is distributed in the hope that it will be useful,
      19             : ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
      20             : ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21             : ;; GNU General Public License for more details.
      22             : 
      23             : ;; You should have received a copy of the GNU General Public License
      24             : ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
      25             : 
      26             : ;;; Commentary:
      27             : 
      28             : ;; This file defines a shell-in-a-buffer package (shell mode) built on
      29             : ;; top of comint mode.  This is actually cmushell with things renamed
      30             : ;; to replace its counterpart in Emacs 18.  cmushell is more
      31             : ;; featureful, robust, and uniform than the Emacs 18 version.
      32             : 
      33             : ;; Since this mode is built on top of the general command-interpreter-in-
      34             : ;; a-buffer mode (comint mode), it shares a common base functionality,
      35             : ;; and a common set of bindings, with all modes derived from comint mode.
      36             : ;; This makes these modes easier to use.
      37             : 
      38             : ;; For documentation on the functionality provided by comint mode, and
      39             : ;; the hooks available for customizing it, see the file comint.el.
      40             : ;; For further information on shell mode, see the comments below.
      41             : 
      42             : ;; Needs fixin:
      43             : ;; When sending text from a source file to a subprocess, the process-mark can
      44             : ;; move off the window, so you can lose sight of the process interactions.
      45             : ;; Maybe I should ensure the process mark is in the window when I send
      46             : ;; text to the process? Switch selectable?
      47             : 
      48             : ;; YOUR .EMACS FILE
      49             : ;;=============================================================================
      50             : ;; Some suggestions for your init file.
      51             : ;;
      52             : ;; ;; Define M-# to run some strange command:
      53             : ;; (eval-after-load "shell"
      54             : ;;  '(define-key shell-mode-map "\M-#" 'shells-dynamic-spell))
      55             : 
      56             : ;; Brief Command Documentation:
      57             : ;;============================================================================
      58             : ;; Comint Mode Commands: (common to shell and all comint-derived modes)
      59             : ;;
      60             : ;; m-p     comint-previous-input           Cycle backwards in input history
      61             : ;; m-n     comint-next-input               Cycle forwards
      62             : ;; m-r     comint-previous-matching-input  Previous input matching a regexp
      63             : ;; m-s     comint-next-matching-input      Next input that matches
      64             : ;; m-c-l   comint-show-output              Show last batch of process output
      65             : ;; return  comint-send-input
      66             : ;; c-d     comint-delchar-or-maybe-eof     Delete char unless at end of buff.
      67             : ;; c-c c-a comint-bol                      Beginning of line; skip prompt
      68             : ;; c-c c-u comint-kill-input               ^u
      69             : ;; c-c c-w backward-kill-word              ^w
      70             : ;; c-c c-c comint-interrupt-subjob         ^c
      71             : ;; c-c c-z comint-stop-subjob              ^z
      72             : ;; c-c c-\ comint-quit-subjob              ^\
      73             : ;; c-c c-o comint-delete-output            Delete last batch of process output
      74             : ;; c-c c-r comint-show-output              Show last batch of process output
      75             : ;; c-c c-l comint-dynamic-list-input-ring  List input history
      76             : ;;         send-invisible                  Read line w/o echo & send to proc
      77             : ;;         comint-continue-subjob          Useful if you accidentally suspend
      78             : ;;                                              top-level job
      79             : ;; comint-mode-hook is the comint mode hook.
      80             : 
      81             : ;; Shell Mode Commands:
      82             : ;;         shell                        Fires up the shell process
      83             : ;; tab     completion-at-point          Complete filename/command/history
      84             : ;; m-?     comint-dynamic-list-filename-completions
      85             : ;;                                      List completions in help buffer
      86             : ;; c-c c-f shell-forward-command        Forward a shell command
      87             : ;; c-c c-b shell-backward-command       Backward a shell command
      88             : ;;         dirs                         Resync the buffer's dir stack
      89             : ;;         shell-dirtrack-mode          Turn dir tracking on/off
      90             : ;;         comint-strip-ctrl-m          Remove trailing ^Ms from output
      91             : ;;
      92             : ;; The shell mode hook is shell-mode-hook
      93             : ;; comint-prompt-regexp is initialized to shell-prompt-pattern, for backwards
      94             : ;; compatibility.
      95             : 
      96             : ;; Read the rest of this file for more information.
      97             : 
      98             : ;;; Code:
      99             : 
     100             : (require 'comint)
     101             : (require 'pcomplete)
     102             : 
     103             : ;;; Customization and Buffer Variables
     104             : 
     105             : (defgroup shell nil
     106             :   "Running shell from within Emacs buffers."
     107             :   :group 'processes
     108             :   :group 'unix)
     109             : 
     110             : (defgroup shell-directories nil
     111             :   "Directory support in shell mode."
     112             :   :group 'shell)
     113             : 
     114             : ;; Unused.
     115             : ;;; (defgroup shell-faces nil
     116             : ;;;   "Faces in shell buffers."
     117             : ;;;   :group 'shell)
     118             : 
     119             : ;;;###autoload
     120             : (defcustom shell-dumb-shell-regexp (purecopy "cmd\\(proxy\\)?\\.exe")
     121             :   "Regexp to match shells that don't save their command history, and
     122             : don't handle the backslash as a quote character.  For shells that
     123             : match this regexp, Emacs will write out the command history when the
     124             : shell finishes, and won't remove backslashes when it unquotes shell
     125             : arguments."
     126             :   :type 'regexp
     127             :   :group 'shell)
     128             : 
     129             : (defcustom shell-prompt-pattern "^[^#$%>\n]*[#$%>] *"
     130             :   "Regexp to match prompts in the inferior shell.
     131             : Defaults to \"^[^#$%>\\n]*[#$%>] *\", which works pretty well.
     132             : This variable is used to initialize `comint-prompt-regexp' in the
     133             : shell buffer.
     134             : 
     135             : If `comint-use-prompt-regexp' is nil, then this variable is only used
     136             : to determine paragraph boundaries.  See Info node `Shell Prompts' for
     137             : how Shell mode treats paragraphs.
     138             : 
     139             : The pattern should probably not match more than one line.  If it does,
     140             : Shell mode may become confused trying to distinguish prompt from input
     141             : on lines which don't start with a prompt."
     142             :   :type 'regexp
     143             :   :group 'shell)
     144             : 
     145             : (defcustom shell-completion-fignore nil
     146             :   "List of suffixes to be disregarded during file/command completion.
     147             : This variable is used to initialize `comint-completion-fignore' in the shell
     148             : buffer.  The default is nil, for compatibility with most shells.
     149             : Some people like (\"~\" \"#\" \"%\")."
     150             :   :type '(repeat (string :tag "Suffix"))
     151             :   :group 'shell)
     152             : 
     153             : (defcustom shell-delimiter-argument-list '(?\| ?& ?< ?> ?\( ?\) ?\;)
     154             :   "List of characters to recognize as separate arguments.
     155             : This variable is used to initialize `comint-delimiter-argument-list' in the
     156             : shell buffer.  The value may depend on the operating system or shell."
     157             :   :type '(choice (const nil)
     158             :                  (repeat :tag "List of characters" character))
     159             :   :group 'shell)
     160             : 
     161             : (defcustom shell-file-name-chars
     162             :   (if (memq system-type '(ms-dos windows-nt cygwin))
     163             :       "~/A-Za-z0-9_^$!#%&{}@`'.,:()-"
     164             :     "[]~/A-Za-z0-9+@:_.$#%,={}-")
     165             :   "String of characters valid in a file name.
     166             : This variable is used to initialize `comint-file-name-chars' in the
     167             : shell buffer.  The value may depend on the operating system or shell."
     168             :   :type 'string
     169             :   :group 'shell)
     170             : 
     171             : (defcustom shell-file-name-quote-list
     172             :   (if (memq system-type '(ms-dos windows-nt))
     173             :       nil
     174             :     (append shell-delimiter-argument-list '(?\s ?$ ?\* ?\! ?\" ?\' ?\` ?\# ?\\)))
     175             :   "List of characters to quote when in a file name.
     176             : This variable is used to initialize `comint-file-name-quote-list' in the
     177             : shell buffer.  The value may depend on the operating system or shell."
     178             :   :type '(repeat character)
     179             :   :group 'shell)
     180             : 
     181             : (defcustom shell-dynamic-complete-functions
     182             :   '(comint-c-a-p-replace-by-expanded-history
     183             :     shell-environment-variable-completion
     184             :     shell-command-completion
     185             :     shell-c-a-p-replace-by-expanded-directory
     186             :     pcomplete-completions-at-point
     187             :     shell-filename-completion
     188             :     comint-filename-completion)
     189             :   "List of functions called to perform completion.
     190             : This variable is used to initialize `comint-dynamic-complete-functions' in the
     191             : shell buffer."
     192             :   :type '(repeat function)
     193             :   :group 'shell)
     194             : 
     195             : (defcustom shell-command-regexp "[^;&|\n]+"
     196             :   "Regexp to match a single command within a pipeline.
     197             : This is used for directory tracking and does not do a perfect job."
     198             :   :type 'regexp
     199             :   :group 'shell)
     200             : 
     201             : (defcustom shell-command-separator-regexp "[;&|\n \t]*"
     202             :   "Regexp to match a single command within a pipeline.
     203             : This is used for directory tracking and does not do a perfect job."
     204             :   :type 'regexp
     205             :   :group 'shell)
     206             : 
     207             : (defcustom shell-completion-execonly t
     208             :   "If non-nil, use executable files only for completion candidates.
     209             : This mirrors the optional behavior of tcsh.
     210             : 
     211             : Detecting executability of files may slow command completion considerably."
     212             :   :type 'boolean
     213             :   :group 'shell)
     214             : 
     215             : (defcustom shell-popd-regexp "popd"
     216             :   "Regexp to match subshell commands equivalent to popd."
     217             :   :type 'regexp
     218             :   :group 'shell-directories)
     219             : 
     220             : (defcustom shell-pushd-regexp "pushd"
     221             :   "Regexp to match subshell commands equivalent to pushd."
     222             :   :type 'regexp
     223             :   :group 'shell-directories)
     224             : 
     225             : (defcustom shell-pushd-tohome nil
     226             :   "If non-nil, make pushd with no arg behave as \"pushd ~\" (like cd).
     227             : This mirrors the optional behavior of tcsh."
     228             :   :type 'boolean
     229             :   :group 'shell-directories)
     230             : 
     231             : (defcustom shell-pushd-dextract nil
     232             :   "If non-nil, make \"pushd +n\" pop the nth dir to the stack top.
     233             : This mirrors the optional behavior of tcsh."
     234             :   :type 'boolean
     235             :   :group 'shell-directories)
     236             : 
     237             : (defcustom shell-pushd-dunique nil
     238             :   "If non-nil, make pushd only add unique directories to the stack.
     239             : This mirrors the optional behavior of tcsh."
     240             :   :type 'boolean
     241             :   :group 'shell-directories)
     242             : 
     243             : (defcustom shell-cd-regexp "cd"
     244             :   "Regexp to match subshell commands equivalent to cd."
     245             :   :type 'regexp
     246             :   :group 'shell-directories)
     247             : 
     248             : (defcustom shell-chdrive-regexp
     249             :   (if (memq system-type '(ms-dos windows-nt))
     250             :       ; NetWare allows the five chars between upper and lower alphabetics.
     251             :       "[]a-zA-Z^_`\\[\\\\]:"
     252             :     nil)
     253             :   "If non-nil, is regexp used to track drive changes."
     254             :   :type '(choice regexp
     255             :                  (const nil))
     256             :   :group 'shell-directories)
     257             : 
     258             : (defcustom shell-dirtrack-verbose t
     259             :   "If non-nil, show the directory stack following directory change.
     260             : This is effective only if directory tracking is enabled.
     261             : The `dirtrack' package provides an alternative implementation of this feature -
     262             : see the function `dirtrack-mode'."
     263             :   :type 'boolean
     264             :   :group 'shell-directories)
     265             : 
     266             : (defcustom explicit-shell-file-name nil
     267             :   "If non-nil, is file name to use for explicitly requested inferior shell.
     268             : When nil, such interactive shell sessions fallback to using either
     269             : the shell specified in $ESHELL or in `shell-file-name'."
     270             :   :type '(choice (const :tag "None" nil) file)
     271             :   :group 'shell)
     272             : 
     273             : ;; Note: There are no explicit references to the variable `explicit-csh-args'.
     274             : ;; It is used implicitly by M-x shell when the shell is `csh'.
     275             : (defcustom explicit-csh-args
     276             :   (if (eq system-type 'hpux)
     277             :       ;; -T persuades HP's csh not to think it is smarter
     278             :       ;; than us about what terminal modes to use.
     279             :       '("-i" "-T")
     280             :     '("-i"))
     281             :   "Args passed to inferior shell by \\[shell], if the shell is csh.
     282             : Value is a list of strings, which may be nil."
     283             :   :type '(repeat (string :tag "Argument"))
     284             :   :group 'shell)
     285             : 
     286             : ;; Note: There are no explicit references to the variable `explicit-bash-args'.
     287             : ;; It is used implicitly by M-x shell when the interactive shell is `bash'.
     288             : (defcustom explicit-bash-args
     289             :   ;; Tell bash not to use readline.  It's safe to assume --noediting now,
     290             :   ;; as it was introduced in 1996 in Bash version 2.
     291             :   '("--noediting" "-i")
     292             :   "Args passed to inferior shell by \\[shell], if the shell is bash.
     293             : Value is a list of strings, which may be nil."
     294             :   :type '(repeat (string :tag "Argument"))
     295             :   :group 'shell)
     296             : 
     297             : (defcustom shell-input-autoexpand 'history
     298             :   "If non-nil, expand input command history references on completion.
     299             : This mirrors the optional behavior of tcsh (its autoexpand and histlit).
     300             : 
     301             : If the value is `input', then the expansion is seen on input.
     302             : If the value is `history', then the expansion is only when inserting
     303             : into the buffer's input ring.  See also `comint-magic-space' and
     304             : `comint-dynamic-complete-functions'.
     305             : 
     306             : This variable supplies a default for `comint-input-autoexpand',
     307             : for Shell mode only."
     308             :   :type '(choice (const :tag "off" nil)
     309             :                  (const input)
     310             :                  (const history)
     311             :                  (const :tag "on" t))
     312             :   :group 'shell)
     313             : 
     314             : (defvar shell-dirstack nil
     315             :   "List of directories saved by pushd in this buffer's shell.
     316             : Thus, this does not include the shell's current directory.")
     317             : 
     318             : (defvar shell-dirtrackp t
     319             :   "Non-nil in a shell buffer means directory tracking is enabled.")
     320             : 
     321             : (defvar shell-last-dir nil
     322             :   "Keep track of last directory for ksh `cd -' command.")
     323             : 
     324             : (defvar shell-dirstack-query nil
     325             :   "Command used by `shell-resync-dirs' to query the shell.")
     326             : 
     327             : (defvar shell-mode-map
     328             :   (let ((map (nconc (make-sparse-keymap) comint-mode-map)))
     329             :     (define-key map "\C-c\C-f" 'shell-forward-command)
     330             :     (define-key map "\C-c\C-b" 'shell-backward-command)
     331             :     (define-key map "\t" 'completion-at-point)
     332             :     (define-key map (kbd "M-RET") 'shell-resync-dirs)
     333             :     (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
     334             :     (define-key map [menu-bar completion]
     335             :       (cons "Complete"
     336             :             (copy-keymap (lookup-key comint-mode-map [menu-bar completion]))))
     337             :     (define-key-after (lookup-key map [menu-bar completion])
     338             :       [complete-env-variable] '("Complete Env. Variable Name" .
     339             :                                 shell-dynamic-complete-environment-variable)
     340             :       'complete-file)
     341             :     (define-key-after (lookup-key map [menu-bar completion])
     342             :       [expand-directory] '("Expand Directory Reference" .
     343             :                            shell-replace-by-expanded-directory)
     344             :       'complete-expand)
     345             :     map))
     346             : 
     347             : (defcustom shell-mode-hook '()
     348             :   "Hook for customizing Shell mode."
     349             :   :type 'hook
     350             :   :group 'shell)
     351             : 
     352             : (defvar shell-font-lock-keywords
     353             :   '(("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-comment-face)
     354             :     ("^[^ \t\n]+:.*" . font-lock-string-face)
     355             :     ("^\\[[1-9][0-9]*\\]" . font-lock-string-face))
     356             :   "Additional expressions to highlight in Shell mode.")
     357             : 
     358             : ;;; Basic Procedures
     359             : 
     360             : (defun shell--unquote&requote-argument (qstr &optional upos)
     361           0 :   (unless upos (setq upos 0))
     362           0 :   (let* ((qpos 0)
     363             :          (dquotes nil)
     364             :          (ustrs '())
     365           0 :          (re (concat
     366             :               "[\"']"
     367             :               "\\|\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)"
     368             :               "\\|{\\(?1:[^{}]+\\)}\\)"
     369           0 :               (when (memq system-type '(ms-dos windows-nt))
     370           0 :                 "\\|%\\(?1:[^\\\\/]*\\)%")
     371           0 :               (when comint-file-name-quote-list
     372           0 :                 "\\|\\\\\\(.\\)")))
     373             :          (qupos nil)
     374             :          (push (lambda (str end)
     375           0 :                  (push str ustrs)
     376           0 :                  (setq upos (- upos (length str)))
     377           0 :                  (unless (or qupos (> upos 0))
     378           0 :                    (setq qupos (if (< end 0) (- end) (+ upos end))))))
     379             :          match)
     380           0 :     (while (setq match (string-match re qstr qpos))
     381           0 :       (funcall push (substring qstr qpos match) match)
     382           0 :       (cond
     383           0 :        ((match-beginning 2) (funcall push (match-string 2 qstr) (match-end 0)))
     384           0 :        ((match-beginning 1) (funcall push (getenv (match-string 1 qstr))
     385           0 :                                      (- (match-end 0))))
     386           0 :        ((eq (aref qstr match) ?\") (setq dquotes (not dquotes)))
     387           0 :        ((eq (aref qstr match) ?\')
     388           0 :         (cond
     389             :          ;; Treat single quote as text if inside double quotes.
     390           0 :          (dquotes (funcall push "'" (match-end 0)))
     391           0 :          ((< (1+ match) (length qstr))
     392           0 :           (let ((end (string-match "'" qstr (1+ match))))
     393           0 :             (unless end
     394           0 :               (setq end (length qstr))
     395           0 :               (set-match-data (list match (length qstr))))
     396           0 :             (funcall push (substring qstr (1+ match) end) end)))
     397             :          ;; Ignore if at the end of string.
     398           0 :          (t nil)))
     399           0 :        (t (error "Unexpected case in shell--unquote&requote-argument!")))
     400           0 :       (setq qpos (match-end 0)))
     401           0 :     (funcall push (substring qstr qpos) (length qstr))
     402           0 :     (list (mapconcat #'identity (nreverse ustrs) "")
     403           0 :           qupos #'comint-quote-filename)))
     404             : 
     405             : (defun shell--unquote-argument (str)
     406           0 :   (car (shell--unquote&requote-argument str)))
     407             : (defun shell--requote-argument (upos qstr)
     408             :   ;; See `completion-table-with-quoting'.
     409           0 :   (let ((res (shell--unquote&requote-argument qstr upos)))
     410           0 :     (cons (nth 1 res) (nth 2 res))))
     411             : 
     412             : (defun shell--parse-pcomplete-arguments ()
     413             :   "Parse whitespace separated arguments in the current region."
     414             :   ;; FIXME: share code with shell--unquote&requote-argument.
     415           0 :   (let ((begin (save-excursion (shell-backward-command 1) (point)))
     416           0 :         (end (point))
     417             :         begins args)
     418           0 :     (save-excursion
     419           0 :       (goto-char begin)
     420           0 :       (while (< (point) end)
     421           0 :         (skip-chars-forward " \t\n")
     422           0 :         (push (point) begins)
     423           0 :         (let ((arg ()))
     424           0 :           (while (looking-at
     425           0 :                   (eval-when-compile
     426           1 :                     (concat
     427             :                      "\\(?:[^\s\t\n\\\"']+"
     428             :                      "\\|'\\([^']*\\)'?"
     429             :                      "\\|\"\\(\\(?:[^\"\\]\\|\\\\.\\)*\\)\"?"
     430           1 :                      "\\|\\\\\\(\\(?:.\\|\n\\)?\\)\\)")))
     431           0 :             (goto-char (match-end 0))
     432           0 :             (cond
     433           0 :              ((match-beginning 3)       ;Backslash escape.
     434           0 :               (push (cond
     435           0 :                      ((null comint-file-name-quote-list)
     436           0 :                       (goto-char (match-beginning 3)) "\\")
     437           0 :                      ((= (match-beginning 3) (match-end 3)) "\\")
     438           0 :                      (t (match-string 3)))
     439           0 :                     arg))
     440           0 :              ((match-beginning 2)       ;Double quote.
     441           0 :               (push (if (null comint-file-name-quote-list) (match-string 2)
     442           0 :                       (replace-regexp-in-string
     443           0 :                        "\\\\\\(.\\)" "\\1" (match-string 2)))
     444           0 :                     arg))
     445           0 :              ((match-beginning 1)       ;Single quote.
     446           0 :               (push (match-string 1) arg))
     447           0 :              (t (push (match-string 0) arg))))
     448           0 :           (push (mapconcat #'identity (nreverse arg) "") args)))
     449           0 :       (cons (nreverse args) (nreverse begins)))))
     450             : 
     451             : (defun shell-command-completion-function ()
     452             :   "Completion function for shell command names.
     453             : This is the value of `pcomplete-command-completion-function' for
     454             : Shell buffers.  It implements `shell-completion-execonly' for
     455             : `pcomplete' completion."
     456           0 :   (pcomplete-here (pcomplete-entries nil
     457           0 :                                      (if shell-completion-execonly
     458           0 :                                          'file-executable-p))))
     459             : 
     460             : (defun shell-completion-vars ()
     461             :   "Setup completion vars for `shell-mode' and `read-shell-command'."
     462          11 :   (set (make-local-variable 'comint-completion-fignore)
     463          11 :        shell-completion-fignore)
     464          11 :   (set (make-local-variable 'comint-delimiter-argument-list)
     465          11 :        shell-delimiter-argument-list)
     466          11 :   (set (make-local-variable 'comint-file-name-chars) shell-file-name-chars)
     467          11 :   (set (make-local-variable 'comint-file-name-quote-list)
     468          11 :        shell-file-name-quote-list)
     469          11 :   (set (make-local-variable 'comint-dynamic-complete-functions)
     470          11 :        shell-dynamic-complete-functions)
     471          11 :   (setq-local comint-unquote-function #'shell--unquote-argument)
     472          11 :   (setq-local comint-requote-function #'shell--requote-argument)
     473          11 :   (set (make-local-variable 'pcomplete-parse-arguments-function)
     474          11 :        #'shell--parse-pcomplete-arguments)
     475          11 :   (set (make-local-variable 'pcomplete-termination-string)
     476          11 :        (cond ((not comint-completion-addsuffix) "")
     477          11 :              ((stringp comint-completion-addsuffix)
     478           0 :               comint-completion-addsuffix)
     479          11 :              ((not (consp comint-completion-addsuffix)) " ")
     480          11 :              (t (cdr comint-completion-addsuffix))))
     481          11 :   (set (make-local-variable 'pcomplete-command-completion-function)
     482          11 :        #'shell-command-completion-function)
     483             :   ;; Don't use pcomplete's defaulting mechanism, rely on
     484             :   ;; shell-dynamic-complete-functions instead.
     485          11 :   (set (make-local-variable 'pcomplete-default-completion-function) #'ignore)
     486          11 :   (setq comint-input-autoexpand shell-input-autoexpand)
     487             :   ;; Not needed in shell-mode because it's inherited from comint-mode, but
     488             :   ;; placed here for read-shell-command.
     489          11 :   (add-hook 'completion-at-point-functions 'comint-completion-at-point nil t))
     490             : 
     491             : (put 'shell-mode 'mode-class 'special)
     492             : 
     493             : (define-derived-mode shell-mode comint-mode "Shell"
     494             :   "Major mode for interacting with an inferior shell.\\<shell-mode-map>
     495             : \\[comint-send-input] after the end of the process' output sends the text from
     496             :     the end of process to the end of the current line.
     497             : \\[comint-send-input] before end of process output copies the current line minus the prompt to
     498             :     the end of the buffer and sends it (\\[comint-copy-old-input] just copies the current line).
     499             : \\[send-invisible] reads a line of text without echoing it, and sends it to
     500             :     the shell.  This is useful for entering passwords.  Or, add the function
     501             :     `comint-watch-for-password-prompt' to `comint-output-filter-functions'.
     502             : 
     503             : If you want to make multiple shell buffers, rename the `*shell*' buffer
     504             : using \\[rename-buffer] or \\[rename-uniquely] and start a new shell.
     505             : 
     506             : If you want to make shell buffers limited in length, add the function
     507             : `comint-truncate-buffer' to `comint-output-filter-functions'.
     508             : 
     509             : If you accidentally suspend your process, use \\[comint-continue-subjob]
     510             : to continue it.
     511             : 
     512             : `cd', `pushd' and `popd' commands given to the shell are watched by Emacs to
     513             : keep this buffer's default directory the same as the shell's working directory.
     514             : While directory tracking is enabled, the shell's working directory is displayed
     515             : by \\[list-buffers] or \\[mouse-buffer-menu] in the `File' field.
     516             : \\[dirs] queries the shell and resyncs Emacs's idea of what the current
     517             :     directory stack is.
     518             : \\[shell-dirtrack-mode] turns directory tracking on and off.
     519             : \(The `dirtrack' package provides an alternative implementation of this
     520             : feature - see the function `dirtrack-mode'.)
     521             : 
     522             : \\{shell-mode-map}
     523             : Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
     524             : `shell-mode-hook' (in that order).  Before each input, the hooks on
     525             : `comint-input-filter-functions' are run.  After each shell output, the hooks
     526             : on `comint-output-filter-functions' are run.
     527             : 
     528             : Variables `shell-cd-regexp', `shell-chdrive-regexp', `shell-pushd-regexp'
     529             : and `shell-popd-regexp' are used to match their respective commands,
     530             : while `shell-pushd-tohome', `shell-pushd-dextract' and `shell-pushd-dunique'
     531             : control the behavior of the relevant command.
     532             : 
     533             : Variables `comint-completion-autolist', `comint-completion-addsuffix',
     534             : `comint-completion-recexact' and `comint-completion-fignore' control the
     535             : behavior of file name, command name and variable name completion.  Variable
     536             : `shell-completion-execonly' controls the behavior of command name completion.
     537             : Variable `shell-completion-fignore' is used to initialize the value of
     538             : `comint-completion-fignore'.
     539             : 
     540             : Variables `comint-input-ring-file-name' and `comint-input-autoexpand' control
     541             : the initialization of the input ring history, and history expansion.
     542             : 
     543             : Variables `comint-output-filter-functions', a hook, and
     544             : `comint-scroll-to-bottom-on-input' and `comint-scroll-to-bottom-on-output'
     545             : control whether input and output cause the window to scroll to the end of the
     546             : buffer."
     547          11 :   (setq comint-prompt-regexp shell-prompt-pattern)
     548          11 :   (shell-completion-vars)
     549          11 :   (setq-local paragraph-separate "\\'")
     550          11 :   (setq-local paragraph-start comint-prompt-regexp)
     551          11 :   (setq-local font-lock-defaults '(shell-font-lock-keywords t))
     552          11 :   (setq-local shell-dirstack nil)
     553          11 :   (setq-local shell-last-dir nil)
     554             :   ;; People expect Shell mode to keep the last line of output at
     555             :   ;; window bottom.
     556          11 :   (setq-local scroll-conservatively 101)
     557          11 :   (shell-dirtrack-mode 1)
     558             : 
     559             :   ;; By default, ansi-color applies faces using overlays.  This is
     560             :   ;; very inefficient in Shell buffers (e.g. Bug#10835).  We use a
     561             :   ;; custom `ansi-color-apply-face-function' to convert color escape
     562             :   ;; sequences into `font-lock-face' properties.
     563          11 :   (setq-local ansi-color-apply-face-function #'shell-apply-ansi-color)
     564          11 :   (shell-reapply-ansi-color)
     565             : 
     566             :   ;; This is not really correct, since the shell buffer does not really
     567             :   ;; edit this directory.  But it is useful in the buffer list and menus.
     568          11 :   (setq list-buffers-directory (expand-file-name default-directory))
     569             :   ;; shell-dependent assignments.
     570          11 :   (when (ring-empty-p comint-input-ring)
     571          11 :     (let ((shell (file-name-nondirectory (car
     572          11 :                    (process-command (get-buffer-process (current-buffer))))))
     573          11 :           (hsize (getenv "HISTSIZE")))
     574          11 :       (and (stringp hsize)
     575           0 :            (integerp (setq hsize (string-to-number hsize)))
     576           0 :            (> hsize 0)
     577          11 :            (set (make-local-variable 'comint-input-ring-size) hsize))
     578          11 :       (setq comint-input-ring-file-name
     579          11 :             (or (getenv "HISTFILE")
     580          11 :                 (cond ((string-equal shell "bash") "~/.bash_history")
     581          11 :                       ((string-equal shell "ksh") "~/.sh_history")
     582          11 :                       (t "~/.history"))))
     583          11 :       (if (or (equal comint-input-ring-file-name "")
     584          11 :               (equal (file-truename comint-input-ring-file-name)
     585          11 :                      (file-truename "/dev/null")))
     586          11 :           (setq comint-input-ring-file-name nil))
     587             :       ;; Arrange to write out the input ring on exit, if the shell doesn't
     588             :       ;; do this itself.
     589          11 :       (if (and comint-input-ring-file-name
     590          11 :                (string-match shell-dumb-shell-regexp shell))
     591           0 :           (set-process-sentinel (get-buffer-process (current-buffer))
     592          11 :                                 #'shell-write-history-on-exit))
     593          11 :       (setq shell-dirstack-query
     594          11 :             (cond ((string-equal shell "sh") "pwd")
     595           0 :                   ((string-equal shell "ksh") "echo $PWD ~-")
     596             :                   ;; Bypass any aliases.  TODO all shells could use this.
     597           0 :                   ((string-equal shell "bash") "command dirs")
     598           0 :                   ((string-equal shell "zsh") "dirs -l")
     599          11 :                   (t "dirs")))
     600             :       ;; Bypass a bug in certain versions of bash.
     601          11 :       (when (string-equal shell "bash")
     602           0 :         (add-hook 'comint-preoutput-filter-functions
     603          11 :                   'shell-filter-ctrl-a-ctrl-b nil t)))
     604          11 :     (comint-read-input-ring t)))
     605             : 
     606             : (defun shell-apply-ansi-color (beg end face)
     607             :   "Apply FACE as the ansi-color face for the text between BEG and END."
     608          26 :   (when face
     609           0 :     (put-text-property beg end 'ansi-color-face face)
     610          26 :     (put-text-property beg end 'font-lock-face face)))
     611             : 
     612             : (defun shell-reapply-ansi-color ()
     613             :   "Reapply ansi-color faces to the existing contents of the buffer."
     614          11 :   (save-restriction
     615          11 :     (widen)
     616          11 :     (let* ((pos (point-min))
     617          11 :            (end (or (next-single-property-change pos 'ansi-color-face)
     618          11 :                     (point-max)))
     619             :            face)
     620          22 :       (while end
     621          11 :         (if (setq face (get-text-property pos 'ansi-color-face))
     622           0 :             (put-text-property pos (or end (point-max))
     623          11 :                                'font-lock-face face))
     624          11 :         (setq pos end
     625          11 :               end (next-single-property-change pos 'ansi-color-face))))))
     626             : 
     627             : (defun shell-filter-ctrl-a-ctrl-b (string)
     628             :   "Remove `^A' and `^B' characters from comint output.
     629             : 
     630             : Bash uses these characters as internal quoting characters in its
     631             : prompt.  Due to a bug in some bash versions (including 2.03,
     632             : 2.04, and 2.05b), they may erroneously show up when bash is
     633             : started with the `--noediting' option and Select Graphic
     634             : Rendition (SGR) control sequences (formerly known as ANSI escape
     635             : sequences) are used to color the prompt.
     636             : 
     637             : This function can be put on `comint-preoutput-filter-functions'."
     638           0 :   (if (string-match "[\C-a\C-b]" string)
     639           0 :       (replace-regexp-in-string "[\C-a\C-b]" "" string t t)
     640           0 :     string))
     641             : 
     642             : (defun shell-write-history-on-exit (process event)
     643             :   "Called when the shell process is stopped.
     644             : 
     645             : Writes the input history to a history file
     646             : `comint-input-ring-file-name' using `comint-write-input-ring'
     647             : and inserts a short message in the shell buffer.
     648             : 
     649             : This function is a sentinel watching the shell interpreter process.
     650             : Sentinels will always get the two parameters PROCESS and EVENT."
     651             :   ;; Write history.
     652           0 :   (comint-write-input-ring)
     653           0 :   (let ((buf (process-buffer process)))
     654           0 :     (when (buffer-live-p buf)
     655           0 :       (with-current-buffer buf
     656           0 :         (insert (format "\nProcess %s %s\n" process event))))))
     657             : 
     658             : ;;;###autoload
     659             : (defun shell (&optional buffer)
     660             :   "Run an inferior shell, with I/O through BUFFER (which defaults to `*shell*').
     661             : Interactively, a prefix arg means to prompt for BUFFER.
     662             : If `default-directory' is a remote file name, it is also prompted
     663             : to change if called with a prefix arg.
     664             : 
     665             : If BUFFER exists but shell process is not running, make new shell.
     666             : If BUFFER exists and shell process is running, just switch to BUFFER.
     667             : Program used comes from variable `explicit-shell-file-name',
     668             :  or (if that is nil) from the ESHELL environment variable,
     669             :  or (if that is nil) from `shell-file-name'.
     670             : If a file `~/.emacs_SHELLNAME' exists, or `~/.emacs.d/init_SHELLNAME.sh',
     671             : it is given as initial input (but this may be lost, due to a timing
     672             : error, if the shell discards input when it starts up).
     673             : The buffer is put in Shell mode, giving commands for sending input
     674             : and controlling the subjobs of the shell.  See `shell-mode'.
     675             : See also the variable `shell-prompt-pattern'.
     676             : 
     677             : To specify a coding system for converting non-ASCII characters
     678             : in the input and output to the shell, use \\[universal-coding-system-argument]
     679             : before \\[shell].  You can also specify this with \\[set-buffer-process-coding-system]
     680             : in the shell buffer, after you start the shell.
     681             : The default comes from `process-coding-system-alist' and
     682             : `default-process-coding-system'.
     683             : 
     684             : The shell file name (sans directories) is used to make a symbol name
     685             : such as `explicit-csh-args'.  If that symbol is a variable,
     686             : its value is used as a list of arguments when invoking the shell.
     687             : Otherwise, one argument `-i' is passed to the shell.
     688             : 
     689             : \(Type \\[describe-mode] in the shell buffer for a list of commands.)"
     690             :   (interactive
     691           1 :    (list
     692           1 :     (and current-prefix-arg
     693           0 :          (prog1
     694           0 :              (read-buffer "Shell buffer: "
     695             :                           ;; If the current buffer is an inactive
     696             :                           ;; shell buffer, use it as the default.
     697           0 :                           (if (and (eq major-mode 'shell-mode)
     698           0 :                                    (null (get-buffer-process (current-buffer))))
     699           0 :                               (buffer-name)
     700           0 :                             (generate-new-buffer-name "*shell*")))
     701           0 :            (if (file-remote-p default-directory)
     702             :                ;; It must be possible to declare a local default-directory.
     703             :                ;; FIXME: This can't be right: it changes the default-directory
     704             :                ;; of the current-buffer rather than of the *shell* buffer.
     705           0 :                (setq default-directory
     706           0 :                      (expand-file-name
     707           0 :                       (read-directory-name
     708           0 :                        "Default directory: " default-directory default-directory
     709           1 :                        t nil))))))))
     710           1 :   (setq buffer (if (or buffer (not (derived-mode-p 'shell-mode))
     711           1 :                        (comint-check-proc (current-buffer)))
     712           1 :                    (get-buffer-create (or buffer "*shell*"))
     713             :                  ;; If the current buffer is a dead shell buffer, use it.
     714           1 :                  (current-buffer)))
     715             : 
     716           1 :   (with-current-buffer buffer
     717           1 :     (when (file-remote-p default-directory)
     718             :       ;; Apply connection-local variables.
     719           1 :       (hack-connection-local-variables-apply
     720           1 :        `(:application tramp
     721           1 :          :protocol ,(file-remote-p default-directory 'method)
     722           1 :          :user ,(file-remote-p default-directory 'user)
     723           1 :          :machine ,(file-remote-p default-directory 'host)))
     724             : 
     725             :       ;; On remote hosts, the local `shell-file-name' might be useless.
     726           1 :       (if (and (called-interactively-p 'any)
     727           0 :                (null explicit-shell-file-name)
     728           1 :                (null (getenv "ESHELL")))
     729           0 :           (set (make-local-variable 'explicit-shell-file-name)
     730           0 :                (expand-file-name
     731           0 :                 (file-local-name
     732           0 :                  (read-file-name
     733           0 :                   "Remote shell path: " default-directory shell-file-name
     734           1 :                   t shell-file-name)))))))
     735             : 
     736             :   ;; The buffer's window must be correctly set when we call comint
     737             :   ;; (so that comint sets the COLUMNS env var properly).
     738           1 :   (pop-to-buffer buffer)
     739             :   ;; Rain or shine, BUFFER must be current by now.
     740           1 :   (unless (comint-check-proc buffer)
     741           1 :     (let* ((prog (or explicit-shell-file-name
     742           1 :                      (getenv "ESHELL") shell-file-name))
     743           1 :            (name (file-name-nondirectory prog))
     744           1 :            (startfile (concat "~/.emacs_" name))
     745           1 :            (xargs-name (intern-soft (concat "explicit-" name "-args"))))
     746           1 :       (unless (file-exists-p startfile)
     747           1 :         (setq startfile (concat user-emacs-directory "init_" name ".sh")))
     748           1 :       (apply 'make-comint-in-buffer "shell" buffer prog
     749           1 :              (if (file-exists-p startfile) startfile)
     750           1 :              (if (and xargs-name (boundp xargs-name))
     751           1 :                  (symbol-value xargs-name)
     752           1 :                '("-i")))
     753           1 :       (shell-mode)))
     754           1 :   buffer)
     755             : 
     756             : ;;; Directory tracking
     757             : ;;
     758             : ;; This code provides the shell mode input sentinel
     759             : ;;     SHELL-DIRECTORY-TRACKER
     760             : ;; that tracks cd, pushd, and popd commands issued to the shell, and
     761             : ;; changes the current directory of the shell buffer accordingly.
     762             : ;;
     763             : ;; This is basically a fragile hack, although it's more accurate than
     764             : ;; the version in Emacs 18's shell.el. It has the following failings:
     765             : ;; 1. It doesn't know about the cdpath shell variable.
     766             : ;; 2. It cannot infallibly deal with command sequences, though it does well
     767             : ;;    with these and with ignoring commands forked in another shell with ()s.
     768             : ;; 3. More generally, any complex command is going to throw it. Otherwise,
     769             : ;;    you'd have to build an entire shell interpreter in Emacs Lisp.  Failing
     770             : ;;    that, there's no way to catch shell commands where cd's are buried
     771             : ;;    inside conditional expressions, aliases, and so forth.
     772             : ;;
     773             : ;; The whole approach is a crock. Shell aliases mess it up. File sourcing
     774             : ;; messes it up. You run other processes under the shell; these each have
     775             : ;; separate working directories, and some have commands for manipulating
     776             : ;; their w.d.'s (e.g., the lcd command in ftp). Some of these programs have
     777             : ;; commands that do *not* affect the current w.d. at all, but look like they
     778             : ;; do (e.g., the cd command in ftp).  In shells that allow you job
     779             : ;; control, you can switch between jobs, all having different w.d.'s. So
     780             : ;; simply saying %3 can shift your w.d..
     781             : ;;
     782             : ;; The solution is to relax, not stress out about it, and settle for
     783             : ;; a hack that works pretty well in typical circumstances. Remember
     784             : ;; that a half-assed solution is more in keeping with the spirit of Unix,
     785             : ;; anyway. Blech.
     786             : ;;
     787             : ;; One good hack not implemented here for users of programmable shells
     788             : ;; is to program up the shell w.d. manipulation commands to output
     789             : ;; a coded command sequence to the tty. Something like
     790             : ;;     ESC | <cwd> |
     791             : ;; where <cwd> is the new current working directory. Then trash the
     792             : ;; directory tracking machinery currently used in this package, and
     793             : ;; replace it with a process filter that watches for and strips out
     794             : ;; these messages.
     795             : 
     796             : (defun shell-directory-tracker (str)
     797             :   "Tracks cd, pushd and popd commands issued to the shell.
     798             : This function is called on each input passed to the shell.
     799             : It watches for cd, pushd and popd commands and sets the buffer's
     800             : default directory to track these commands.
     801             : 
     802             : You may toggle this tracking on and off with \\[shell-dirtrack-mode].
     803             : If Emacs gets confused, you can resync with the shell with \\[dirs].
     804             : \(The `dirtrack' package provides an alternative implementation of this
     805             : feature - see the function `dirtrack-mode'.)
     806             : 
     807             : See variables `shell-cd-regexp', `shell-chdrive-regexp', `shell-pushd-regexp',
     808             : and  `shell-popd-regexp', while `shell-pushd-tohome', `shell-pushd-dextract',
     809             : and `shell-pushd-dunique' control the behavior of the relevant command.
     810             : 
     811             : Environment variables are expanded, see function `substitute-in-file-name'."
     812           0 :   (if shell-dirtrackp
     813             :       ;; We fail gracefully if we think the command will fail in the shell.
     814             : ;;;      (with-demoted-errors "Directory tracker failure: %s"
     815             :       ;; This fails so often that it seems better to just ignore errors (?).
     816             :       ;; Eg even: foo=/tmp; cd $foo is beyond us (bug#17159).
     817           0 :       (ignore-errors
     818           0 :         (let ((start (progn (string-match
     819           0 :                                (concat "^" shell-command-separator-regexp)
     820           0 :                                str) ; skip whitespace
     821           0 :                               (match-end 0)))
     822             :                 (case-fold-search)
     823             :                 end cmd arg1)
     824           0 :             (while (string-match shell-command-regexp str start)
     825           0 :               (setq end (match-end 0)
     826           0 :                     cmd (comint-arguments (substring str start end) 0 0)
     827           0 :                     arg1 (comint-arguments (substring str start end) 1 1))
     828           0 :               (if arg1
     829           0 :                   (setq arg1 (shell-unquote-argument arg1)))
     830           0 :               (cond ((string-match (concat "\\`\\(" shell-popd-regexp
     831           0 :                                            "\\)\\($\\|[ \t]\\)")
     832           0 :                                    cmd)
     833           0 :                      (shell-process-popd (comint-substitute-in-file-name arg1)))
     834           0 :                     ((string-match (concat "\\`\\(" shell-pushd-regexp
     835           0 :                                            "\\)\\($\\|[ \t]\\)")
     836           0 :                                    cmd)
     837           0 :                      (shell-process-pushd (comint-substitute-in-file-name arg1)))
     838           0 :                     ((string-match (concat "\\`\\(" shell-cd-regexp
     839           0 :                                            "\\)\\($\\|[ \t]\\)")
     840           0 :                                    cmd)
     841           0 :                      (shell-process-cd (comint-substitute-in-file-name arg1)))
     842           0 :                     ((and shell-chdrive-regexp
     843           0 :                           (string-match (concat "\\`\\(" shell-chdrive-regexp
     844           0 :                                                 "\\)\\($\\|[ \t]\\)")
     845           0 :                                         cmd))
     846           0 :                      (shell-process-cd (comint-substitute-in-file-name cmd))))
     847           0 :               (setq start (progn (string-match shell-command-separator-regexp
     848           0 :                                                str end)
     849             :                                  ;; skip again
     850           0 :                                  (match-end 0))))))))
     851             : 
     852             : (defun shell-unquote-argument (string)
     853             :   "Remove all kinds of shell quoting from STRING."
     854           0 :   (save-match-data
     855           0 :     (let ((idx 0) next inside
     856             :           (quote-chars
     857           0 :            (if (string-match shell-dumb-shell-regexp
     858           0 :                              (file-name-nondirectory
     859           0 :                               (car (process-command (get-buffer-process (current-buffer))))))
     860             :                "['`\"]"
     861           0 :              "[\\'`\"]")))
     862           0 :       (while (and (< idx (length string))
     863           0 :                   (setq next (string-match quote-chars string next)))
     864           0 :         (cond ((= (aref string next) ?\\)
     865           0 :                (setq string (replace-match "" nil nil string))
     866           0 :                (setq next (1+ next)))
     867           0 :               ((and inside (= (aref string next) inside))
     868           0 :                (setq string (replace-match "" nil nil string))
     869           0 :                (setq inside nil))
     870           0 :               (inside
     871           0 :                (setq next (1+ next)))
     872             :               (t
     873           0 :                (setq inside (aref string next))
     874           0 :                (setq string (replace-match "" nil nil string)))))
     875           0 :       string)))
     876             : 
     877             : ;; popd [+n]
     878             : (defun shell-process-popd (arg)
     879           0 :   (let ((num (or (shell-extract-num arg) 0)))
     880           0 :     (cond ((and num (= num 0) shell-dirstack)
     881           0 :            (shell-cd (shell-prefixed-directory-name (car shell-dirstack)))
     882           0 :            (setq shell-dirstack (cdr shell-dirstack))
     883           0 :            (shell-dirstack-message))
     884           0 :           ((and num (> num 0) (<= num (length shell-dirstack)))
     885           0 :            (let* ((ds (cons nil shell-dirstack))
     886           0 :                   (cell (nthcdr (1- num) ds)))
     887           0 :              (rplacd cell (cdr (cdr cell)))
     888           0 :              (setq shell-dirstack (cdr ds))
     889           0 :              (shell-dirstack-message)))
     890             :           (t
     891           0 :            (error "Couldn't popd")))))
     892             : 
     893             : ;; Return DIR prefixed with comint-file-name-prefix as appropriate.
     894             : (defun shell-prefixed-directory-name (dir)
     895           0 :   (if (= (length comint-file-name-prefix) 0)
     896           0 :       dir
     897           0 :     (if (file-name-absolute-p dir)
     898             :         ;; The name is absolute, so prepend the prefix.
     899           0 :         (concat comint-file-name-prefix dir)
     900             :       ;; For relative name we assume default-directory already has the prefix.
     901           0 :       (expand-file-name dir))))
     902             : 
     903             : ;; cd [dir]
     904             : (defun shell-process-cd (arg)
     905           0 :   (let ((new-dir (cond ((zerop (length arg)) (concat comint-file-name-prefix
     906           0 :                                                      "~"))
     907           0 :                        ((string-equal "-" arg) shell-last-dir)
     908           0 :                        (t (shell-prefixed-directory-name arg)))))
     909           0 :     (setq shell-last-dir default-directory)
     910           0 :     (shell-cd new-dir)
     911           0 :     (shell-dirstack-message)))
     912             : 
     913             : ;; pushd [+n | dir]
     914             : (defun shell-process-pushd (arg)
     915           0 :   (let ((num (shell-extract-num arg)))
     916           0 :     (cond ((zerop (length arg))
     917             :            ;; no arg -- swap pwd and car of stack unless shell-pushd-tohome
     918           0 :            (cond (shell-pushd-tohome
     919           0 :                   (shell-process-pushd (concat comint-file-name-prefix "~")))
     920           0 :                  (shell-dirstack
     921           0 :                   (let ((old default-directory))
     922           0 :                     (shell-cd (car shell-dirstack))
     923           0 :                     (setq shell-dirstack (cons old (cdr shell-dirstack)))
     924           0 :                     (shell-dirstack-message)))
     925             :                  (t
     926           0 :                   (message "Directory stack empty."))))
     927           0 :           ((numberp num)
     928             :            ;; pushd +n
     929           0 :            (cond ((> num (length shell-dirstack))
     930           0 :                   (message "Directory stack not that deep."))
     931           0 :                  ((= num 0)
     932           0 :                   (error "Couldn't cd"))
     933           0 :                  (shell-pushd-dextract
     934           0 :                   (let ((dir (nth (1- num) shell-dirstack)))
     935           0 :                     (shell-process-popd arg)
     936           0 :                     (shell-process-pushd default-directory)
     937           0 :                     (shell-cd dir)
     938           0 :                     (shell-dirstack-message)))
     939             :                  (t
     940           0 :                   (let* ((ds (cons default-directory shell-dirstack))
     941           0 :                          (dslen (length ds))
     942           0 :                          (front (nthcdr num ds))
     943           0 :                          (back (reverse (nthcdr (- dslen num) (reverse ds))))
     944           0 :                          (new-ds (append front back)))
     945           0 :                     (shell-cd (car new-ds))
     946           0 :                     (setq shell-dirstack (cdr new-ds))
     947           0 :                     (shell-dirstack-message)))))
     948             :           (t
     949             :            ;; pushd <dir>
     950           0 :            (let ((old-wd default-directory))
     951           0 :              (shell-cd (shell-prefixed-directory-name arg))
     952           0 :              (if (or (null shell-pushd-dunique)
     953           0 :                      (not (member old-wd shell-dirstack)))
     954           0 :                  (setq shell-dirstack (cons old-wd shell-dirstack)))
     955           0 :              (shell-dirstack-message))))))
     956             : 
     957             : ;; If STR is of the form +n, for n>0, return n. Otherwise, nil.
     958             : (defun shell-extract-num (str)
     959           0 :   (and (string-match "^\\+[1-9][0-9]*$" str)
     960           0 :        (string-to-number str)))
     961             : 
     962             : (defvaralias 'shell-dirtrack-mode 'shell-dirtrackp)
     963             : (define-minor-mode shell-dirtrack-mode
     964             :   "Toggle directory tracking in this shell buffer (Shell Dirtrack mode).
     965             : With a prefix argument ARG, enable Shell Dirtrack mode if ARG is
     966             : positive, and disable it otherwise.  If called from Lisp, enable
     967             : the mode if ARG is omitted or nil.
     968             : 
     969             : The `dirtrack' package provides an alternative implementation of
     970             : this feature; see the function `dirtrack-mode'."
     971             :   nil nil nil
     972          11 :   (setq list-buffers-directory (if shell-dirtrack-mode default-directory))
     973          11 :   (if shell-dirtrack-mode
     974          11 :       (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t)
     975          11 :     (remove-hook 'comint-input-filter-functions 'shell-directory-tracker t)))
     976             : 
     977             : (define-obsolete-function-alias 'shell-dirtrack-toggle 'shell-dirtrack-mode
     978             :   "23.1")
     979             : 
     980             : (defun shell-cd (dir)
     981             :   "Do normal `cd' to DIR, and set `list-buffers-directory'."
     982           0 :   (cd dir)
     983           0 :   (if shell-dirtrackp
     984           0 :       (setq list-buffers-directory default-directory)))
     985             : 
     986             : (defun shell-resync-dirs ()
     987             :   "Resync the buffer's idea of the current directory stack.
     988             : This command queries the shell with the command bound to
     989             : `shell-dirstack-query' (default \"dirs\"), reads the next
     990             : line output and parses it to form the new directory stack.
     991             : DON'T issue this command unless the buffer is at a shell prompt.
     992             : Also, note that if some other subprocess decides to do output
     993             : immediately after the query, its output will be taken as the
     994             : new directory stack -- you lose.  If this happens, just do the
     995             : command again."
     996             :   (interactive)
     997           0 :   (let* ((proc (get-buffer-process (current-buffer)))
     998           0 :          (pmark (process-mark proc))
     999           0 :          (started-at-pmark (= (point) (marker-position pmark))))
    1000           0 :     (save-excursion
    1001           0 :       (goto-char pmark)
    1002             :       ;; If the process echoes commands, don't insert a fake command in
    1003             :       ;; the buffer or it will appear twice.
    1004           0 :       (unless comint-process-echoes
    1005           0 :         (insert shell-dirstack-query) (insert "\n"))
    1006           0 :       (sit-for 0)                       ; force redisplay
    1007           0 :       (comint-send-string proc shell-dirstack-query)
    1008           0 :       (comint-send-string proc "\n")
    1009           0 :       (set-marker pmark (point))
    1010           0 :       (let ((pt (point))
    1011             :             (regexp
    1012           0 :              (concat
    1013           0 :               (if comint-process-echoes
    1014             :                   ;; Skip command echo if the process echoes
    1015           0 :                   (concat "\\(" (regexp-quote shell-dirstack-query) "\n\\)")
    1016           0 :                 "\\(\\)")
    1017           0 :               "\\(.+\n\\)")))
    1018             :         ;; This extra newline prevents the user's pending input from spoofing us.
    1019           0 :         (insert "\n") (backward-char 1)
    1020             :         ;; Wait for one line.
    1021           0 :         (while (not (looking-at regexp))
    1022           0 :           (accept-process-output proc)
    1023           0 :           (goto-char pt)))
    1024           0 :       (goto-char pmark) (delete-char 1) ; remove the extra newline
    1025             :       ;; That's the dirlist. grab it & parse it.
    1026           0 :       (let* ((dl (buffer-substring (match-beginning 2) (1- (match-end 2))))
    1027           0 :              (dl-len (length dl))
    1028             :              (ds '())                   ; new dir stack
    1029             :              (i 0))
    1030           0 :         (while (< i dl-len)
    1031             :           ;; regexp = optional whitespace, (non-whitespace), optional whitespace
    1032           0 :           (string-match "\\s *\\(\\S +\\)\\s *" dl i) ; pick off next dir
    1033           0 :           (setq ds (cons (concat comint-file-name-prefix
    1034           0 :                                  (substring dl (match-beginning 1)
    1035           0 :                                             (match-end 1)))
    1036           0 :                          ds))
    1037           0 :           (setq i (match-end 0)))
    1038           0 :         (let ((ds (nreverse ds)))
    1039           0 :           (with-demoted-errors "Couldn't cd: %s"
    1040           0 :             (shell-cd (car ds))
    1041           0 :             (setq shell-dirstack (cdr ds)
    1042           0 :                   shell-last-dir (car shell-dirstack))
    1043           0 :             (shell-dirstack-message)))))
    1044           0 :     (if started-at-pmark (goto-char (marker-position pmark)))))
    1045             : 
    1046             : ;; For your typing convenience:
    1047             : (defalias 'dirs 'shell-resync-dirs)
    1048             : 
    1049             : 
    1050             : ;; Show the current dirstack on the message line.
    1051             : ;; Pretty up dirs a bit by changing "/usr/jqr/foo" to "~/foo".
    1052             : ;; (This isn't necessary if the dirlisting is generated with a simple "dirs".)
    1053             : ;; All the commands that mung the buffer's dirstack finish by calling
    1054             : ;; this guy.
    1055             : (defun shell-dirstack-message ()
    1056           0 :   (when shell-dirtrack-verbose
    1057           0 :     (let* ((msg "")
    1058           0 :            (ds (cons default-directory shell-dirstack))
    1059           0 :            (home (expand-file-name (concat comint-file-name-prefix "~/")))
    1060           0 :            (homelen (length home)))
    1061           0 :       (while ds
    1062           0 :         (let ((dir (car ds)))
    1063           0 :           (and (>= (length dir) homelen)
    1064           0 :                (string= home (substring dir 0 homelen))
    1065           0 :                (setq dir (concat "~/" (substring dir homelen))))
    1066             :           ;; Strip off comint-file-name-prefix if present.
    1067           0 :           (and comint-file-name-prefix
    1068           0 :                (>= (length dir) (length comint-file-name-prefix))
    1069           0 :                (string= comint-file-name-prefix
    1070           0 :                         (substring dir 0 (length comint-file-name-prefix)))
    1071           0 :                (setq dir (substring dir (length comint-file-name-prefix)))
    1072           0 :                (setcar ds dir))
    1073           0 :           (setq msg (concat msg (directory-file-name dir) " "))
    1074           0 :           (setq ds (cdr ds))))
    1075           0 :       (message "%s" msg))))
    1076             : 
    1077             : ;; This was mostly copied from shell-resync-dirs.
    1078             : (defun shell-snarf-envar (var)
    1079             :   "Return as a string the shell's value of environment variable VAR."
    1080           0 :   (let* ((cmd (format "printenv '%s'\n" var))
    1081           0 :          (proc (get-buffer-process (current-buffer)))
    1082           0 :          (pmark (process-mark proc)))
    1083           0 :     (goto-char pmark)
    1084           0 :     (insert cmd)
    1085           0 :     (sit-for 0)                         ; force redisplay
    1086           0 :     (comint-send-string proc cmd)
    1087           0 :     (set-marker pmark (point))
    1088           0 :     (let ((pt (point)))                 ; wait for 1 line
    1089             :       ;; This extra newline prevents the user's pending input from spoofing us.
    1090           0 :       (insert "\n") (backward-char 1)
    1091           0 :       (while (not (looking-at ".+\n"))
    1092           0 :         (accept-process-output proc)
    1093           0 :         (goto-char pt)))
    1094           0 :     (goto-char pmark) (delete-char 1)   ; remove the extra newline
    1095           0 :     (buffer-substring (match-beginning 0) (1- (match-end 0)))))
    1096             : 
    1097             : (defun shell-copy-environment-variable (variable)
    1098             :   "Copy the environment variable VARIABLE from the subshell to Emacs.
    1099             : This command reads the value of the specified environment variable
    1100             : in the shell, and sets the same environment variable in Emacs
    1101             : \(what `getenv' in Emacs would return) to that value.
    1102             : That value will affect any new subprocesses that you subsequently start
    1103             : from Emacs."
    1104           0 :   (interactive (list (read-envvar-name "\
    1105           0 : Copy Shell environment variable to Emacs: ")))
    1106           0 :   (setenv variable (shell-snarf-envar variable)))
    1107             : 
    1108             : (defun shell-forward-command (&optional arg)
    1109             :   "Move forward across ARG shell command(s).  Does not cross lines.
    1110             : See `shell-command-regexp'."
    1111             :   (interactive "p")
    1112           0 :   (let ((limit (line-end-position))
    1113           0 :         (pt (point)))
    1114           0 :     (re-search-forward (concat shell-command-regexp "\\([;&|][\t ]*\\)+")
    1115           0 :                        limit 'move arg)
    1116           0 :     (and (/= pt (point))
    1117           0 :          (skip-syntax-backward " " pt))))
    1118             : 
    1119             : 
    1120             : (defun shell-backward-command (&optional arg)
    1121             :   "Move backward across ARG shell command(s).  Does not cross lines.
    1122             : See `shell-command-regexp'."
    1123             :   (interactive "p")
    1124           0 :   (let ((limit (save-excursion (comint-bol nil) (point))))
    1125           0 :     (when (> limit (point))
    1126           0 :       (setq limit (line-beginning-position)))
    1127           0 :     (skip-syntax-backward " " limit)
    1128           0 :     (let ((pt (point)))
    1129           0 :       (if (re-search-backward
    1130           0 :            (format "[;&|]+[\t ]*\\(%s\\)" shell-command-regexp) limit 'move arg)
    1131           0 :           (progn (goto-char (match-beginning 1))
    1132           0 :                  (skip-chars-forward ";&|")))
    1133           0 :       (and (/= pt (point))
    1134           0 :            (skip-syntax-forward " " pt)))))
    1135             : 
    1136             : (defun shell-dynamic-complete-command ()
    1137             :   "Dynamically complete the command at point.
    1138             : This function is similar to `comint-dynamic-complete-filename', except that it
    1139             : searches `exec-path' (minus trailing `exec-directory') for completion
    1140             : candidates.  Note that this may not be the same as the shell's idea of the
    1141             : path.
    1142             : 
    1143             : Completion is dependent on the value of `shell-completion-execonly',
    1144             : `shell-completion-fignore', plus those that affect file completion.  See Info
    1145             : node `Shell Options'.
    1146             : 
    1147             : Returns t if successful."
    1148             :   (interactive)
    1149           0 :   (let ((data (shell-command-completion)))
    1150           0 :     (if data
    1151           0 :         (prog2 (unless (window-minibuffer-p)
    1152           0 :                  (message "Completing command name..."))
    1153           0 :             (apply #'completion-in-region data)))))
    1154             : 
    1155             : (defun shell-command-completion ()
    1156             :   "Return the completion data for the command at point, if any."
    1157           0 :   (let ((filename (comint-match-partial-filename)))
    1158           0 :     (if (and filename
    1159           0 :              (save-match-data (not (string-match "[~/]" filename)))
    1160           0 :              (eq (match-beginning 0)
    1161           0 :                  (save-excursion (shell-backward-command 1) (point))))
    1162           0 :         (shell--command-completion-data))))
    1163             : 
    1164             : (defun shell--command-completion-data ()
    1165             :   "Return the completion data for the command at point."
    1166           0 :   (let* ((filename (or (comint-match-partial-filename) ""))
    1167           0 :          (start (if (zerop (length filename)) (point) (match-beginning 0)))
    1168           0 :          (end (if (zerop (length filename)) (point) (match-end 0)))
    1169           0 :          (filenondir (file-name-nondirectory filename))
    1170             :          ; why cdr? see `shell-dynamic-complete-command'
    1171           0 :          (path-dirs (append (cdr (reverse exec-path))
    1172           0 :            (if (memq system-type '(windows-nt ms-dos)) '("."))))
    1173           0 :          (cwd (file-name-as-directory (expand-file-name default-directory)))
    1174             :          (ignored-extensions
    1175           0 :           (and comint-completion-fignore
    1176           0 :                (mapconcat (function (lambda (x) (concat (regexp-quote x) "\\'")))
    1177           0 :                           comint-completion-fignore "\\|")))
    1178             :          (dir "") (comps-in-dir ())
    1179             :          (file "") (abs-file-name "") (completions ()))
    1180             :     ;; Go thru each dir in the search path, finding completions.
    1181           0 :     (while path-dirs
    1182           0 :       (setq dir (file-name-as-directory (comint-directory (or (car path-dirs) ".")))
    1183           0 :             comps-in-dir (and (file-accessible-directory-p dir)
    1184           0 :                               (file-name-all-completions filenondir dir)))
    1185             :       ;; Go thru each completion found, to see whether it should be used.
    1186           0 :       (while comps-in-dir
    1187           0 :         (setq file (car comps-in-dir)
    1188           0 :               abs-file-name (concat dir file))
    1189           0 :         (if (and (not (member file completions))
    1190           0 :                  (not (and ignored-extensions
    1191           0 :                            (string-match ignored-extensions file)))
    1192           0 :                  (or (string-equal dir cwd)
    1193           0 :                      (not (file-directory-p abs-file-name)))
    1194           0 :                  (or (null shell-completion-execonly)
    1195           0 :                      (file-executable-p abs-file-name)))
    1196           0 :             (setq completions (cons file completions)))
    1197           0 :         (setq comps-in-dir (cdr comps-in-dir)))
    1198           0 :       (setq path-dirs (cdr path-dirs)))
    1199             :     ;; OK, we've got a list of completions.
    1200           0 :     (list
    1201           0 :      start end
    1202             :      (lambda (string pred action)
    1203           0 :        (if (string-match "/" string)
    1204           0 :            (completion-file-name-table string pred action)
    1205           0 :          (complete-with-action action completions string pred)))
    1206             :      :exit-function
    1207             :      (lambda (_string finished)
    1208           0 :        (when (memq finished '(sole finished))
    1209           0 :          (if (looking-at " ")
    1210           0 :              (goto-char (match-end 0))
    1211           0 :            (insert " ")))))))
    1212             : 
    1213             : ;; (defun shell-dynamic-complete-as-command ()
    1214             : ;;    "Dynamically complete at point as a command.
    1215             : ;;  See `shell-dynamic-complete-filename'.  Returns t if successful."
    1216             : ;;    (apply #'completion-in-region shell--command-completion-data))
    1217             : 
    1218             : (defun shell-dynamic-complete-filename ()
    1219             :   "Dynamically complete the filename at point.
    1220             : This completes only if point is at a suitable position for a
    1221             : filename argument."
    1222             :   (interactive)
    1223           0 :   (let ((data (shell-filename-completion)))
    1224           0 :     (if data (apply #'completion-in-region data))))
    1225             : 
    1226             : (defun shell-filename-completion ()
    1227             :   "Return the completion data for file name at point, if any."
    1228           0 :   (let ((opoint (point))
    1229           0 :         (beg (comint-line-beginning-position)))
    1230           0 :     (when (save-excursion
    1231           0 :             (goto-char (if (re-search-backward "[;|&]" beg t)
    1232           0 :                            (match-end 0)
    1233           0 :                          beg))
    1234           0 :             (re-search-forward "[^ \t][ \t]" opoint t))
    1235           0 :       (comint-filename-completion))))
    1236             : 
    1237             : (defun shell-match-partial-variable ()
    1238             :   "Return the shell variable at point, or nil if none is found."
    1239           0 :   (save-excursion
    1240           0 :     (if (re-search-backward "[^A-Za-z0-9_{(]" nil 'move)
    1241           0 :         (or (looking-at "\\$") (forward-char 1)))
    1242           0 :     (if (or (eolp) (looking-at "[^A-Za-z0-9_{($]"))
    1243             :         nil
    1244           0 :       (looking-at "\\$?[{(]?[A-Za-z0-9_]*[})]?")
    1245           0 :       (buffer-substring (match-beginning 0) (match-end 0)))))
    1246             : 
    1247             : (defun shell-dynamic-complete-environment-variable ()
    1248             :   "Dynamically complete the environment variable at point.
    1249             : Completes if after a variable, i.e., if it starts with a \"$\".
    1250             : 
    1251             : This function is similar to `comint-dynamic-complete-filename', except that it
    1252             : searches `process-environment' for completion candidates.  Note that this may
    1253             : not be the same as the interpreter's idea of variable names.  The main problem
    1254             : with this type of completion is that `process-environment' is the environment
    1255             : which Emacs started with.  Emacs does not track changes to the environment made
    1256             : by the interpreter.  Perhaps it would be more accurate if this function was
    1257             : called `shell-dynamic-complete-process-environment-variable'.
    1258             : 
    1259             : Returns non-nil if successful."
    1260             :   (interactive)
    1261           0 :   (let ((data (shell-environment-variable-completion)))
    1262           0 :     (if data
    1263           0 :         (prog2 (unless (window-minibuffer-p)
    1264           0 :                  (message "Completing variable name..."))
    1265           0 :             (apply #'completion-in-region data)))))
    1266             : 
    1267             : 
    1268             : (defun shell-environment-variable-completion ()
    1269             :   "Completion data for an environment variable at point, if any."
    1270           0 :   (let* ((var (shell-match-partial-variable))
    1271           0 :          (end (match-end 0)))
    1272           0 :     (when (and (not (zerop (length var))) (eq (aref var 0) ?$))
    1273           0 :       (let* ((start
    1274           0 :               (save-excursion
    1275           0 :                 (goto-char (match-beginning 0))
    1276           0 :                 (looking-at "\\$?[({]*")
    1277           0 :                 (match-end 0)))
    1278           0 :              (variables (mapcar (lambda (x)
    1279           0 :                                   (substring x 0 (string-match "=" x)))
    1280           0 :                                 process-environment))
    1281           0 :              (suffix (pcase (char-before start) (?\{ "}") (?\( ")") (_ ""))))
    1282           0 :         (list start end variables
    1283             :               :exit-function
    1284             :               (lambda (s finished)
    1285           0 :                 (when (memq finished '(sole finished))
    1286           0 :                   (let ((suf (concat suffix
    1287           0 :                                      (if (file-directory-p
    1288           0 :                                           (comint-directory (getenv s)))
    1289           0 :                                          "/"))))
    1290           0 :                     (if (looking-at (regexp-quote suf))
    1291           0 :                         (goto-char (match-end 0))
    1292           0 :                       (insert suf))))))))))
    1293             : 
    1294             : 
    1295             : (defun shell-c-a-p-replace-by-expanded-directory ()
    1296             :   "Expand directory stack reference before point.
    1297             : For use on `completion-at-point-functions'."
    1298           0 :   (when (comint-match-partial-filename)
    1299           0 :     (save-excursion
    1300           0 :       (goto-char (match-beginning 0))
    1301           0 :       (let ((stack (cons default-directory shell-dirstack))
    1302           0 :             (index (cond ((looking-at "=-/?")
    1303           0 :                           (length shell-dirstack))
    1304           0 :                          ((looking-at "=\\([0-9]+\\)/?")
    1305           0 :                           (string-to-number
    1306           0 :                            (buffer-substring
    1307           0 :                             (match-beginning 1) (match-end 1)))))))
    1308           0 :         (when index
    1309           0 :           (let ((start (match-beginning 0))
    1310           0 :                 (end (match-end 0))
    1311           0 :                 (replacement (file-name-as-directory (nth index stack))))
    1312             :             (lambda ()
    1313           0 :               (cond
    1314           0 :                ((>= index (length stack))
    1315           0 :                 (error "Directory stack not that deep"))
    1316             :                (t
    1317           0 :                 (save-excursion
    1318           0 :                   (goto-char start)
    1319           0 :                   (insert replacement)
    1320           0 :                   (delete-char (- end start)))
    1321           0 :                 (message "Directory item: %d" index)
    1322           0 :                 t)))))))))
    1323             : 
    1324             : (defun shell-replace-by-expanded-directory ()
    1325             :   "Expand directory stack reference before point.
    1326             : Directory stack references are of the form \"=digit\" or \"=-\".
    1327             : See `default-directory' and `shell-dirstack'.
    1328             : 
    1329             : Returns t if successful."
    1330             :   (interactive)
    1331           0 :   (let ((f (shell-c-a-p-replace-by-expanded-directory)))
    1332           0 :     (if f (funcall f))))
    1333             : 
    1334             : (provide 'shell)
    1335             : 
    1336             : ;;; shell.el ends here

Generated by: LCOV version 1.12