em-alias.el 10 KB

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