comparison lisp/dirtrack.el @ 71666:743b3d313968

(dirtrack-default-directory-function): Remove. (dirtrack-directory-function): Use file-name-as-directory. (dirtrack-windows-directory-function): Simplify. (dirtrack-forward-slash, dirtrack-backward-slash) (dirtrack-replace-slash): Remove. (dirtrack-toggle): Adjust comint-preoutput-filter-functions as well. (dirtrack): Fix wrong parenthesizing; use match-string.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Fri, 07 Jul 2006 15:14:47 +0000
parents 3bd95f4f2941
children ddcbd2c1b70d 8a8e69664178
comparison
equal deleted inserted replaced
71665:611496c32b32 71666:743b3d313968
27 ;;; Commentary: 27 ;;; Commentary:
28 28
29 ;; Shell directory tracking by watching the prompt. 29 ;; Shell directory tracking by watching the prompt.
30 ;; 30 ;;
31 ;; This is yet another attempt at a directory-tracking package for 31 ;; This is yet another attempt at a directory-tracking package for
32 ;; Emacs shell-mode. However, this package makes one strong assumption: 32 ;; Emacs shell-mode. However, this package makes one strong assumption:
33 ;; that you can customize your shell's prompt to contain the 33 ;; that you can customize your shell's prompt to contain the
34 ;; current working directory. Most shells do support this, including 34 ;; current working directory. Most shells do support this, including
35 ;; almost every type of Bourne and C shell on Unix, the native shells on 35 ;; almost every type of Bourne and C shell on Unix, the native shells on
36 ;; Windows95 (COMMAND.COM) and Windows NT (CMD.EXE), and most 3rd party 36 ;; Windows95 (COMMAND.COM) and Windows NT (CMD.EXE), and most 3rd party
37 ;; Windows shells. If you cannot do this, or do not wish to, this package 37 ;; Windows shells. If you cannot do this, or do not wish to, this package
38 ;; will be useless to you. 38 ;; will be useless to you.
39 ;; 39 ;;
40 ;; Installation: 40 ;; Installation:
41 ;; 41 ;;
42 ;; 1) Set your shell's prompt to contain the current working directory. 42 ;; 1) Set your shell's prompt to contain the current working directory.
43 ;; You may need to consult your shell's documentation to find out how to 43 ;; You may need to consult your shell's documentation to find out how to
44 ;; do this. 44 ;; do this.
45 ;; 45 ;;
46 ;; Note that directory tracking is done by matching regular expressions, 46 ;; Note that directory tracking is done by matching regular expressions,
47 ;; therefore it is *VERY IMPORTANT* for your prompt to be easily 47 ;; therefore it is *VERY IMPORTANT* for your prompt to be easily
48 ;; distinguishable from other output. If your prompt regexp is too general, 48 ;; distinguishable from other output. If your prompt regexp is too general,
49 ;; you will see error messages from the dirtrack filter as it attempts to cd 49 ;; you will see error messages from the dirtrack filter as it attempts to cd
50 ;; to non-existent directories. 50 ;; to non-existent directories.
51 ;; 51 ;;
52 ;; 2) Set the variable `dirtrack-list' to an appropriate value. This 52 ;; 2) Set the variable `dirtrack-list' to an appropriate value. This
53 ;; should be a list of two elements: the first is a regular expression 53 ;; should be a list of two elements: the first is a regular expression
54 ;; which matches your prompt up to and including the pathname part. 54 ;; which matches your prompt up to and including the pathname part.
55 ;; The second is a number which tells which regular expression group to 55 ;; The second is a number which tells which regular expression group to
56 ;; match to extract only the pathname. If you use a multi-line prompt, 56 ;; match to extract only the pathname. If you use a multi-line prompt,
57 ;; add 't' as a third element. Note that some of the functions in 57 ;; add 't' as a third element. Note that some of the functions in
58 ;; 'comint.el' assume a single-line prompt (eg, comint-bol). 58 ;; 'comint.el' assume a single-line prompt (eg, comint-bol).
59 ;; 59 ;;
60 ;; Determining this information may take some experimentation. Setting 60 ;; Determining this information may take some experimentation. Setting
61 ;; the variable `dirtrack-debug' may help; it causes the directory-tracking 61 ;; the variable `dirtrack-debug' may help; it causes the directory-tracking
62 ;; filter to log messages to the buffer `dirtrack-debug-buffer'. You can easily 62 ;; filter to log messages to the buffer `dirtrack-debug-buffer'. You can easily
63 ;; toggle this setting with the `dirtrack-debug-toggle' function. 63 ;; toggle this setting with the `dirtrack-debug-toggle' function.
64 ;; 64 ;;
65 ;; 3) Add a hook to shell-mode to enable the directory tracking: 65 ;; 3) Add a hook to shell-mode to enable the directory tracking:
66 ;; 66 ;;
67 ;; (add-hook 'shell-mode-hook 67 ;; (add-hook 'shell-mode-hook
68 ;; (function (lambda () 68 ;; (lambda () (add-hook 'comint-preoutput-filter-functions 'dirtrack nil t)))
69 ;; (setq comint-preoutput-filter-functions
70 ;; (append (list 'dirtrack)
71 ;; comint-preoutput-filter-functions)))))
72 ;; 69 ;;
73 ;; You may wish to turn ordinary shell tracking off by calling 70 ;; You may wish to turn ordinary shell tracking off by calling
74 ;; `shell-dirtrack-toggle' or setting `shell-dirtrackp'. 71 ;; `shell-dirtrack-toggle' or setting `shell-dirtrackp'.
75 ;; 72 ;;
76 ;; Examples: 73 ;; Examples:
105 ;; 102 ;;
106 ;; I run LOTS of shell buffers through Emacs, sometimes as different users 103 ;; I run LOTS of shell buffers through Emacs, sometimes as different users
107 ;; (eg, when logged in as myself, I'll run a root shell in the same Emacs). 104 ;; (eg, when logged in as myself, I'll run a root shell in the same Emacs).
108 ;; If you do this, and the shell prompt contains a ~, Emacs will interpret 105 ;; If you do this, and the shell prompt contains a ~, Emacs will interpret
109 ;; this relative to the user which owns the Emacs process, not the user 106 ;; this relative to the user which owns the Emacs process, not the user
110 ;; who owns the shell buffer. This may cause dirtrack to behave strangely 107 ;; who owns the shell buffer. This may cause dirtrack to behave strangely
111 ;; (typically it reports that it is unable to cd to a directory 108 ;; (typically it reports that it is unable to cd to a directory
112 ;; with a ~ in it). 109 ;; with a ~ in it).
113 ;; 110 ;;
114 ;; The same behavior can occur if you use dirtrack with remote filesystems 111 ;; The same behavior can occur if you use dirtrack with remote filesystems
115 ;; (using telnet, rlogin, etc) as Emacs will be checking the local 112 ;; (using telnet, rlogin, etc) as Emacs will be checking the local
116 ;; filesystem, not the remote one. This problem is not specific to dirtrack, 113 ;; filesystem, not the remote one. This problem is not specific to dirtrack,
117 ;; but also affects file completion, etc. 114 ;; but also affects file completion, etc.
118 115
119 ;;; Code: 116 ;;; Code:
120 117
121 (eval-when-compile 118 (eval-when-compile
130 "Directory tracking by watching the prompt." 127 "Directory tracking by watching the prompt."
131 :prefix "dirtrack-" 128 :prefix "dirtrack-"
132 :group 'shell) 129 :group 'shell)
133 130
134 (defcustom dirtrack-list (list "^emacs \\([a-zA-Z]:.*\\)>" 1) 131 (defcustom dirtrack-list (list "^emacs \\([a-zA-Z]:.*\\)>" 1)
135 "*List for directory tracking. 132 "List for directory tracking.
136 First item is a regexp that describes where to find the path in a prompt. 133 First item is a regexp that describes where to find the path in a prompt.
137 Second is a number, the regexp group to match. Optional third item is 134 Second is a number, the regexp group to match. Optional third item is
138 whether the prompt is multi-line. If nil or omitted, prompt is assumed to 135 whether the prompt is multi-line. If nil or omitted, prompt is assumed to
139 be on a single line." 136 be on a single line."
140 :group 'dirtrack 137 :group 'dirtrack
141 :type '(sexp (regexp :tag "Prompt Expression") 138 :type '(sexp (regexp :tag "Prompt Expression")
142 (integer :tag "Regexp Group") 139 (integer :tag "Regexp Group")
143 (boolean :tag "Multiline Prompt") 140 (boolean :tag "Multiline Prompt")))
144 )
145 )
146 141
147 (make-variable-buffer-local 'dirtrack-list) 142 (make-variable-buffer-local 'dirtrack-list)
148 143
149 (defcustom dirtrack-debug nil 144 (defcustom dirtrack-debug nil
150 "*If non-nil, the function `dirtrack' will report debugging info." 145 "If non-nil, the function `dirtrack' will report debugging info."
151 :group 'dirtrack 146 :group 'dirtrack
152 :type 'boolean 147 :type 'boolean)
153 )
154 148
155 (defcustom dirtrack-debug-buffer "*Directory Tracking Log*" 149 (defcustom dirtrack-debug-buffer "*Directory Tracking Log*"
156 "Buffer to write directory tracking debug information." 150 "Buffer to write directory tracking debug information."
157 :group 'dirtrack 151 :group 'dirtrack
158 :type 'string 152 :type 'string)
159 )
160 153
161 (defcustom dirtrackp t 154 (defcustom dirtrackp t
162 "*If non-nil, directory tracking via `dirtrack' is enabled." 155 "If non-nil, directory tracking via `dirtrack' is enabled."
163 :group 'dirtrack 156 :group 'dirtrack
164 :type 'boolean 157 :type 'boolean)
165 )
166 158
167 (make-variable-buffer-local 'dirtrackp) 159 (make-variable-buffer-local 'dirtrackp)
168 160
169 (defcustom dirtrack-directory-function 161 (defcustom dirtrack-directory-function
170 (if (memq system-type (list 'ms-dos 'windows-nt 'cygwin)) 162 (if (memq system-type (list 'ms-dos 'windows-nt 'cygwin))
171 'dirtrack-windows-directory-function 163 'dirtrack-windows-directory-function
172 'dirtrack-default-directory-function) 164 'file-name-as-directory)
173 "*Function to apply to the prompt directory for comparison purposes." 165 "Function to apply to the prompt directory for comparison purposes."
174 :group 'dirtrack 166 :group 'dirtrack
175 :type 'function 167 :type 'function)
176 )
177 168
178 (defcustom dirtrack-canonicalize-function 169 (defcustom dirtrack-canonicalize-function
179 (if (memq system-type (list 'ms-dos 'windows-nt 'cygwin)) 170 (if (memq system-type (list 'ms-dos 'windows-nt 'cygwin))
180 'downcase 'identity) 171 'downcase 'identity)
181 "*Function to apply to the default directory for comparison purposes." 172 "Function to apply to the default directory for comparison purposes."
182 :group 'dirtrack 173 :group 'dirtrack
183 :type 'function 174 :type 'function)
184 )
185 175
186 (defcustom dirtrack-directory-change-hook nil 176 (defcustom dirtrack-directory-change-hook nil
187 "Hook that is called when a directory change is made." 177 "Hook that is called when a directory change is made."
188 :group 'dirtrack 178 :group 'dirtrack
189 :type 'hook 179 :type 'hook)
190 )
191 180
192 181
193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 182 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194 ;; Functions 183 ;; Functions
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 184 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
196 185
197 (defun dirtrack-default-directory-function (dir)
198 "Return a canonical directory for comparison purposes.
199 Such a directory ends with a forward slash."
200 (let ((directory dir))
201 (if (not (char-equal ?/ (string-to-char (substring directory -1))))
202 (concat directory "/")
203 directory)))
204 186
205 (defun dirtrack-windows-directory-function (dir) 187 (defun dirtrack-windows-directory-function (dir)
206 "Return a canonical directory for comparison purposes. 188 "Return a canonical directory for comparison purposes.
207 Such a directory is all lowercase, has forward-slashes as delimiters, 189 Such a directory is all lowercase, has forward-slashes as delimiters,
208 and ends with a forward slash." 190 and ends with a forward slash."
209 (let ((directory dir)) 191 (file-name-as-directory (downcase (subst-char-in-string ?\\ ?/ dir))))
210 (setq directory (downcase (dirtrack-replace-slash directory t)))
211 (if (not (char-equal ?/ (string-to-char (substring directory -1))))
212 (concat directory "/")
213 directory)))
214 192
215 (defun dirtrack-cygwin-directory-function (dir) 193 (defun dirtrack-cygwin-directory-function (dir)
216 "Return a canonical directory taken from a Cygwin path for comparison purposes." 194 "Return a canonical directory taken from a Cygwin path for comparison purposes."
217 (if (string-match "/cygdrive/\\([A-Z]\\)\\(.*\\)" dir) 195 (if (string-match "/cygdrive/\\([A-Z]\\)\\(.*\\)" dir)
218 (concat (match-string 1 dir) ":" (match-string 2 dir)) 196 (concat (match-string 1 dir) ":" (match-string 2 dir))
219 dir)) 197 dir))
220 198
221 (defconst dirtrack-forward-slash (regexp-quote "/"))
222 (defconst dirtrack-backward-slash (regexp-quote "\\"))
223
224 (defun dirtrack-replace-slash (string &optional opposite)
225 "Replace forward slashes with backwards ones.
226 If additional argument is non-nil, replace backwards slashes with
227 forward ones."
228 (let ((orig (if opposite
229 dirtrack-backward-slash
230 dirtrack-forward-slash))
231 (replace (if opposite
232 dirtrack-forward-slash
233 dirtrack-backward-slash))
234 (newstring string)
235 )
236 (while (string-match orig newstring)
237 (setq newstring (replace-match replace nil t newstring)))
238 newstring))
239
240 ;; Copied from shell.el 199 ;; Copied from shell.el
241 (defun dirtrack-toggle () 200 (defun dirtrack-toggle ()
242 "Enable or disable Dirtrack directory tracking in a shell buffer." 201 "Enable or disable Dirtrack directory tracking in a shell buffer."
243 (interactive) 202 (interactive)
244 (setq dirtrackp (not dirtrackp)) 203 (if (setq dirtrackp (not dirtrackp))
204 (add-hook 'comint-preoutput-filter-functions 'dirtrack nil t)
205 (remove-hook 'comint-preoutput-filter-functions 'dirtrack t))
245 (message "Directory tracking %s" (if dirtrackp "ON" "OFF"))) 206 (message "Directory tracking %s" (if dirtrackp "ON" "OFF")))
246 207
247 (defun dirtrack-debug-toggle () 208 (defun dirtrack-debug-toggle ()
248 "Enable or disable Dirtrack debugging." 209 "Enable or disable Dirtrack debugging."
249 (interactive) 210 (interactive)
271 232
272 If directory tracking does not seem to be working, you can use the 233 If directory tracking does not seem to be working, you can use the
273 function `dirtrack-debug-toggle' to turn on debugging output. 234 function `dirtrack-debug-toggle' to turn on debugging output.
274 235
275 You can enable directory tracking by adding this function to 236 You can enable directory tracking by adding this function to
276 `comint-output-filter-functions'. 237 `comint-output-filter-functions'."
277 " 238 (if (or (null dirtrackp)
278 (if (null dirtrackp) 239 ;; No output?
240 (eq (point) (point-min)))
279 nil 241 nil
280 (let (prompt-path 242 (let (prompt-path
281 matched
282 (current-dir default-directory) 243 (current-dir default-directory)
283 (dirtrack-regexp (nth 0 dirtrack-list)) 244 (dirtrack-regexp (nth 0 dirtrack-list))
284 (match-num (nth 1 dirtrack-list)) 245 (match-num (nth 1 dirtrack-list))
285 (multi-line (nth 2 dirtrack-list)) 246 ;; Currently unimplemented, it seems. --Stef
286 ) 247 (multi-line (nth 2 dirtrack-list)))
287 ;; No output? 248 (save-excursion
288 (if (eq (point) (point-min)) 249 ;; No match
289 nil 250 (if (null (string-match dirtrack-regexp input))
290 (save-excursion 251 (and dirtrack-debug
291 (setq matched (string-match dirtrack-regexp input))) 252 (dirtrack-debug-message
292 ;; No match 253 (format
293 (if (null matched) 254 "Input `%s' failed to match `dirtrack-regexp'" input)))
294 (and dirtrack-debug 255 (setq prompt-path (match-string match-num input))
295 (dirtrack-debug-message 256 ;; Empty string
296 (format 257 (if (not (> (length prompt-path) 0))
297 "Input `%s' failed to match regexp: %s" 258 (and dirtrack-debug
298 input dirtrack-regexp))) 259 (dirtrack-debug-message "Match is empty string"))
299 (setq prompt-path 260 ;; Transform prompts into canonical forms
300 (substring input 261 (setq prompt-path (funcall dirtrack-directory-function
301 (match-beginning match-num) (match-end match-num))) 262 prompt-path))
302 ;; Empty string 263 (setq current-dir (funcall dirtrack-canonicalize-function
303 (if (not (> (length prompt-path) 0)) 264 current-dir))
304 (and dirtrack-debug 265 (and dirtrack-debug
305 (dirtrack-debug-message "Match is empty string")) 266 (dirtrack-debug-message
306 ;; Transform prompts into canonical forms 267 (format
307 (setq prompt-path (funcall dirtrack-directory-function 268 "Prompt is %s\nCurrent directory is %s"
308 prompt-path)) 269 prompt-path current-dir)))
309 (setq current-dir (funcall dirtrack-canonicalize-function 270 ;; Compare them
310 current-dir)) 271 (if (or (string= current-dir prompt-path)
311 (and dirtrack-debug 272 (string= current-dir
312 (dirtrack-debug-message 273 (abbreviate-file-name prompt-path)))
313 (format 274 (and dirtrack-debug
314 "Prompt is %s\nCurrent directory is %s" 275 (dirtrack-debug-message
315 prompt-path current-dir))) 276 (format "Not changing directory")))
316 ;; Compare them 277 ;; It's possible that Emacs will think the directory
317 (if (or (string= current-dir prompt-path) 278 ;; won't exist (eg, rlogin buffers)
318 (string= current-dir 279 (if (file-accessible-directory-p prompt-path)
319 (abbreviate-file-name prompt-path))) 280 ;; Change directory
320 (and dirtrack-debug 281 (and (shell-process-cd prompt-path)
321 (dirtrack-debug-message 282 (run-hooks 'dirtrack-directory-change-hook)
322 (format "Not changing directory"))) 283 dirtrack-debug
323 ;; It's possible that Emacs will think the directory 284 (dirtrack-debug-message
324 ;; won't exist (eg, rlogin buffers) 285 (format "Changing directory to %s" prompt-path)))
325 (if (file-accessible-directory-p prompt-path) 286 (error "Directory %s does not exist" prompt-path)))
326 ;; Change directory 287 )))))
327 (and (shell-process-cd prompt-path)
328 (run-hooks 'dirtrack-directory-change-hook)
329 dirtrack-debug
330 (dirtrack-debug-message
331 (format "Changing directory to %s" prompt-path)))
332 (error "Directory %s does not exist" prompt-path)))
333 )))))
334 input) 288 input)
335 289
336 (provide 'dirtrack) 290 (provide 'dirtrack)
337 291
338 ;;; arch-tag: 168de071-be88-4937-aff6-2aba9f328d5a 292 ;; arch-tag: 168de071-be88-4937-aff6-2aba9f328d5a
339 ;;; dirtrack.el ends here 293 ;;; dirtrack.el ends here