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