Spelling fixes.
[bpt/emacs.git] / lisp / eshell / em-alias.el
CommitLineData
60370d40 1;;; em-alias.el --- creation and management of command aliases
affbf647 2
73b0cd50 3;; Copyright (C) 1999-2011 Free Software Foundation, Inc.
affbf647 4
7de5b421
GM
5;; Author: John Wiegley <johnw@gnu.org>
6
affbf647
GM
7;; This file is part of GNU Emacs.
8
4ee57b2a 9;; GNU Emacs is free software: you can redistribute it and/or modify
affbf647 10;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
affbf647
GM
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
4ee57b2a 20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
affbf647 21
affbf647
GM
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
700645db 51;; variable eshell-aliases-file.
affbf647 52
affbf647
GM
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
53964682 77;; misspelled command, once a given tolerance threshold has been
affbf647
GM
78;; reached.
79
700645db
GM
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!
affbf647
GM
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
700645db
GM
93(eval-when-compile
94 (require 'esh-util))
95(require 'eshell)
96
3146b070
GM
97;;;###autoload
98(eshell-defgroup eshell-alias nil
700645db
GM
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
42c3a9e3 104(defcustom eshell-aliases-file (expand-file-name "alias" eshell-directory-name)
ec60da52 105 "The file in which aliases are kept.
700645db
GM
106Whenever an alias is defined by the user, using the `alias' command,
107it will be written to this file. Thus, alias definitions (and
108deletions) are always permanent. This approach was chosen for the
109sake of simplicity, since that's pretty much the only benefit to be
110gained by using this module."
111 :type 'file
112 :group 'eshell-alias)
113
114(defcustom eshell-bad-command-tolerance 3
ec60da52 115 "The number of failed commands to ignore before creating an alias."
700645db
GM
116 :type 'integer
117 ;; :link '(custom-manual "(eshell)Auto-correction of bad commands")
118 :group 'eshell-alias)
119
d783d303 120(defcustom eshell-alias-load-hook nil
ec60da52 121 "A hook that gets run when `eshell-alias' is loaded."
d783d303 122 :version "24.1" ; removed eshell-alias-initialize
affbf647
GM
123 :type 'hook
124 :group 'eshell-alias)
125
126(defvar eshell-command-aliases-list nil
127 "A list of command aliases currently defined by the user.
128Each element of this alias is a list of the form:
129
130 (NAME DEFINITION)
131
132Where NAME is the textual name of the alias, and DEFINITION is the
133command string to replace that command with.
134
135Note: this list should not be modified in your '.emacs' file. Rather,
136any desired alias definitions should be declared using the `alias'
137command, which will automatically write them to the file named by
138`eshell-aliases-file'.")
139
140(put 'eshell-command-aliases-list 'risky-local-variable t)
141
142(defvar eshell-failed-commands-alist nil
143 "An alist of command name failures.")
144
145(defun eshell-alias-initialize ()
146 "Initialize the alias handling code."
147 (make-local-variable 'eshell-failed-commands-alist)
affbf647
GM
148 (add-hook 'eshell-alternate-command-hook 'eshell-fix-bad-commands t t)
149 (eshell-read-aliases-list)
dace60cf
JW
150 (add-hook 'eshell-named-command-hook 'eshell-maybe-replace-by-alias t t)
151 (make-local-variable 'eshell-complex-commands)
152 (add-to-list 'eshell-complex-commands 'eshell-command-aliased-p))
153
154(defun eshell-command-aliased-p (name)
00fbbecd 155 (assoc name eshell-command-aliases-list))
affbf647
GM
156
157(defun eshell/alias (&optional alias &rest definition)
158 "Define an ALIAS in the user's alias list using DEFINITION."
159 (if (not alias)
a9eeff78 160 (dolist (alias eshell-command-aliases-list)
affbf647
GM
161 (eshell-print (apply 'format "alias %s %s\n" alias)))
162 (if (not definition)
163 (setq eshell-command-aliases-list
164 (delq (assoc alias eshell-command-aliases-list)
165 eshell-command-aliases-list))
166 (and (stringp definition)
167 (set-text-properties 0 (length definition) nil definition))
168 (let ((def (assoc alias eshell-command-aliases-list))
169 (alias-def (list alias
170 (eshell-flatten-and-stringify definition))))
171 (if def
172 (setq eshell-command-aliases-list
173 (delq def eshell-command-aliases-list)))
174 (setq eshell-command-aliases-list
175 (cons alias-def eshell-command-aliases-list))))
176 (eshell-write-aliases-list))
177 nil)
178
700645db
GM
179(defvar pcomplete-stub)
180(autoload 'pcomplete-here "pcomplete")
181
affbf647
GM
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)
493fa1c5 219 "If COMMAND has an alias definition, call that instead using ARGS."
affbf647
GM
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.
240These are all the command aliases which begin with NAME."
241 (let (completions)
a9eeff78 242 (dolist (alias eshell-command-aliases-list)
affbf647
GM
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
700645db
GM
276(provide 'em-alias)
277
3146b070
GM
278;; Local Variables:
279;; generated-autoload-file: "esh-groups.el"
280;; End:
281
affbf647 282;;; em-alias.el ends here