[TMP] enable load_prefer_newer
[bpt/emacs.git] / lisp / dirtrack.el
CommitLineData
76889e51
RS
1;;; dirtrack.el --- Directory Tracking by watching the prompt
2
ba318903 3;; Copyright (C) 1996, 2001-2014 Free Software Foundation, Inc.
76889e51 4
d1212648 5;; Author: Peter Breton <pbreton@cs.umb.edu>
76889e51
RS
6;; Created: Sun Nov 17 1996
7;; Keywords: processes
76889e51
RS
8
9;; This file is part of GNU Emacs.
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
76889e51 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
76889e51
RS
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
76889e51
RS
23
24;;; Commentary:
25
26;; Shell directory tracking by watching the prompt.
27;;
28;; This is yet another attempt at a directory-tracking package for
df8a0bff 29;; Emacs shell-mode. However, this package makes one strong assumption:
76889e51 30;; that you can customize your shell's prompt to contain the
df8a0bff 31;; current working directory. Most shells do support this, including
76889e51
RS
32;; almost every type of Bourne and C shell on Unix, the native shells on
33;; Windows95 (COMMAND.COM) and Windows NT (CMD.EXE), and most 3rd party
df8a0bff 34;; Windows shells. If you cannot do this, or do not wish to, this package
76889e51
RS
35;; will be useless to you.
36;;
37;; Installation:
38;;
39;; 1) Set your shell's prompt to contain the current working directory.
40;; You may need to consult your shell's documentation to find out how to
41;; do this.
c60ee5e7
JB
42;;
43;; Note that directory tracking is done by matching regular expressions,
76889e51 44;; therefore it is *VERY IMPORTANT* for your prompt to be easily
df8a0bff 45;; distinguishable from other output. If your prompt regexp is too general,
76889e51
RS
46;; you will see error messages from the dirtrack filter as it attempts to cd
47;; to non-existent directories.
48;;
df8a0bff 49;; 2) Set the variable `dirtrack-list' to an appropriate value. This
76889e51
RS
50;; should be a list of two elements: the first is a regular expression
51;; which matches your prompt up to and including the pathname part.
c60ee5e7 52;; The second is a number which tells which regular expression group to
df8a0bff
SM
53;; match to extract only the pathname. If you use a multi-line prompt,
54;; add 't' as a third element. Note that some of the functions in
76889e51 55;; 'comint.el' assume a single-line prompt (eg, comint-bol).
c60ee5e7 56;;
ac37dedb
GM
57;; Determining this information may take some experimentation. Using
58;; `dirtrack-debug-mode' may help; it causes the directory-tracking
59;; filter to log messages to the buffer `dirtrack-debug-buffer'.
c60ee5e7 60;;
ac37dedb
GM
61;; 3) Activate `dirtrack-mode'. You may wish to turn ordinary shell
62;; tracking off by calling `shell-dirtrack-mode'.
76889e51
RS
63;;
64;; Examples:
65;;
66;; 1) On Windows NT, my prompt is set to emacs$S$P$G.
67;; 'dirtrack-list' is set to (list "^emacs \\([a-zA-Z]:.*\\)>" 1)
68;;
69;; 2) On Solaris running bash, my prompt is set like this:
70;; PS1="\w\012emacs@\h(\!) [\t]% "
71;; 'dirtrack-list' is set to (list "^\\([/~].*\\)\nemacs@[^%]+% *" 1 t)
72;;
73;; I'd appreciate other examples from people who use this package.
c60ee5e7 74;;
d1212648
RS
75;; Here's one from Stephen Eglen:
76;;
77;; Running under tcsh:
78;; (setq-default dirtrack-list '("^%E \\([^ ]+\\)" 1))
c60ee5e7 79;;
d1212648
RS
80;; It might be worth mentioning in your file that emacs sources start up
81;; files of the form: ~/.emacs_<SHELL> where <SHELL> is the name of the
82;; shell. So for example, I have the following in ~/.emacs_tcsh:
c60ee5e7 83;;
d1212648 84;; set prompt = "%%E %~ %h% "
c60ee5e7 85;;
d1212648 86;; This produces a prompt of the form:
c60ee5e7
JB
87;; %E /var/spool 10%
88;;
d1212648
RS
89;; This saves me from having to use the %E prefix in other non-emacs
90;; shells.
261f6363
KH
91;;
92;; A final note:
c60ee5e7 93;;
9b4b0c9e
RS
94;; I run LOTS of shell buffers through Emacs, sometimes as different users
95;; (eg, when logged in as myself, I'll run a root shell in the same Emacs).
96;; If you do this, and the shell prompt contains a ~, Emacs will interpret
97;; this relative to the user which owns the Emacs process, not the user
df8a0bff 98;; who owns the shell buffer. This may cause dirtrack to behave strangely
9b4b0c9e 99;; (typically it reports that it is unable to cd to a directory
261f6363
KH
100;; with a ~ in it).
101;;
9b4b0c9e
RS
102;; The same behavior can occur if you use dirtrack with remote filesystems
103;; (using telnet, rlogin, etc) as Emacs will be checking the local
df8a0bff 104;; filesystem, not the remote one. This problem is not specific to dirtrack,
9b4b0c9e 105;; but also affects file completion, etc.
76889e51
RS
106
107;;; Code:
108
109(eval-when-compile
110 (require 'comint)
111 (require 'shell))
112
d1212648
RS
113;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
114;; Customization Variables
115;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
116
117(defgroup dirtrack nil
118 "Directory tracking by watching the prompt."
119 :prefix "dirtrack-"
120 :group 'shell)
121
122(defcustom dirtrack-list (list "^emacs \\([a-zA-Z]:.*\\)>" 1)
df8a0bff 123 "List for directory tracking.
76889e51 124First item is a regexp that describes where to find the path in a prompt.
f75bfc33 125Second is a number, the regexp group to match."
d1212648 126 :group 'dirtrack
c60ee5e7 127 :type '(sexp (regexp :tag "Prompt Expression")
f75bfc33
CY
128 (integer :tag "Regexp Group"))
129 :version "24.1")
76889e51
RS
130
131(make-variable-buffer-local 'dirtrack-list)
132
d1212648 133(defcustom dirtrack-debug nil
df8a0bff 134 "If non-nil, the function `dirtrack' will report debugging info."
d1212648 135 :group 'dirtrack
df8a0bff 136 :type 'boolean)
76889e51 137
d1212648 138(defcustom dirtrack-debug-buffer "*Directory Tracking Log*"
ac37dedb 139 "Buffer in which to write directory tracking debug information."
d1212648 140 :group 'dirtrack
df8a0bff 141 :type 'string)
76889e51 142
c60ee5e7 143(defcustom dirtrack-directory-function
72bc50c0 144 (if (memq system-type '(ms-dos windows-nt cygwin))
76889e51 145 'dirtrack-windows-directory-function
df8a0bff
SM
146 'file-name-as-directory)
147 "Function to apply to the prompt directory for comparison purposes."
d1212648 148 :group 'dirtrack
df8a0bff 149 :type 'function)
76889e51 150
c60ee5e7 151(defcustom dirtrack-canonicalize-function
72bc50c0 152 (if (memq system-type '(ms-dos windows-nt cygwin))
76889e51 153 'downcase 'identity)
df8a0bff 154 "Function to apply to the default directory for comparison purposes."
d1212648 155 :group 'dirtrack
df8a0bff 156 :type 'function)
d1212648 157
12b0f451
KH
158(defcustom dirtrack-directory-change-hook nil
159 "Hook that is called when a directory change is made."
160 :group 'dirtrack
df8a0bff 161 :type 'hook)
12b0f451
KH
162
163
d1212648
RS
164;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
165;; Functions
166;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
76889e51 167
76889e51
RS
168
169(defun dirtrack-windows-directory-function (dir)
170 "Return a canonical directory for comparison purposes.
c60ee5e7 171Such a directory is all lowercase, has forward-slashes as delimiters,
76889e51 172and ends with a forward slash."
df8a0bff 173 (file-name-as-directory (downcase (subst-char-in-string ?\\ ?/ dir))))
76889e51 174
b9e316dd
PB
175(defun dirtrack-cygwin-directory-function (dir)
176 "Return a canonical directory taken from a Cygwin path for comparison purposes."
177 (if (string-match "/cygdrive/\\([A-Z]\\)\\(.*\\)" dir)
178 (concat (match-string 1 dir) ":" (match-string 2 dir))
179 dir))
180
ac37dedb 181
e5bd0a28
SM
182(define-obsolete-function-alias 'dirtrack-toggle 'dirtrack-mode "23.1")
183(define-obsolete-variable-alias 'dirtrackp 'dirtrack-mode "23.1")
ac37dedb
GM
184;;;###autoload
185(define-minor-mode dirtrack-mode
06e21633
CY
186 "Toggle directory tracking in shell buffers (Dirtrack mode).
187With a prefix argument ARG, enable Dirtrack mode if ARG is
188positive, and disable it otherwise. If called from Lisp, enable
189the mode if ARG is omitted or nil.
190
f75bfc33
CY
191This method requires that your shell prompt contain the current
192working directory at all times, and that you set the variable
193`dirtrack-list' to match the prompt.
194
195This is an alternative to `shell-dirtrack-mode', which works by
196tracking `cd' and similar commands which change the shell working
197directory."
ac37dedb
GM
198 nil nil nil
199 (if dirtrack-mode
df8a0bff 200 (add-hook 'comint-preoutput-filter-functions 'dirtrack nil t)
ac37dedb 201 (remove-hook 'comint-preoutput-filter-functions 'dirtrack t)))
76889e51 202
ac37dedb 203
e5bd0a28
SM
204(define-obsolete-function-alias 'dirtrack-debug-toggle 'dirtrack-debug-mode
205 "23.1")
206(define-obsolete-variable-alias 'dirtrack-debug 'dirtrack-debug-mode "23.1")
ac37dedb 207(define-minor-mode dirtrack-debug-mode
e1ac4066
GM
208 "Toggle Dirtrack debugging.
209With a prefix argument ARG, enable Dirtrack debugging if ARG is
210positive, and disable it otherwise. If called from Lisp, enable
211the mode if ARG is omitted or nil."
ac37dedb
GM
212 nil nil nil
213 (if dirtrack-debug-mode
214 (display-buffer (get-buffer-create dirtrack-debug-buffer))))
215
f75bfc33
CY
216(defun dirtrack-debug-message (msg1 msg2)
217 "Insert strings at the end of `dirtrack-debug-buffer'."
ac37dedb
GM
218 (when dirtrack-debug-mode
219 (with-current-buffer (get-buffer-create dirtrack-debug-buffer)
220 (goto-char (point-max))
f75bfc33 221 (insert msg1 msg2 "\n"))))
76889e51 222
d7fe6352
JB
223(declare-function shell-prefixed-directory-name "shell" (dir))
224(declare-function shell-process-cd "shell" (arg))
225
76889e51
RS
226;;;###autoload
227(defun dirtrack (input)
f75bfc33
CY
228 "Determine the current directory from the process output for a prompt.
229This filter function is used by `dirtrack-mode'. It looks for
230the prompt specified by `dirtrack-list', and calls
231`shell-process-cd' if the directory seems to have changed away
232from `default-directory'."
233 (when (and dirtrack-mode
234 (not (eq (point) (point-min)))) ; there must be output
235 (save-excursion ; What's this for? -- cyd
236 (if (not (string-match (nth 0 dirtrack-list) input))
237 ;; No match
238 (dirtrack-debug-message
239 "Input failed to match `dirtrack-list': " input)
240 (let ((prompt-path (match-string (nth 1 dirtrack-list) input))
241 temp)
242 (cond
243 ;; Don't do anything for empty string
244 ((string-equal prompt-path "")
245 (dirtrack-debug-message "Prompt match gives empty string: " input))
246 ;; If the prompt contains an absolute file name, call
247 ;; `shell-process-cd' if the directory has changed.
248 ((file-name-absolute-p prompt-path)
249 ;; Transform prompts into canonical forms
250 (let ((orig-prompt-path (funcall dirtrack-directory-function
251 prompt-path))
252 (current-dir (funcall dirtrack-canonicalize-function
253 default-directory)))
254 (setq prompt-path (shell-prefixed-directory-name orig-prompt-path))
255 ;; Compare them
256 (if (or (string-equal current-dir prompt-path)
257 (string-equal (expand-file-name current-dir)
258 (expand-file-name prompt-path)))
259 (dirtrack-debug-message "Not changing directory: " current-dir)
260 ;; It's possible that Emacs thinks the directory
261 ;; doesn't exist (e.g. rlogin buffers)
262 (if (file-accessible-directory-p prompt-path)
263 ;; `shell-process-cd' adds the prefix, so we need
264 ;; to give it the original (un-prefixed) path.
265 (progn
266 (shell-process-cd orig-prompt-path)
267 (run-hooks 'dirtrack-directory-change-hook)
268 (dirtrack-debug-message "Changing directory to "
269 prompt-path))
270 (dirtrack-debug-message "Not changing to non-existent directory: "
271 prompt-path)))))
272 ;; If the file name is non-absolute, try and see if it
273 ;; seems to be up or down from where we were.
274 ((string-match "\\`\\(.*\\)\\(?:/.*\\)?\n\\(.*/\\)\\1\\(?:/.*\\)?\\'"
275 (setq temp
276 (concat prompt-path "\n" default-directory)))
277 (shell-process-cd (concat (match-string 2 temp)
278 prompt-path))
279 (run-hooks 'dirtrack-directory-change-hook)))))))
9b4b0c9e 280 input)
76889e51
RS
281
282(provide 'dirtrack)
283
284;;; dirtrack.el ends here