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