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