diff options
| author | Richard M. Stallman | 1993-10-22 02:53:24 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1993-10-22 02:53:24 +0000 |
| commit | d76b71afe39a724f5fc819c84f9bc9e34df34240 (patch) | |
| tree | 89a01279e029c45a7718680a73cd231dd34f5ea8 | |
| parent | 20ddfff7191bdbfbb419964a1ce129e050c4e382 (diff) | |
| download | emacs-d76b71afe39a724f5fc819c84f9bc9e34df34240.tar.gz emacs-d76b71afe39a724f5fc819c84f9bc9e34df34240.zip | |
(shell-after-partial-filename): Renamed from
shell-after-partial-pathname.
Commented out shell-load-hooks.
(shell-after-partial-pathname): New subroutine.
Renamed shell-command-execonly to
shell-completion-execonly for consistency.
(shell-read-input-ring, shell-input-ring-file-name):
Moved to, and renamed in, comint.el.
(shell-dynamic-complete-command): Make sure local
completion-ignore-case is nil.
(shell-mode): Set buffer-local variable paragraph-start
to comint-prompt-regexp so paragraph motion/mark commands work on
output groups.
Set comint-after-partial-pathname to it.
(shell-read-input-ring): Use find-file-noselect.
(shell-match-cmd-w/optional-arg): Removed.
(shell-delimiter-argument-list): New variable.
(shell-input-ring-file-name): New variable.
(shell-mode-map): Changed file name completions listing
binding to new name comint-dynamic-list-filename-completions.
(shell-mode): Call new function shell-read-input-ring
and shell-dirstack on start up. Doc fix for new functionality.
(shell-mode): Set shell-input-ring-file-name depending
on the command that was invoked for the inferior shell. Set
comint-delimiter-argument-list to shell-delimiter-argument-list.
(shell-read-input-ring): New function.
(shell-directory-tracker): Use comint-arguments.
(shell-front-match): Removed.
(shell-match-cmd-w/optional-arg): Removed.
(shell-process-popd): Fixed bug when numeric argument
equal to length of stack including current directory.
(shell-process-pushd): Fixed missing ()s in cond.
(shell-dynamic-complete-command): Uses exec-path minus
trailing emacs library path. Uses "." for nil elements in
exec-path. Uses string-match rather than funcall to test
candidate extensions. Uses member on completions list rather than
file-exists-p to test for existence. Uses file-directory-p rather
than funcall to test for directory. Uses directories only if in
current directory. Uses comint-dynamic-list-completions.
(shell-command-regexp, shell-command-execonly)
(shell-pushd-tohome, shell-pushd-dextract)
(shell-pushd-dunique): New variables.
(shell-mode-map): Bound shell-forward/backward-command
to C-c C-f and C-c C-b.
(shell-mode): Set comint variables for which
shells have different values:
comint-get-current-command, comint-dynamic-complete-command.
(shell-directory-tracker): Parse through
command sequences for directory commands.
(shell-process-popd): Signal error if can't
process argument/stack. Fixed bug when no argument and no stack.
(shell-process-cd): Signal error if can't process argument.
(shell-process-pushd): Signal error if can't
process argument/stack. Handle shell-pushd-tohome,
shell-pushd-dextract, and shell-pushd-dunique.
(shell-forward-command, shell-backward-command)
(shell-dynamic-complete-command): New commands.
| -rw-r--r-- | lisp/shell.el | 518 |
1 files changed, 328 insertions, 190 deletions
diff --git a/lisp/shell.el b/lisp/shell.el index 27f16685a1b..c1fbf3e73f5 100644 --- a/lisp/shell.el +++ b/lisp/shell.el | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | ;;; Copyright (C) 1988, 1993 Free Software Foundation, Inc. | 2 | ;;; Copyright (C) 1988, 1993 Free Software Foundation, Inc. |
| 3 | 3 | ||
| 4 | ;; Author: Olin Shivers <shivers@cs.cmu.edu> | 4 | ;; Author: Olin Shivers <shivers@cs.cmu.edu> |
| 5 | ;; Adapted-by: Simon Marshall <s.marshall@dcs.hull.ac.uk> | ||
| 5 | ;; Keywords: processes | 6 | ;; Keywords: processes |
| 6 | 7 | ||
| 7 | ;;; This file is part of GNU Emacs. | 8 | ;;; This file is part of GNU Emacs. |
| @@ -22,11 +23,10 @@ | |||
| 22 | 23 | ||
| 23 | ;;; Commentary: | 24 | ;;; Commentary: |
| 24 | 25 | ||
| 25 | ;;; The changelog is at the end of file. | ||
| 26 | |||
| 27 | ;;; Please send me bug reports, bug fixes, and extensions, so that I can | 26 | ;;; Please send me bug reports, bug fixes, and extensions, so that I can |
| 28 | ;;; merge them into the master source. | 27 | ;;; merge them into the master source. |
| 29 | ;;; - Olin Shivers (shivers@cs.cmu.edu) | 28 | ;;; - Olin Shivers (shivers@cs.cmu.edu) |
| 29 | ;;; - Simon Marshall (s.marshall@dcs.hull.ac.uk) | ||
| 30 | 30 | ||
| 31 | ;;; This file defines a a shell-in-a-buffer package (shell mode) built | 31 | ;;; This file defines a a shell-in-a-buffer package (shell mode) built |
| 32 | ;;; on top of comint mode. This is actually cmushell with things | 32 | ;;; on top of comint mode. This is actually cmushell with things |
| @@ -62,7 +62,7 @@ | |||
| 62 | ;; t) | 62 | ;; t) |
| 63 | ;; | 63 | ;; |
| 64 | ;; ; Define C-c t to run my favorite command in shell mode: | 64 | ;; ; Define C-c t to run my favorite command in shell mode: |
| 65 | ;; (setq shell-load-hook | 65 | ;; (setq shell-mode-hook |
| 66 | ;; '((lambda () | 66 | ;; '((lambda () |
| 67 | ;; (define-key shell-mode-map "\C-ct" 'favorite-cmd)))) | 67 | ;; (define-key shell-mode-map "\C-ct" 'favorite-cmd)))) |
| 68 | 68 | ||
| @@ -73,9 +73,13 @@ | |||
| 73 | ;;; | 73 | ;;; |
| 74 | ;;; m-p comint-previous-input Cycle backwards in input history | 74 | ;;; m-p comint-previous-input Cycle backwards in input history |
| 75 | ;;; m-n comint-next-input Cycle forwards | 75 | ;;; m-n comint-next-input Cycle forwards |
| 76 | ;;; m-r comint-previous-matching-input Previous input matching a regexp | ||
| 77 | ;;; m-R comint-previous-matching-input-from-input -"- matching input | ||
| 78 | ;;; m-s comint-next-matching-input Next input that matches | ||
| 79 | ;;; m-S comint-next-matching-input-from-input -"- matching input | ||
| 76 | ;;; m-c-r comint-previous-input-matching Search backwards in input history | 80 | ;;; m-c-r comint-previous-input-matching Search backwards in input history |
| 77 | ;;; return comint-send-input | 81 | ;;; return comint-send-input |
| 78 | ;;; c-a comint-bol Beginning of line; skip prompt. | 82 | ;;; c-a comint-bol Beginning of line; skip prompt |
| 79 | ;;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff. | 83 | ;;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff. |
| 80 | ;;; c-c c-u comint-kill-input ^u | 84 | ;;; c-c c-u comint-kill-input ^u |
| 81 | ;;; c-c c-w backward-kill-word ^w | 85 | ;;; c-c c-w backward-kill-word ^w |
| @@ -84,20 +88,22 @@ | |||
| 84 | ;;; c-c c-\ comint-quit-subjob ^\ | 88 | ;;; c-c c-\ comint-quit-subjob ^\ |
| 85 | ;;; c-c c-o comint-kill-output Delete last batch of process output | 89 | ;;; c-c c-o comint-kill-output Delete last batch of process output |
| 86 | ;;; c-c c-r comint-show-output Show last batch of process output | 90 | ;;; c-c c-r comint-show-output Show last batch of process output |
| 91 | ;;; c-c c-h comint-dynamic-list-input-ring List input history | ||
| 87 | ;;; send-invisible Read line w/o echo & send to proc | 92 | ;;; send-invisible Read line w/o echo & send to proc |
| 88 | ;;; comint-continue-subjob Useful if you accidentally suspend | 93 | ;;; comint-continue-subjob Useful if you accidentally suspend |
| 89 | ;;; top-level job. | 94 | ;;; top-level job |
| 90 | ;;; comint-mode-hook is the comint mode hook. | 95 | ;;; comint-mode-hook is the comint mode hook. |
| 91 | 96 | ||
| 92 | ;;; Shell Mode Commands: | 97 | ;;; Shell Mode Commands: |
| 93 | ;;; shell Fires up the shell process. | 98 | ;;; shell Fires up the shell process |
| 94 | ;;; tab comint-dynamic-complete Complete a partial file name | 99 | ;;; tab comint-dynamic-complete Complete filename/command/history |
| 95 | ;;; m-? comint-dynamic-list-completions List completions in help buffer | 100 | ;;; m-? comint-dynamic-list-filename-completions List completions in help buffer |
| 96 | ;;; dirs Resync the buffer's dir stack. | 101 | ;;; m-c-f shell-forward-command Forward a shell command |
| 97 | ;;; dirtrack-toggle Turn dir tracking on/off. | 102 | ;;; m-c-b shell-backward-command Backward a shell command |
| 103 | ;;; dirs Resync the buffer's dir stack | ||
| 104 | ;;; dirtrack-toggle Turn dir tracking on/off | ||
| 98 | ;;; | 105 | ;;; |
| 99 | ;;; The shell mode hook is shell-mode-hook | 106 | ;;; The shell mode hook is shell-mode-hook |
| 100 | ;;; The shell-load-hook is run after this file is loaded. | ||
| 101 | ;;; comint-prompt-regexp is initialised to shell-prompt-pattern, for backwards | 107 | ;;; comint-prompt-regexp is initialised to shell-prompt-pattern, for backwards |
| 102 | ;;; compatibility. | 108 | ;;; compatibility. |
| 103 | 109 | ||
| @@ -156,12 +162,43 @@ on lines which don't start with a prompt. | |||
| 156 | 162 | ||
| 157 | This is a fine thing to set in your `.emacs' file.") | 163 | This is a fine thing to set in your `.emacs' file.") |
| 158 | 164 | ||
| 165 | (defvar shell-delimiter-argument-list '("|" "&" "<" ">" "(" ")" ";") | ||
| 166 | "List of characters to recognise as separate arguments. | ||
| 167 | Defaults to \(\"|\" \"&\" \"\(\" \")\" \";\"), which works pretty well. | ||
| 168 | This variable is used to initialise `comint-delimiter-argument-list' in the | ||
| 169 | shell buffer. | ||
| 170 | |||
| 171 | This is a fine thing to set in your `.emacs' file.") | ||
| 172 | |||
| 173 | (defvar shell-command-regexp "\\((.*)\\|[^;&|]\\)+" | ||
| 174 | "*Regexp to match shell commands. | ||
| 175 | Elements of pipes are considered as separate commands, forks and redirections | ||
| 176 | as part of one command.") | ||
| 177 | |||
| 178 | (defvar shell-completion-execonly t | ||
| 179 | "*If non-nil, use executable files only for completion candidates. | ||
| 180 | This mirrors the optional behaviour of the tcsh. | ||
| 181 | |||
| 182 | Detecting executability of files may slow command completion considerably.") | ||
| 183 | |||
| 159 | (defvar shell-popd-regexp "popd" | 184 | (defvar shell-popd-regexp "popd" |
| 160 | "*Regexp to match subshell commands equivalent to popd.") | 185 | "*Regexp to match subshell commands equivalent to popd.") |
| 161 | 186 | ||
| 162 | (defvar shell-pushd-regexp "pushd" | 187 | (defvar shell-pushd-regexp "pushd" |
| 163 | "*Regexp to match subshell commands equivalent to pushd.") | 188 | "*Regexp to match subshell commands equivalent to pushd.") |
| 164 | 189 | ||
| 190 | (defvar shell-pushd-tohome nil | ||
| 191 | "*If non-nil, make pushd with no arg behave as \"pushd ~\" (like cd). | ||
| 192 | This mirrors the optional behaviour of the tcsh.") | ||
| 193 | |||
| 194 | (defvar shell-pushd-dextract nil | ||
| 195 | "*If non-nil, make \"pushd +n\" pop the nth dir to the stack top. | ||
| 196 | This mirrors the optional behaviour of the tcsh.") | ||
| 197 | |||
| 198 | (defvar shell-pushd-dunique nil | ||
| 199 | "*If non-nil, make pushd only add unique directories to the stack. | ||
| 200 | This mirrors the optional behaviour of the tcsh.") | ||
| 201 | |||
| 165 | (defvar shell-cd-regexp "cd" | 202 | (defvar shell-cd-regexp "cd" |
| 166 | "*Regexp to match subshell commands equivalent to cd.") | 203 | "*Regexp to match subshell commands equivalent to cd.") |
| 167 | 204 | ||
| @@ -193,8 +230,11 @@ Thus, this does not include the shell's current directory.") | |||
| 193 | (defvar shell-mode-map '()) | 230 | (defvar shell-mode-map '()) |
| 194 | (cond ((not shell-mode-map) | 231 | (cond ((not shell-mode-map) |
| 195 | (setq shell-mode-map (full-copy-sparse-keymap comint-mode-map)) | 232 | (setq shell-mode-map (full-copy-sparse-keymap comint-mode-map)) |
| 233 | (define-key shell-mode-map "\C-c\C-f" 'shell-forward-command) | ||
| 234 | (define-key shell-mode-map "\C-c\C-b" 'shell-backward-command) | ||
| 196 | (define-key shell-mode-map "\t" 'comint-dynamic-complete) | 235 | (define-key shell-mode-map "\t" 'comint-dynamic-complete) |
| 197 | (define-key shell-mode-map "\M-?" 'comint-dynamic-list-completions))) | 236 | (define-key shell-mode-map "\M-?" |
| 237 | 'comint-dynamic-list-filename-completions))) | ||
| 198 | 238 | ||
| 199 | (defvar shell-mode-hook '() | 239 | (defvar shell-mode-hook '() |
| 200 | "*Hook for customising Shell mode.") | 240 | "*Hook for customising Shell mode.") |
| @@ -224,31 +264,60 @@ M-x dirtrack-toggle turns directory tracking on and off. | |||
| 224 | 264 | ||
| 225 | \\{shell-mode-map} | 265 | \\{shell-mode-map} |
| 226 | Customization: Entry to this mode runs the hooks on `comint-mode-hook' and | 266 | Customization: Entry to this mode runs the hooks on `comint-mode-hook' and |
| 227 | `shell-mode-hook' (in that order). | 267 | `shell-mode-hook' (in that order). After each shell output, the hooks on |
| 268 | `comint-output-filter-hook' are run. | ||
| 228 | 269 | ||
| 229 | Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp' | 270 | Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp' |
| 230 | are used to match their respective commands." | 271 | are used to match their respective commands, while `shell-pushd-tohome', |
| 272 | `shell-pushd-dextract' and `shell-pushd-dunique' control the behaviour of the | ||
| 273 | relevant command. | ||
| 274 | |||
| 275 | Variables `comint-completion-autolist', `comint-completion-addsuffix' and | ||
| 276 | `comint-completion-recexact' control the behaviour of file name, command name | ||
| 277 | and variable name completion. Variable `shell-completion-execonly' controls | ||
| 278 | the behaviour of command name completion. | ||
| 279 | |||
| 280 | Variables `comint-input-ring-file-name' and `comint-input-autoexpand' control | ||
| 281 | the initialisation of the input ring history, and history expansion. | ||
| 282 | |||
| 283 | Variables `comint-output-filter-hook', `comint-scroll-to-bottom-on-input', and | ||
| 284 | `comint-scroll-to-bottom-on-output' control whether input and output cause the | ||
| 285 | window to scroll to the end of the buffer." | ||
| 231 | (interactive) | 286 | (interactive) |
| 232 | (comint-mode) | 287 | (comint-mode) |
| 233 | (setq comint-prompt-regexp shell-prompt-pattern) | ||
| 234 | (setq major-mode 'shell-mode) | 288 | (setq major-mode 'shell-mode) |
| 235 | (setq mode-name "Shell") | 289 | (setq mode-name "Shell") |
| 236 | (use-local-map shell-mode-map) | 290 | (use-local-map shell-mode-map) |
| 291 | (setq comint-prompt-regexp shell-prompt-pattern) | ||
| 292 | (setq comint-delimiter-argument-list shell-delimiter-argument-list) | ||
| 293 | (setq comint-after-partial-filename-command 'shell-after-partial-filename) | ||
| 294 | (setq comint-get-current-command 'shell-get-current-command) | ||
| 295 | (setq comint-dynamic-complete-command-command 'shell-dynamic-complete-command) | ||
| 296 | (make-local-variable 'paragraph-start) | ||
| 297 | (setq paragraph-start comint-prompt-regexp) | ||
| 237 | (make-local-variable 'shell-dirstack) | 298 | (make-local-variable 'shell-dirstack) |
| 238 | (setq shell-dirstack nil) | 299 | (setq shell-dirstack nil) |
| 239 | (setq shell-last-dir nil) | 300 | (setq shell-last-dir nil) |
| 240 | (make-local-variable 'shell-dirtrackp) | 301 | (make-local-variable 'shell-dirtrackp) |
| 241 | (setq shell-dirtrackp t) | 302 | (setq shell-dirtrackp t) |
| 242 | (setq comint-input-sentinel 'shell-directory-tracker) | 303 | (setq comint-input-sentinel 'shell-directory-tracker) |
| 243 | (run-hooks 'shell-mode-hook)) | 304 | ;; shell-dependent assignments. |
| 244 | 305 | (let ((shell (car (process-command (get-buffer-process (current-buffer)))))) | |
| 306 | (setq comint-input-ring-file-name | ||
| 307 | (or (getenv "HISTFILE") | ||
| 308 | (cond ((string-match "csh$" shell) "~/.history") | ||
| 309 | ((string-match "bash$" shell) "~/.bash_history") | ||
| 310 | ((string-match "ksh$" shell) "~/.sh_history") | ||
| 311 | (t "~/.history"))))) | ||
| 312 | (run-hooks 'shell-mode-hook) | ||
| 313 | (comint-read-input-ring) | ||
| 314 | (shell-dirstack-message)) | ||
| 245 | 315 | ||
| 246 | ;;;###autoload | 316 | ;;;###autoload |
| 247 | (defun shell () | 317 | (defun shell () |
| 248 | "Run an inferior shell, with I/O through buffer *shell*. | 318 | "Run an inferior shell, with I/O through buffer *shell*. |
| 249 | If buffer exists but shell process is not running, make new shell. | 319 | If buffer exists but shell process is not running, make new shell. |
| 250 | If buffer exists and shell process is running, | 320 | If buffer exists and shell process is running, just switch to buffer `*shell*'. |
| 251 | just switch to buffer `*shell*'. | ||
| 252 | Program used comes from variable `explicit-shell-file-name', | 321 | Program used comes from variable `explicit-shell-file-name', |
| 253 | or (if that is nil) from the ESHELL environment variable, | 322 | or (if that is nil) from the ESHELL environment variable, |
| 254 | or else from SHELL if there is no ESHELL. | 323 | or else from SHELL if there is no ESHELL. |
| @@ -266,22 +335,21 @@ Otherwise, one argument `-i' is passed to the shell. | |||
| 266 | 335 | ||
| 267 | \(Type \\[describe-mode] in the shell buffer for a list of commands.)" | 336 | \(Type \\[describe-mode] in the shell buffer for a list of commands.)" |
| 268 | (interactive) | 337 | (interactive) |
| 269 | (cond ((not (comint-check-proc "*shell*")) | 338 | (if (not (comint-check-proc "*shell*")) |
| 270 | (let* ((prog (or explicit-shell-file-name | 339 | (let* ((prog (or explicit-shell-file-name |
| 271 | (getenv "ESHELL") | 340 | (getenv "ESHELL") |
| 272 | (getenv "SHELL") | 341 | (getenv "SHELL") |
| 273 | "/bin/sh")) | 342 | "/bin/sh")) |
| 274 | (name (file-name-nondirectory prog)) | 343 | (name (file-name-nondirectory prog)) |
| 275 | (startfile (concat "~/.emacs_" name)) | 344 | (startfile (concat "~/.emacs_" name)) |
| 276 | (xargs-name (intern-soft (concat "explicit-" name "-args")))) | 345 | (xargs-name (intern-soft (concat "explicit-" name "-args")))) |
| 277 | (set-buffer (apply 'make-comint "shell" prog | 346 | (set-buffer (apply 'make-comint "shell" prog |
| 278 | (if (file-exists-p startfile) startfile) | 347 | (if (file-exists-p startfile) startfile) |
| 279 | (if (and xargs-name (boundp xargs-name)) | 348 | (if (and xargs-name (boundp xargs-name)) |
| 280 | (symbol-value xargs-name) | 349 | (symbol-value xargs-name) |
| 281 | '("-i")))) | 350 | '("-i")))) |
| 282 | (shell-mode)))) | 351 | (shell-mode))) |
| 283 | (switch-to-buffer "*shell*")) | 352 | (switch-to-buffer "*shell*")) |
| 284 | |||
| 285 | 353 | ||
| 286 | ;;; Directory tracking | 354 | ;;; Directory tracking |
| 287 | ;;; =========================================================================== | 355 | ;;; =========================================================================== |
| @@ -293,12 +361,12 @@ Otherwise, one argument `-i' is passed to the shell. | |||
| 293 | ;;; This is basically a fragile hack, although it's more accurate than | 361 | ;;; This is basically a fragile hack, although it's more accurate than |
| 294 | ;;; the version in Emacs 18's shell.el. It has the following failings: | 362 | ;;; the version in Emacs 18's shell.el. It has the following failings: |
| 295 | ;;; 1. It doesn't know about the cdpath shell variable. | 363 | ;;; 1. It doesn't know about the cdpath shell variable. |
| 296 | ;;; 2. It only spots the first command in a command sequence. E.g., it will | 364 | ;;; 2. It cannot infallibly deal with command sequences, though it does well |
| 297 | ;;; miss the cd in "ls; cd foo" | 365 | ;;; with these and with ignoring commands forked in another shell with ()s. |
| 298 | ;;; 3. More generally, any complex command (like ";" sequencing) is going to | 366 | ;;; 3. More generally, any complex command is going to throw it. Otherwise, |
| 299 | ;;; throw it. Otherwise, you'd have to build an entire shell interpreter in | 367 | ;;; you'd have to build an entire shell interpreter in emacs lisp. Failing |
| 300 | ;;; emacs lisp. Failing that, there's no way to catch shell commands where | 368 | ;;; that, there's no way to catch shell commands where cd's are buried |
| 301 | ;;; cd's are buried inside conditional expressions, aliases, and so forth. | 369 | ;;; inside conditional expressions, aliases, and so forth. |
| 302 | ;;; | 370 | ;;; |
| 303 | ;;; The whole approach is a crock. Shell aliases mess it up. File sourcing | 371 | ;;; The whole approach is a crock. Shell aliases mess it up. File sourcing |
| 304 | ;;; messes it up. You run other processes under the shell; these each have | 372 | ;;; messes it up. You run other processes under the shell; these each have |
| @@ -323,12 +391,6 @@ Otherwise, one argument `-i' is passed to the shell. | |||
| 323 | ;;; replace it with a process filter that watches for and strips out | 391 | ;;; replace it with a process filter that watches for and strips out |
| 324 | ;;; these messages. | 392 | ;;; these messages. |
| 325 | 393 | ||
| 326 | ;;; REGEXP is a regular expression. STR is a string. START is a fixnum. | ||
| 327 | ;;; Returns T if REGEXP matches STR where the match is anchored to start | ||
| 328 | ;;; at position START in STR. Sort of like LOOKING-AT for strings. | ||
| 329 | (defun shell-front-match (regexp str start) | ||
| 330 | (eq start (string-match regexp str start))) | ||
| 331 | |||
| 332 | (defun shell-directory-tracker (str) | 394 | (defun shell-directory-tracker (str) |
| 333 | "Tracks cd, pushd and popd commands issued to the shell. | 395 | "Tracks cd, pushd and popd commands issued to the shell. |
| 334 | This function is called on each input passed to the shell. | 396 | This function is called on each input passed to the shell. |
| @@ -338,118 +400,107 @@ default directory to track these commands. | |||
| 338 | You may toggle this tracking on and off with M-x dirtrack-toggle. | 400 | You may toggle this tracking on and off with M-x dirtrack-toggle. |
| 339 | If emacs gets confused, you can resync with the shell with M-x dirs. | 401 | If emacs gets confused, you can resync with the shell with M-x dirs. |
| 340 | 402 | ||
| 341 | See variables `shell-cd-regexp', `shell-pushd-regexp', and `shell-popd-regexp'. | 403 | See variables `shell-cd-regexp', `shell-pushd-regexp', and `shell-popd-regexp', |
| 404 | while `shell-pushd-tohome', `shell-pushd-dextract' and `shell-pushd-dunique' | ||
| 405 | control the behaviour of the relevant command. | ||
| 406 | |||
| 342 | Environment variables are expanded, see function `substitute-in-file-name'." | 407 | Environment variables are expanded, see function `substitute-in-file-name'." |
| 343 | (condition-case err | 408 | (if shell-dirtrackp |
| 344 | (cond (shell-dirtrackp | 409 | ;; We fail gracefully if we think the command will fail in the shell. |
| 345 | (string-match "^\\s *" str) ; skip whitespace | 410 | (condition-case chdir-failure |
| 346 | (let ((bos (match-end 0)) | 411 | (let ((start (progn (string-match "^[;\\s ]*" str) ; skip whitespace |
| 347 | (x nil)) | 412 | (match-end 0))) |
| 348 | (cond ((setq x (shell-match-cmd-w/optional-arg shell-popd-regexp | 413 | end cmd arg1) |
| 349 | str bos)) | 414 | (while (string-match shell-command-regexp str start) |
| 350 | (shell-process-popd (substitute-in-file-name x))) | 415 | (setq end (match-end 0) |
| 351 | ((setq x (shell-match-cmd-w/optional-arg shell-pushd-regexp | 416 | cmd (comint-arguments (substring str start end) 0 0) |
| 352 | str bos)) | 417 | arg1 (comint-arguments (substring str start end) 1 1)) |
| 353 | (shell-process-pushd (substitute-in-file-name x))) | 418 | (cond ((eq (string-match shell-popd-regexp cmd) 0) |
| 354 | ((setq x (shell-match-cmd-w/optional-arg shell-cd-regexp | 419 | (shell-process-popd (substitute-in-file-name arg1))) |
| 355 | str bos)) | 420 | ((eq (string-match shell-pushd-regexp cmd) 0) |
| 356 | (shell-process-cd (substitute-in-file-name x))))))) | 421 | (shell-process-pushd (substitute-in-file-name arg1))) |
| 357 | (error (message (car (cdr err)))))) | 422 | ((eq (string-match shell-cd-regexp cmd) 0) |
| 358 | 423 | (shell-process-cd (substitute-in-file-name arg1)))) | |
| 359 | 424 | (setq start (progn (string-match "[;\\s ]*" str end) ; skip again | |
| 360 | ;;; Try to match regexp CMD to string, anchored at position START. | 425 | (match-end 0))))) |
| 361 | ;;; CMD may be followed by a single argument. If a match, then return | 426 | (error (message "Couldn't cd"))))) |
| 362 | ;;; the argument, if there is one, or the empty string if not. If | 427 | |
| 363 | ;;; no match, return nil. | 428 | |
| 364 | 429 | ;; Like `cd', but prepends comint-file-name-prefix to absolute names. | |
| 365 | (defun shell-match-cmd-w/optional-arg (cmd str start) | ||
| 366 | (and (shell-front-match cmd str start) | ||
| 367 | (let ((eoc (match-end 0))) ; end of command | ||
| 368 | (cond ((shell-front-match "\\s *\\(\;\\|$\\)" str eoc) | ||
| 369 | "") ; no arg | ||
| 370 | ((shell-front-match "\\s +\\([^ \t\;]+\\)\\s *\\(\;\\|$\\)" | ||
| 371 | str eoc) | ||
| 372 | (substring str (match-beginning 1) (match-end 1))) ; arg | ||
| 373 | (t nil))))) ; something else. | ||
| 374 | ;;; The first regexp is [optional whitespace, (";" or the end of string)]. | ||
| 375 | ;;; The second regexp is [whitespace, (an arg), optional whitespace, | ||
| 376 | ;;; (";" or end of string)]. | ||
| 377 | |||
| 378 | |||
| 379 | ;; Like `cd', but prepends comint-filename-prefix to absolute names. | ||
| 380 | (defsubst shell-cd (directory) | 430 | (defsubst shell-cd (directory) |
| 381 | (if (file-name-absolute-p directory) | 431 | (if (file-name-absolute-p directory) |
| 382 | (cd-absolute (concat comint-filename-prefix directory)) | 432 | (cd-absolute (concat comint-file-name-prefix directory)) |
| 383 | (cd directory))) | 433 | (cd directory))) |
| 384 | 434 | ||
| 385 | ;;; popd [+n] | 435 | ;;; popd [+n] |
| 386 | (defun shell-process-popd (arg) | 436 | (defun shell-process-popd (arg) |
| 387 | (let ((num (if (zerop (length arg)) 0 ; no arg means +0 | 437 | (let ((num (or (shell-extract-num arg) 0))) |
| 388 | (shell-extract-num arg)))) | 438 | (cond ((and num (= num 0) shell-dirstack) |
| 389 | (if (and num (< num (length shell-dirstack))) | 439 | (shell-cd (car shell-dirstack)) |
| 390 | (if (= num 0) ; condition-case because the CD could lose. | 440 | (setq shell-dirstack (cdr shell-dirstack)) |
| 391 | (condition-case nil (progn (shell-cd (car shell-dirstack)) | 441 | (shell-dirstack-message)) |
| 392 | (setq shell-dirstack | 442 | ((and num (> num 0) (<= num (length shell-dirstack))) |
| 393 | (cdr shell-dirstack)) | 443 | (let* ((ds (cons nil shell-dirstack)) |
| 394 | (shell-dirstack-message)) | 444 | (cell (nthcdr (1- num) ds))) |
| 395 | (error (message "Couldn't cd."))) | 445 | (rplacd cell (cdr (cdr cell))) |
| 396 | (let* ((ds (cons nil shell-dirstack)) | 446 | (setq shell-dirstack (cdr ds)) |
| 397 | (cell (nthcdr (- num 1) ds))) | 447 | (shell-dirstack-message))) |
| 398 | (rplacd cell (cdr (cdr cell))) | 448 | (t |
| 399 | (setq shell-dirstack (cdr ds)) | 449 | (error (message "Couldn't popd.")))))) |
| 400 | (shell-dirstack-message))) | ||
| 401 | (message "Bad popd.")))) | ||
| 402 | |||
| 403 | 450 | ||
| 404 | ;;; cd [dir] | 451 | ;;; cd [dir] |
| 405 | (defun shell-process-cd (arg) | 452 | (defun shell-process-cd (arg) |
| 406 | (condition-case nil | 453 | (let ((new-dir (cond ((zerop (length arg)) (getenv "HOME")) |
| 407 | (let ((new-dir (cond | 454 | ((string-equal "-" arg) shell-last-dir) |
| 408 | ((zerop (length arg)) (getenv "HOME")) | 455 | (t arg)))) |
| 409 | ((string-equal "-" arg) shell-last-dir) | 456 | (setq shell-last-dir default-directory) |
| 410 | (t arg)))) | 457 | (shell-cd new-dir) |
| 411 | (setq shell-last-dir default-directory) | 458 | (shell-dirstack-message))) |
| 412 | (shell-cd new-dir) | ||
| 413 | (shell-dirstack-message)) | ||
| 414 | (error (message "Couldn't cd.")))) | ||
| 415 | 459 | ||
| 416 | ;;; pushd [+n | dir] | 460 | ;;; pushd [+n | dir] |
| 417 | (defun shell-process-pushd (arg) | 461 | (defun shell-process-pushd (arg) |
| 418 | (if (zerop (length arg)) | 462 | (let ((num (shell-extract-num arg))) |
| 419 | ;; no arg -- swap pwd and car of shell stack | 463 | (cond ((zerop (length arg)) |
| 420 | (condition-case nil (if shell-dirstack | 464 | ;; no arg -- swap pwd and car of stack unless shell-pushd-tohome |
| 421 | (let ((old default-directory)) | 465 | (cond (shell-pushd-tohome |
| 422 | (shell-cd (car shell-dirstack)) | 466 | (shell-process-pushd "~")) |
| 423 | (setq shell-dirstack | 467 | (shell-dirstack |
| 424 | (cons old (cdr shell-dirstack))) | 468 | (let ((old default-directory)) |
| 425 | (shell-dirstack-message)) | 469 | (shell-cd (car shell-dirstack)) |
| 426 | (message "Directory stack empty.")) | 470 | (setq shell-dirstack |
| 427 | (error | 471 | (cons old (cdr shell-dirstack))) |
| 428 | (message "Couldn't cd."))) | 472 | (shell-dirstack-message))) |
| 429 | 473 | (t | |
| 430 | (let ((num (shell-extract-num arg))) | 474 | (message "Directory stack empty.")))) |
| 431 | (if num ; pushd +n | 475 | ((numberp num) |
| 432 | (if (> num (length shell-dirstack)) | 476 | ;; pushd +n |
| 433 | (message "Directory stack not that deep.") | 477 | (cond ((> num (length shell-dirstack)) |
| 434 | (let* ((ds (cons default-directory shell-dirstack)) | 478 | (message "Directory stack not that deep.")) |
| 435 | (dslen (length ds)) | 479 | ((= num 0) |
| 436 | (front (nthcdr num ds)) | 480 | (error (message "Couldn't cd."))) |
| 437 | (back (reverse (nthcdr (- dslen num) (reverse ds)))) | 481 | (shell-pushd-dextract |
| 438 | (new-ds (append front back))) | 482 | (let ((dir (nth (1- num) shell-dirstack))) |
| 439 | (condition-case nil | 483 | (shell-process-popd arg) |
| 440 | (progn (shell-cd (car new-ds)) | 484 | (shell-process-pushd default-directory) |
| 441 | (setq shell-dirstack (cdr new-ds)) | 485 | (shell-cd dir) |
| 442 | (shell-dirstack-message)) | 486 | (shell-dirstack-message))) |
| 443 | (error (message "Couldn't cd."))))) | 487 | (t |
| 444 | 488 | (let* ((ds (cons default-directory shell-dirstack)) | |
| 445 | ;; pushd <dir> | 489 | (dslen (length ds)) |
| 446 | (let ((old-wd default-directory)) | 490 | (front (nthcdr num ds)) |
| 447 | (condition-case nil | 491 | (back (reverse (nthcdr (- dslen num) (reverse ds)))) |
| 448 | (progn (shell-cd arg) | 492 | (new-ds (append front back))) |
| 449 | (setq shell-dirstack | 493 | (shell-cd (car new-ds)) |
| 450 | (cons old-wd shell-dirstack)) | 494 | (setq shell-dirstack (cdr new-ds)) |
| 451 | (shell-dirstack-message)) | 495 | (shell-dirstack-message))))) |
| 452 | (error (message "Couldn't cd.")))))))) | 496 | (t |
| 497 | ;; pushd <dir> | ||
| 498 | (let ((old-wd default-directory)) | ||
| 499 | (shell-cd arg) | ||
| 500 | (if (or (null shell-pushd-dunique) | ||
| 501 | (not (member old-wd shell-dirstack))) | ||
| 502 | (setq shell-dirstack (cons old-wd shell-dirstack))) | ||
| 503 | (shell-dirstack-message)))))) | ||
| 453 | 504 | ||
| 454 | ;; If STR is of the form +n, for n>0, return n. Otherwise, nil. | 505 | ;; If STR is of the form +n, for n>0, return n. Otherwise, nil. |
| 455 | (defun shell-extract-num (str) | 506 | (defun shell-extract-num (str) |
| @@ -461,8 +512,7 @@ Environment variables are expanded, see function `substitute-in-file-name'." | |||
| 461 | "Turn directory tracking on and off in a shell buffer." | 512 | "Turn directory tracking on and off in a shell buffer." |
| 462 | (interactive) | 513 | (interactive) |
| 463 | (setq shell-dirtrackp (not shell-dirtrackp)) | 514 | (setq shell-dirtrackp (not shell-dirtrackp)) |
| 464 | (message "directory tracking %s." | 515 | (message "Directory tracking %s" (if shell-dirtrackp "ON" "OFF"))) |
| 465 | (if shell-dirtrackp "ON" "OFF"))) | ||
| 466 | 516 | ||
| 467 | ;;; For your typing convenience: | 517 | ;;; For your typing convenience: |
| 468 | (defalias 'dirtrack-toggle 'shell-dirtrack-toggle) | 518 | (defalias 'dirtrack-toggle 'shell-dirtrack-toggle) |
| @@ -495,7 +545,7 @@ command again." | |||
| 495 | (goto-char pt))) | 545 | (goto-char pt))) |
| 496 | (goto-char pmark) (delete-char 1) ; remove the extra newline | 546 | (goto-char pmark) (delete-char 1) ; remove the extra newline |
| 497 | ;; That's the dirlist. grab it & parse it. | 547 | ;; That's the dirlist. grab it & parse it. |
| 498 | (let* ((dl (buffer-substring (match-beginning 0) (- (match-end 0) 1))) | 548 | (let* ((dl (buffer-substring (match-beginning 0) (1- (match-end 0)))) |
| 499 | (dl-len (length dl)) | 549 | (dl-len (length dl)) |
| 500 | (ds '()) ; new dir stack | 550 | (ds '()) ; new dir stack |
| 501 | (i 0)) | 551 | (i 0)) |
| @@ -507,7 +557,7 @@ command again." | |||
| 507 | (setq i (match-end 0))) | 557 | (setq i (match-end 0))) |
| 508 | (let ((ds (reverse ds))) | 558 | (let ((ds (reverse ds))) |
| 509 | (condition-case nil | 559 | (condition-case nil |
| 510 | (progn (shell-cd (car ds)) | 560 | (progn (cd (car ds)) |
| 511 | (setq shell-dirstack (cdr ds)) | 561 | (setq shell-dirstack (cdr ds)) |
| 512 | (shell-dirstack-message)) | 562 | (shell-dirstack-message)) |
| 513 | (error (message "Couldn't cd."))))))) | 563 | (error (message "Couldn't cd."))))))) |
| @@ -524,57 +574,145 @@ command again." | |||
| 524 | (defun shell-dirstack-message () | 574 | (defun shell-dirstack-message () |
| 525 | (let* ((msg "") | 575 | (let* ((msg "") |
| 526 | (ds (cons default-directory shell-dirstack)) | 576 | (ds (cons default-directory shell-dirstack)) |
| 527 | (home (expand-file-name (concat comint-filename-prefix "~/"))) | 577 | (home (expand-file-name (concat comint-file-name-prefix "~/"))) |
| 528 | (homelen (length home))) | 578 | (homelen (length home))) |
| 529 | (while ds | 579 | (while ds |
| 530 | (let ((dir (car ds))) | 580 | (let ((dir (car ds))) |
| 531 | (and (>= (length dir) homelen) (string= home (substring dir 0 homelen)) | 581 | (and (>= (length dir) homelen) (string= home (substring dir 0 homelen)) |
| 532 | (setq dir (concat "~/" (substring dir homelen)))) | 582 | (setq dir (concat "~/" (substring dir homelen)))) |
| 533 | ;; Strip off comint-filename-prefix if present. | 583 | ;; Strip off comint-file-name-prefix if present. |
| 534 | (and comint-filename-prefix | 584 | (and comint-file-name-prefix |
| 535 | (>= (length dir) (length comint-filename-prefix)) | 585 | (>= (length dir) (length comint-file-name-prefix)) |
| 536 | (string= comint-filename-prefix | 586 | (string= comint-file-name-prefix |
| 537 | (substring dir 0 (length comint-filename-prefix))) | 587 | (substring dir 0 (length comint-file-name-prefix))) |
| 538 | (setq dir (substring dir (length comint-filename-prefix))) | 588 | (setq dir (substring dir (length comint-file-name-prefix))) |
| 539 | (setcar ds dir)) | 589 | (setcar ds dir)) |
| 540 | (if (string-equal dir "~/") (setq dir "~")) | 590 | (setq msg (concat msg (directory-file-name dir) " ")) |
| 541 | (setq msg (concat msg dir " ")) | ||
| 542 | (setq ds (cdr ds)))) | 591 | (setq ds (cdr ds)))) |
| 543 | (message msg))) | 592 | (message msg))) |
| 544 | |||
| 545 | |||
| 546 | 593 | ||
| 547 | ;;; Interfacing to client packages (and converting them) | 594 | (defun shell-forward-command (&optional arg) |
| 548 | ;;; Notes from when this was called cmushell, and was not the standard emacs | 595 | "Move forward across ARG shell command(s). Does not cross lines. |
| 549 | ;;; shell package. Many of the conversions discussed here have been done. | 596 | See `shell-command-regexp'." |
| 550 | ;;;============================================================================ | 597 | (interactive "p") |
| 551 | ;;; Several gnu packages (tex-mode, background, dbx, gdb, kermit, prolog, | 598 | (let ((limit (save-excursion (end-of-line nil) (point)))) |
| 552 | ;;; telnet are some) use the shell package as clients. Most of them would | 599 | (if (re-search-forward (concat shell-command-regexp "\\([;&|][\\s ]*\\)+") |
| 553 | ;;; be better off using the comint package directly, but they predate it. | 600 | limit 'move arg) |
| 554 | ;;; The catch is that most of these packages (dbx, gdb, prolog, telnet) | 601 | (skip-syntax-backward "^\\s ")))) |
| 555 | ;;; assume total knowledge of all the local variables that shell mode | 602 | |
| 556 | ;;; functions depend on. So they (kill-all-local-variables), then create | 603 | |
| 557 | ;;; the few local variables that shell.el functions depend on. Alas, | 604 | (defun shell-backward-command (&optional arg) |
| 558 | ;;; cmushell.el functions depend on a different set of vars (for example, | 605 | "Move backward across ARG shell command(s). Does not cross lines. |
| 559 | ;;; the input history ring is a local variable in cmushell.el's shell mode, | 606 | See `shell-command-regexp'." |
| 560 | ;;; whereas there is no input history ring in shell.el's shell mode). | 607 | (interactive "p") |
| 561 | ;;; So we have a situation where the greater functionality of cmushell.el | 608 | (let ((limit (save-excursion (comint-bol nil) (point)))) |
| 562 | ;;; is biting us -- you can't just replace shell will cmushell. | 609 | (skip-syntax-backward "\\s " limit) |
| 563 | ;;; | 610 | (if (re-search-backward |
| 564 | ;;; Altering these packages to use comint mode directly should *greatly* | 611 | (format "[;&|]+[\\s ]*\\(%s\\)" shell-command-regexp) limit 'move arg) |
| 565 | ;;; improve their functionality, and is actually pretty easy. It's | 612 | (progn (goto-char (match-beginning 1)) |
| 566 | ;;; mostly a matter of renaming a few variable names. See comint.el for more. | 613 | (skip-syntax-backward "^\\s "))))) |
| 567 | ;;; -Olin | 614 | |
| 568 | 615 | ||
| 569 | 616 | (defun shell-get-current-command () | |
| 570 | 617 | "Function that returns the current command including arguments." | |
| 618 | (save-excursion | ||
| 619 | (if (looking-at "\\s *[^;&|]") | ||
| 620 | (goto-char (match-end 0))) | ||
| 621 | (buffer-substring | ||
| 622 | (progn (shell-backward-command 1) (point)) | ||
| 623 | (progn (shell-forward-command 1) (if (eolp) (point) (match-end 1)))))) | ||
| 624 | |||
| 625 | |||
| 626 | (defun shell-after-partial-filename () | ||
| 627 | "Returns t if point is after a file name. | ||
| 628 | File names are assumed to contain `/'s or not be the first item in the command. | ||
| 629 | |||
| 630 | See also `shell-backward-command'." | ||
| 631 | (let ((filename (comint-match-partial-filename))) | ||
| 632 | (or (save-match-data (string-match "/" filename)) | ||
| 633 | (not (eq (match-beginning 0) | ||
| 634 | (save-excursion (shell-backward-command 1) (point))))))) | ||
| 635 | |||
| 636 | |||
| 637 | (defun shell-dynamic-complete-command () | ||
| 638 | "Dynamically complete the command at point. | ||
| 639 | This function is similar to `comint-dynamic-complete-filename', except that it | ||
| 640 | searches `exec-path' (minus the trailing emacs library path) for completion | ||
| 641 | candidates. Note that this may not be the same as the shell's idea of the | ||
| 642 | path. | ||
| 643 | |||
| 644 | Completion is dependent on the value of `shell-completion-execonly', plus | ||
| 645 | those that effect file completion. See `comint-dynamic-complete-filename'." | ||
| 646 | (interactive) | ||
| 647 | (let* ((completion-ignore-case nil) | ||
| 648 | (filename (comint-match-partial-filename)) | ||
| 649 | (pathnondir (file-name-nondirectory filename)) | ||
| 650 | (paths (cdr (reverse exec-path))) | ||
| 651 | (cwd (file-name-as-directory (expand-file-name default-directory))) | ||
| 652 | (ignored-extensions | ||
| 653 | (mapconcat (function (lambda (x) (concat (regexp-quote x) "$"))) | ||
| 654 | completion-ignored-extensions "\\|")) | ||
| 655 | (path "") (comps-in-path ()) (file "") (filepath "") (completions ())) | ||
| 656 | ;; Go thru each path in the search path, finding completions. | ||
| 657 | (while paths | ||
| 658 | (setq path (file-name-as-directory (comint-directory (or (car paths) "."))) | ||
| 659 | comps-in-path (and (file-accessible-directory-p path) | ||
| 660 | (file-name-all-completions pathnondir path))) | ||
| 661 | ;; Go thru each completion found, to see whether it should be used. | ||
| 662 | (while comps-in-path | ||
| 663 | (setq file (car comps-in-path) | ||
| 664 | filepath (concat path file)) | ||
| 665 | (if (and (not (member file completions)) | ||
| 666 | (not (string-match ignored-extensions file)) | ||
| 667 | (or (string-equal path cwd) | ||
| 668 | (not (file-directory-p filepath))) | ||
| 669 | (or (null shell-completion-execonly) | ||
| 670 | (file-executable-p filepath))) | ||
| 671 | (setq completions (cons file completions))) | ||
| 672 | (setq comps-in-path (cdr comps-in-path))) | ||
| 673 | (setq paths (cdr paths))) | ||
| 674 | ;; OK, we've got a list of completions. | ||
| 675 | (cond ((null completions) | ||
| 676 | (message "No completions of %s" filename) | ||
| 677 | (ding)) | ||
| 678 | ((= 1 (length completions)) ; Gotcha! | ||
| 679 | (let ((completion (car completions))) | ||
| 680 | (if (string-equal completion pathnondir) | ||
| 681 | (message "Sole completion") | ||
| 682 | (insert (substring (directory-file-name completion) | ||
| 683 | (length pathnondir))) | ||
| 684 | (message "Completed")) | ||
| 685 | (if comint-completion-addsuffix | ||
| 686 | (insert (if (file-directory-p completion) "/" " "))))) | ||
| 687 | (t ; There's no unique completion. | ||
| 688 | (let ((completion | ||
| 689 | (try-completion pathnondir (mapcar (function (lambda (x) | ||
| 690 | (list x))) | ||
| 691 | completions)))) | ||
| 692 | ;; Insert the longest substring. | ||
| 693 | (insert (substring (directory-file-name completion) | ||
| 694 | (length pathnondir))) | ||
| 695 | (cond ((and comint-completion-recexact comint-completion-addsuffix | ||
| 696 | (string-equal pathnondir completion) | ||
| 697 | (member completion completions)) | ||
| 698 | ;; It's not unique, but user wants shortest match. | ||
| 699 | (insert (if (file-directory-p completion) "/" " ")) | ||
| 700 | (message "Completed shortest")) | ||
| 701 | ((or comint-completion-autolist | ||
| 702 | (string-equal pathnondir completion)) | ||
| 703 | ;; It's not unique, list possible completions. | ||
| 704 | (comint-dynamic-list-completions completions)) | ||
| 705 | (t | ||
| 706 | (message "Partially completed")))))))) | ||
| 707 | |||
| 571 | ;;; Do the user's customization... | 708 | ;;; Do the user's customization... |
| 572 | ;;;=============================== | 709 | ;;; |
| 573 | (defvar shell-load-hook nil | 710 | ;;; Isn't this what eval-after-load is for? |
| 574 | "This hook is run when shell is loaded in. | 711 | ;;;(defvar shell-load-hook nil |
| 575 | This is a good place to put keybindings.") | 712 | ;;; "This hook is run when shell is loaded in. |
| 576 | 713 | ;;;This is a good place to put keybindings.") | |
| 577 | (run-hooks 'shell-load-hook) | 714 | ;;; |
| 715 | ;;;(run-hooks 'shell-load-hook) | ||
| 578 | 716 | ||
| 579 | (provide 'shell) | 717 | (provide 'shell) |
| 580 | 718 | ||