aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard M. Stallman1993-10-22 02:53:24 +0000
committerRichard M. Stallman1993-10-22 02:53:24 +0000
commitd76b71afe39a724f5fc819c84f9bc9e34df34240 (patch)
tree89a01279e029c45a7718680a73cd231dd34f5ea8
parent20ddfff7191bdbfbb419964a1ce129e050c4e382 (diff)
downloademacs-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.el518
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
157This is a fine thing to set in your `.emacs' file.") 163This 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.
167Defaults to \(\"|\" \"&\" \"\(\" \")\" \";\"), which works pretty well.
168This variable is used to initialise `comint-delimiter-argument-list' in the
169shell buffer.
170
171This is a fine thing to set in your `.emacs' file.")
172
173(defvar shell-command-regexp "\\((.*)\\|[^;&|]\\)+"
174 "*Regexp to match shell commands.
175Elements of pipes are considered as separate commands, forks and redirections
176as part of one command.")
177
178(defvar shell-completion-execonly t
179 "*If non-nil, use executable files only for completion candidates.
180This mirrors the optional behaviour of the tcsh.
181
182Detecting 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).
192This 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.
196This 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.
200This 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}
226Customization: Entry to this mode runs the hooks on `comint-mode-hook' and 266Customization: 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
229Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp' 270Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp'
230are used to match their respective commands." 271are used to match their respective commands, while `shell-pushd-tohome',
272`shell-pushd-dextract' and `shell-pushd-dunique' control the behaviour of the
273relevant command.
274
275Variables `comint-completion-autolist', `comint-completion-addsuffix' and
276`comint-completion-recexact' control the behaviour of file name, command name
277and variable name completion. Variable `shell-completion-execonly' controls
278the behaviour of command name completion.
279
280Variables `comint-input-ring-file-name' and `comint-input-autoexpand' control
281the initialisation of the input ring history, and history expansion.
282
283Variables `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
285window 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*.
249If buffer exists but shell process is not running, make new shell. 319If buffer exists but shell process is not running, make new shell.
250If buffer exists and shell process is running, 320If buffer exists and shell process is running, just switch to buffer `*shell*'.
251 just switch to buffer `*shell*'.
252Program used comes from variable `explicit-shell-file-name', 321Program 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.
334This function is called on each input passed to the shell. 396This function is called on each input passed to the shell.
@@ -338,118 +400,107 @@ default directory to track these commands.
338You may toggle this tracking on and off with M-x dirtrack-toggle. 400You may toggle this tracking on and off with M-x dirtrack-toggle.
339If emacs gets confused, you can resync with the shell with M-x dirs. 401If emacs gets confused, you can resync with the shell with M-x dirs.
340 402
341See variables `shell-cd-regexp', `shell-pushd-regexp', and `shell-popd-regexp'. 403See variables `shell-cd-regexp', `shell-pushd-regexp', and `shell-popd-regexp',
404while `shell-pushd-tohome', `shell-pushd-dextract' and `shell-pushd-dunique'
405control the behaviour of the relevant command.
406
342Environment variables are expanded, see function `substitute-in-file-name'." 407Environment 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. 596See `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, 606See `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.
628File names are assumed to contain `/'s or not be the first item in the command.
629
630See 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.
639This function is similar to `comint-dynamic-complete-filename', except that it
640searches `exec-path' (minus the trailing emacs library path) for completion
641candidates. Note that this may not be the same as the shell's idea of the
642path.
643
644Completion is dependent on the value of `shell-completion-execonly', plus
645those 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
575This 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