-;;; Interfacing to client packages (and converting them)
-;;; Notes from when this was called cmushell, and was not the standard emacs
-;;; shell package. Many of the conversions discussed here have been done.
-;;;============================================================================
-;;; Several gnu packages (tex-mode, background, dbx, gdb, kermit, prolog,
-;;; telnet are some) use the shell package as clients. Most of them would
-;;; be better off using the comint package directly, but they predate it.
-;;; The catch is that most of these packages (dbx, gdb, prolog, telnet)
-;;; assume total knowledge of all the local variables that shell mode
-;;; functions depend on. So they (kill-all-local-variables), then create
-;;; the few local variables that shell.el functions depend on. Alas,
-;;; cmushell.el functions depend on a different set of vars (for example,
-;;; the input history ring is a local variable in cmushell.el's shell mode,
-;;; whereas there is no input history ring in shell.el's shell mode).
-;;; So we have a situation where the greater functionality of cmushell.el
-;;; is biting us -- you can't just replace shell will cmushell.
-;;;
-;;; Altering these packages to use comint mode directly should *greatly*
-;;; improve their functionality, and is actually pretty easy. It's
-;;; mostly a matter of renaming a few variable names. See comint.el for more.
-;;; -Olin
-
-
-
-;;; Do the user's customisation...
-;;;===============================
-(defvar shell-load-hook nil
- "This hook is run when shell is loaded in.
-This is a good place to put keybindings.")
-
-(run-hooks 'shell-load-hook)
-
-;;; Change Log
-;;; ===========================================================================
-;;; Olin 8/88
-;;; Created.
-;;;
-;;; Olin 5/26/90
-;;; - Split cmulisp and cmushell modes into separate files.
-;;; Not only is this a good idea, it's apparently the way it'll be rel 19.
-;;; - Souped up the directory tracking; it now can handle pushd, pushd +n,
-;;; and popd +n.
-;;; - Added cmushell-dirtrack-toggle command to toggle the directory
-;;; tracking that cmushell tries to do. This is useful, for example,
-;;; when you are running ftp -- it prevents the ftp "cd" command from
-;;; spoofing the tracking machinery. This command is also named
-;;; dirtrack-toggle, so you need only type M-x dirtrack to run it.
-;;; - Added cmushell-resync-dirs command. This queries the shell
-;;; for the current directory stack, and resets the buffer's stack
-;;; accordingly. This command is also named dirs, so you need only type
-;;; M-x dirs to run it.
-;;; - Bits of the new directory tracking code were adapted from source
-;;; contributed by Vince Broman, Jeff Peck, and Barry Warsaw.
-;;; - See also the improvements made to comint.el at the same time.
-;;; - Renamed several variables. Mostly this comprised changing "shell"
-;;; to "cmushell" in the names. The only variables that are not prefixed
-;;; with "cmushell-" are the ones that are common with shell.el:
-;;; explicit-shell-file-name shell-prompt-pattern explicit-csh-args
-;;; and shell-cd/popd/pushd-regexp
-;;; The variables and functions that were changed to have "cmushell-"
-;;; prefixes are:
-;;; shell-directory-stack (v), shell-directory-tracker (f)
-;;; This should not affect users, only Emacs Lisp hackers. Hopefully
-;;; one day shell.el will just go away, and we can drop all this
-;;; "cmushell" bullshit.
-;;; - Upgraded process sends to use comint-send-string instead of
-;;; process-send-string.
-;;;
-;;; Olin 6/14/90
-;;; - If your shell is named <shellname>, and a variable named
-;;; explicit-<shellname>-args exists, cmushell is supposed
-;;; to use its value as the arglist to the shell invocation.
-;;; E.g., if you define explicit-csh-args to be
-;;; ("-ifx"), then when cmushell cranks up a csh, it execs it
-;;; as "csh -ifx". This is what is documented. What has actually
-;;; been the case is that the variable checked is
-;;; explicit-<shellname>-arguments, not explicit-<shellname>-args.
-;;; The documentation has been changed to conform to the code (for
-;;; backwards compatibility with shell.el). This bug is inherited from
-;;; the same bug in shell.el.
-;;; This bug reported by Stephen Anderson.
-;;;
-;;; Olin 9/5/90
-;;; - Arguments to cd, popd, and pushd now have their env vars expanded
-;;; out by the tracking machinery. So if you say "cd $SRCDIR/funs", the
-;;; $SRCDIR var will be replaced by its value *in emacs' process
-;;; environment*. If this is different from the shell's binding of the
-;;; variable, you lose. Several users needed this feature, fragile
-;;; though it may be. The fix was contributed by sk@thp.Uni-Koeln.DE.
-;;;
-;;; Olin 3/12/91
-;;; - Moved comint-dynamic-complete (filename completion) from M-tab to tab.
-;;;
-;;; Jim Blandy 10/30/91
-;;; - Removed the "cmu" prefix from names, renamed file to shell.el,
-;;; to become the standard shell package.
-
+(defun shell-forward-command (&optional arg)
+ "Move forward across ARG shell command(s). Does not cross lines.
+See `shell-command-regexp'."
+ (interactive "p")
+ (let ((limit (save-excursion (end-of-line nil) (point))))
+ (if (re-search-forward (concat shell-command-regexp "\\([;&|][\t ]*\\)+")
+ limit 'move arg)
+ (skip-syntax-backward " "))))
+
+
+(defun shell-backward-command (&optional arg)
+ "Move backward across ARG shell command(s). Does not cross lines.
+See `shell-command-regexp'."
+ (interactive "p")
+ (let ((limit (save-excursion (comint-bol nil) (point))))
+ (if (> limit (point))
+ (save-excursion (beginning-of-line) (setq limit (point))))
+ (skip-syntax-backward " " limit)
+ (if (re-search-backward
+ (format "[;&|]+[\t ]*\\(%s\\)" shell-command-regexp) limit 'move arg)
+ (progn (goto-char (match-beginning 1))
+ (skip-chars-forward ";&|")))))
+
+
+(defun shell-dynamic-complete-command ()
+ "Dynamically complete the command at point.
+This function is similar to `comint-dynamic-complete-filename', except that it
+searches `exec-path' (minus the trailing emacs library path) for completion
+candidates. Note that this may not be the same as the shell's idea of the
+path.
+
+Completion is dependent on the value of `shell-completion-execonly', plus
+those that effect file completion. See `shell-dynamic-complete-as-command'.
+
+Returns t if successful."
+ (interactive)
+ (let ((filename (comint-match-partial-filename)))
+ (if (and filename
+ (save-match-data (not (string-match "[~/]" filename)))
+ (eq (match-beginning 0)
+ (save-excursion (shell-backward-command 1) (point))))
+ (prog2 (message "Completing command name...")
+ (shell-dynamic-complete-as-command)))))
+
+
+(defun shell-dynamic-complete-as-command ()
+ "Dynamically complete at point as a command.
+See `shell-dynamic-complete-filename'. Returns t if successful."
+ (let* ((filename (or (comint-match-partial-filename) ""))
+ (pathnondir (file-name-nondirectory filename))
+ (paths (cdr (reverse exec-path)))
+ (cwd (file-name-as-directory (expand-file-name default-directory)))
+ (ignored-extensions
+ (and comint-completion-fignore
+ (mapconcat (function (lambda (x) (concat (regexp-quote x) "$")))
+ comint-completion-fignore "\\|")))
+ (path "") (comps-in-path ()) (file "") (filepath "") (completions ()))
+ ;; Go thru each path in the search path, finding completions.
+ (while paths
+ (setq path (file-name-as-directory (comint-directory (or (car paths) ".")))
+ comps-in-path (and (file-accessible-directory-p path)
+ (file-name-all-completions pathnondir path)))
+ ;; Go thru each completion found, to see whether it should be used.
+ (while comps-in-path
+ (setq file (car comps-in-path)
+ filepath (concat path file))
+ (if (and (not (member file completions))
+ (not (and ignored-extensions
+ (string-match ignored-extensions file)))
+ (or (string-equal path cwd)
+ (not (file-directory-p filepath)))
+ (or (null shell-completion-execonly)
+ (file-executable-p filepath)))
+ (setq completions (cons file completions)))
+ (setq comps-in-path (cdr comps-in-path)))
+ (setq paths (cdr paths)))
+ ;; OK, we've got a list of completions.
+ (let ((success (let ((comint-completion-addsuffix nil))
+ (comint-dynamic-simple-complete pathnondir completions))))
+ (if (and (memq success '(sole shortest)) comint-completion-addsuffix
+ (not (file-directory-p (comint-match-partial-filename))))
+ (insert " "))
+ success)))
+
+
+(defun shell-match-partial-variable ()
+ "Return the variable at point, or nil if non is found."
+ (save-excursion
+ (let ((limit (point)))
+ (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move)
+ (or (looking-at "\\$") (forward-char 1)))
+ ;; Anchor the search forwards.
+ (if (or (eolp) (looking-at "[^A-Za-z0-9_{}$]"))
+ nil
+ (re-search-forward "\\$?{?[A-Za-z0-9_]*}?" limit)
+ (buffer-substring (match-beginning 0) (match-end 0))))))
+
+
+(defun shell-dynamic-complete-environment-variable ()
+ "Dynamically complete the environment variable at point.
+Completes if after a variable, i.e., if it starts with a \"$\".
+See `shell-dynamic-complete-as-environment-variable'.
+
+This function is similar to `comint-dynamic-complete-filename', except that it
+searches `process-environment' for completion candidates. Note that this may
+not be the same as the interpreter's idea of variable names. The main problem
+with this type of completion is that `process-environment' is the environment
+which Emacs started with. Emacs does not track changes to the environment made
+by the interpreter. Perhaps it would be more accurate if this function was
+called `shell-dynamic-complete-process-environment-variable'.
+
+Returns non-nil if successful."
+ (interactive)
+ (let ((variable (shell-match-partial-variable)))
+ (if (and variable (string-match "^\\$" variable))
+ (prog2 (message "Completing variable name...")
+ (shell-dynamic-complete-as-environment-variable)))))
+
+
+(defun shell-dynamic-complete-as-environment-variable ()
+ "Dynamically complete at point as an environment variable.
+Used by `shell-dynamic-complete-environment-variable'.
+Uses `comint-dynamic-simple-complete'."
+ (let* ((var (or (shell-match-partial-variable) ""))
+ (variable (substring var (or (string-match "[^$({]\\|$" var) 0)))
+ (variables (mapcar (function (lambda (x)
+ (substring x 0 (string-match "=" x))))
+ process-environment))
+ (addsuffix comint-completion-addsuffix)
+ (comint-completion-addsuffix nil)
+ (success (comint-dynamic-simple-complete variable variables)))
+ (if (memq success '(sole shortest))
+ (let* ((var (shell-match-partial-variable))
+ (variable (substring var (string-match "[^$({]" var)))
+ (protection (cond ((string-match "{" var) "}")
+ ((string-match "(" var) ")")
+ (t "")))
+ (suffix (cond ((null addsuffix) "")
+ ((file-directory-p
+ (comint-directory (getenv variable))) "/")
+ (t " "))))
+ (insert protection suffix)))
+ success))
+
+
+(defun shell-replace-by-expanded-directory ()
+ "Expand directory stack reference before point.
+Directory stack references are of the form \"=digit\" or \"=-\".
+See `default-directory' and `shell-dirstack'.
+
+Returns t if successful."
+ (interactive)
+ (if (comint-match-partial-filename)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (let ((stack (cons default-directory shell-dirstack))
+ (index (cond ((looking-at "=-/?")
+ (length shell-dirstack))
+ ((looking-at "=\\([0-9]+\\)")
+ (string-to-number
+ (buffer-substring
+ (match-beginning 1) (match-end 1)))))))
+ (cond ((null index)
+ nil)
+ ((>= index (length stack))
+ (error "Directory stack not that deep."))
+ (t
+ (replace-match (file-name-as-directory (nth index stack)) t t)
+ (message "Directory item: %d" index)
+ t))))))
+\f