Revision: miles@gnu.org--gnu-2005/emacs--unicode--0--patch-68
[bpt/emacs.git] / lisp / eshell / em-alias.el
1 ;;; em-alias.el --- creation and management of command aliases
2
3 ;; Copyright (C) 1999, 2000, 2004 Free Software Foundation
4
5 ;; Author: John Wiegley <johnw@gnu.org>
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
23
24 (provide 'em-alias)
25
26 (eval-when-compile (require 'esh-maint))
27 (require 'eshell)
28
29 (defgroup eshell-alias nil
30 "Command aliases allow for easy definition of alternate commands."
31 :tag "Command aliases"
32 ;; :link '(info-link "(eshell)Command aliases")
33 :group 'eshell-module)
34
35 ;;; Commentary:
36
37 ;; Command aliases greatly simplify the definition of new commands.
38 ;; They exist as an alternative to alias functions, which are
39 ;; otherwise quite superior, being more flexible and natural to the
40 ;; Emacs Lisp environment (if somewhat trickier to define; [Alias
41 ;; functions]).
42 ;;
43 ;;;_* Creating aliases
44 ;;
45 ;; The user interface is simple: type 'alias' followed by the command
46 ;; name followed by the definition. Argument references are made
47 ;; using '$1', '$2', etc., or '$*'. For example:
48 ;;
49 ;; alias ll 'ls -l $*'
50 ;;
51 ;; This will cause the command 'll NEWS' to be replaced by 'ls -l
52 ;; NEWS'. This is then passed back to the command parser for
53 ;; reparsing.{Only the command text specified in the alias definition
54 ;; will be reparsed. Argument references (such as '$*') are handled
55 ;; using variable values, which means that the expansion will not be
56 ;; reparsed, but used directly.}
57 ;;
58 ;; To delete an alias, specify its name without a definition:
59 ;;
60 ;; alias ll
61 ;;
62 ;; Aliases are written to disk immediately after being defined or
63 ;; deleted. The filename in which they are kept is defined by the
64 ;; following variable:
65
66 (defcustom eshell-aliases-file (concat eshell-directory-name "alias")
67 "*The file in which aliases are kept.
68 Whenever an alias is defined by the user, using the `alias' command,
69 it will be written to this file. Thus, alias definitions (and
70 deletions) are always permanent. This approach was chosen for the
71 sake of simplicity, since that's pretty much the only benefit to be
72 gained by using this module."
73 :type 'file
74 :group 'eshell-alias)
75
76 ;;;
77 ;; The format of this file is quite basic. It specifies the alias
78 ;; definitions in almost exactly the same way that the user entered
79 ;; them, minus any argument quoting (since interpolation is not done
80 ;; when the file is read). Hence, it is possible to add new aliases
81 ;; to the alias file directly, using a text editor rather than the
82 ;; `alias' command. Or, this method can be used for editing aliases
83 ;; that have already defined.
84 ;;
85 ;; Here is an example of a few different aliases, and they would
86 ;; appear in the aliases file:
87 ;;
88 ;; alias clean rm -fr **/.#*~
89 ;; alias commit cvs commit -m changes $*
90 ;; alias ll ls -l $*
91 ;; alias info (info)
92 ;; alias reindex glimpseindex -o ~/Mail
93 ;; alias compact for i in ~/Mail/**/*~*.bz2(Lk+50) { bzip2 -9v $i }
94 ;;
95 ;;;_* Auto-correction of bad commands
96 ;;
97 ;; When a user enters the same unknown command many times during a
98 ;; session, it is likely that they are experiencing a spelling
99 ;; difficulty associated with a certain command. To combat this,
100 ;; Eshell will offer to automatically define an alias for that
101 ;; mispelled command, once a given tolerance threshold has been
102 ;; reached.
103
104 (defcustom eshell-bad-command-tolerance 3
105 "*The number of failed commands to ignore before creating an alias."
106 :type 'integer
107 ;; :link '(custom-manual "(eshell)Auto-correction of bad commands")
108 :group 'eshell-alias)
109
110 ;;;
111 ;; Whenever the same bad command name is encountered this many times,
112 ;; the user will be prompted in the minibuffer to provide an alias
113 ;; name. An alias definition will then be created which will result
114 ;; in an equal call to the correct name. In this way, Eshell
115 ;; gradually learns about the commands that the user mistypes
116 ;; frequently, and will automatically correct them!
117 ;;
118 ;; Note that a '$*' is automatically appended at the end of the alias
119 ;; definition, so that entering it is unnecessary when specifying the
120 ;; corrected command name.
121
122 ;;; Code:
123
124 (defcustom eshell-alias-load-hook '(eshell-alias-initialize)
125 "*A hook that gets run when `eshell-alias' is loaded."
126 :type 'hook
127 :group 'eshell-alias)
128
129 (defvar eshell-command-aliases-list nil
130 "A list of command aliases currently defined by the user.
131 Each element of this alias is a list of the form:
132
133 (NAME DEFINITION)
134
135 Where NAME is the textual name of the alias, and DEFINITION is the
136 command string to replace that command with.
137
138 Note: this list should not be modified in your '.emacs' file. Rather,
139 any desired alias definitions should be declared using the `alias'
140 command, which will automatically write them to the file named by
141 `eshell-aliases-file'.")
142
143 (put 'eshell-command-aliases-list 'risky-local-variable t)
144
145 (defvar eshell-failed-commands-alist nil
146 "An alist of command name failures.")
147
148 (defun eshell-alias-initialize ()
149 "Initialize the alias handling code."
150 (make-local-variable 'eshell-failed-commands-alist)
151 (add-hook 'eshell-alternate-command-hook 'eshell-fix-bad-commands t t)
152 (eshell-read-aliases-list)
153 (add-hook 'eshell-named-command-hook 'eshell-maybe-replace-by-alias t t)
154 (make-local-variable 'eshell-complex-commands)
155 (add-to-list 'eshell-complex-commands 'eshell-command-aliased-p))
156
157 (defun eshell-command-aliased-p (name)
158 (assoc name eshell-command-aliases-list))
159
160 (defun eshell/alias (&optional alias &rest definition)
161 "Define an ALIAS in the user's alias list using DEFINITION."
162 (if (not alias)
163 (eshell-for alias eshell-command-aliases-list
164 (eshell-print (apply 'format "alias %s %s\n" alias)))
165 (if (not definition)
166 (setq eshell-command-aliases-list
167 (delq (assoc alias eshell-command-aliases-list)
168 eshell-command-aliases-list))
169 (and (stringp definition)
170 (set-text-properties 0 (length definition) nil definition))
171 (let ((def (assoc alias eshell-command-aliases-list))
172 (alias-def (list alias
173 (eshell-flatten-and-stringify definition))))
174 (if def
175 (setq eshell-command-aliases-list
176 (delq def eshell-command-aliases-list)))
177 (setq eshell-command-aliases-list
178 (cons alias-def eshell-command-aliases-list))))
179 (eshell-write-aliases-list))
180 nil)
181
182 (defun pcomplete/eshell-mode/alias ()
183 "Completion function for Eshell's `alias' command."
184 (pcomplete-here (eshell-alias-completions pcomplete-stub)))
185
186 (defun eshell-read-aliases-list ()
187 "Read in an aliases list from `eshell-aliases-file'."
188 (let ((file eshell-aliases-file))
189 (when (file-readable-p file)
190 (setq eshell-command-aliases-list
191 (with-temp-buffer
192 (let (eshell-command-aliases-list)
193 (insert-file-contents file)
194 (while (not (eobp))
195 (if (re-search-forward
196 "^alias\\s-+\\(\\S-+\\)\\s-+\\(.+\\)")
197 (setq eshell-command-aliases-list
198 (cons (list (match-string 1)
199 (match-string 2))
200 eshell-command-aliases-list)))
201 (forward-line 1))
202 eshell-command-aliases-list))))))
203
204 (defun eshell-write-aliases-list ()
205 "Write out the current aliases into `eshell-aliases-file'."
206 (if (file-writable-p (file-name-directory eshell-aliases-file))
207 (let ((eshell-current-handles
208 (eshell-create-handles eshell-aliases-file 'overwrite)))
209 (eshell/alias)
210 (eshell-close-handles 0))))
211
212 (defsubst eshell-lookup-alias (name)
213 "Check whether NAME is aliased. Return the alias if there is one."
214 (assoc name eshell-command-aliases-list))
215
216 (defvar eshell-prevent-alias-expansion nil)
217
218 (defun eshell-maybe-replace-by-alias (command args)
219 "If COMMAND has an alias definition, call that instead using ARGS."
220 (unless (and eshell-prevent-alias-expansion
221 (member command eshell-prevent-alias-expansion))
222 (let ((alias (eshell-lookup-alias command)))
223 (if alias
224 (throw 'eshell-replace-command
225 (list
226 'let
227 (list
228 (list 'eshell-command-name
229 (list 'quote eshell-last-command-name))
230 (list 'eshell-command-arguments
231 (list 'quote eshell-last-arguments))
232 (list 'eshell-prevent-alias-expansion
233 (list 'quote
234 (cons command
235 eshell-prevent-alias-expansion))))
236 (eshell-parse-command (nth 1 alias))))))))
237
238 (defun eshell-alias-completions (name)
239 "Find all possible completions for NAME.
240 These are all the command aliases which begin with NAME."
241 (let (completions)
242 (eshell-for alias eshell-command-aliases-list
243 (if (string-match (concat "^" name) (car alias))
244 (setq completions (cons (car alias) completions))))
245 completions))
246
247 (defun eshell-fix-bad-commands (name)
248 "If the user repeatedly a bad command NAME, make an alias for them."
249 (ignore
250 (unless (file-name-directory name)
251 (let ((entry (assoc name eshell-failed-commands-alist)))
252 (if (not entry)
253 (setq eshell-failed-commands-alist
254 (cons (cons name 1) eshell-failed-commands-alist))
255 (if (< (cdr entry) eshell-bad-command-tolerance)
256 (setcdr entry (1+ (cdr entry)))
257 (let ((alias (concat
258 (read-string
259 (format "Define alias for \"%s\": " name))
260 " $*")))
261 (eshell/alias name alias)
262 (throw 'eshell-replace-command
263 (list
264 'let
265 (list
266 (list 'eshell-command-name
267 (list 'quote name))
268 (list 'eshell-command-arguments
269 (list 'quote eshell-last-arguments))
270 (list 'eshell-prevent-alias-expansion
271 (list 'quote
272 (cons name
273 eshell-prevent-alias-expansion))))
274 (eshell-parse-command alias))))))))))
275
276 ;;; arch-tag: 8b018fc1-4e07-4ccc-aa73-c0a1ba361f82
277 ;;; em-alias.el ends here