unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
blob 09669477d8c009e855deb9eca96730eec869f50a 10296 bytes (raw)
name: lisp/eshell/em-alias.el 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
 
;;; em-alias.el --- creation and management of command aliases  -*- lexical-binding:t -*-

;; Copyright (C) 1999-2020 Free Software Foundation, Inc.

;; Author: John Wiegley <johnw@gnu.org>

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Command aliases greatly simplify the definition of new commands.
;; They exist as an alternative to alias functions, which are
;; otherwise quite superior, being more flexible and natural to the
;; Emacs Lisp environment (if somewhat trickier to define; [Alias
;; functions]).
;;
;;;_* Creating aliases
;;
;; The user interface is simple: type 'alias' followed by the command
;; name followed by the definition.  Argument references are made
;; using '$1', '$2', etc., or '$*'.  For example:
;;
;;   alias ll 'ls -l $*'
;;
;; This will cause the command 'll NEWS' to be replaced by 'ls -l
;; NEWS'.  This is then passed back to the command parser for
;; reparsing.{Only the command text specified in the alias definition
;; will be reparsed.  Argument references (such as '$*') are handled
;; using variable values, which means that the expansion will not be
;; reparsed, but used directly.}
;;
;; To delete an alias, specify its name without a definition:
;;
;;   alias ll
;;
;; Aliases are written to disk immediately after being defined or
;; deleted.  The filename in which they are kept is defined by the
;; variable eshell-aliases-file.

;; The format of this file is quite basic.  It specifies the alias
;; definitions in almost exactly the same way that the user entered
;; them, minus any argument quoting (since interpolation is not done
;; when the file is read).  Hence, it is possible to add new aliases
;; to the alias file directly, using a text editor rather than the
;; `alias' command.  Or, this method can be used for editing aliases
;; that have already defined.
;;
;; Here is an example of a few different aliases, and they would
;; appear in the aliases file:
;;
;;   alias clean rm -fr **/.#*~
;;   alias commit cvs commit -m changes $*
;;   alias ll ls -l $*
;;   alias info (info)
;;   alias reindex glimpseindex -o ~/Mail
;;   alias compact for i in ~/Mail/**/*~*.bz2(Lk+50) { bzip2 -9v $i }
;;
;;;_* Auto-correction of bad commands
;;
;; When a user enters the same unknown command many times during a
;; session, it is likely that they are experiencing a spelling
;; difficulty associated with a certain command.  To combat this,
;; Eshell will offer to automatically define an alias for that
;; misspelled command, once a given tolerance threshold has been
;; reached.

;; Whenever the same bad command name is encountered
;; `eshell-bad-command-tolerance' times, the user will be prompted in
;; the minibuffer to provide an alias name.  An alias definition will
;; then be created which will result in an equal call to the correct
;; name.  In this way, Eshell gradually learns about the commands that
;; the user mistypes frequently, and will automatically correct them!
;;
;; Note that a '$*' is automatically appended at the end of the alias
;; definition, so that entering it is unnecessary when specifying the
;; corrected command name.

;;; Code:

(require 'esh-mode)

;;;###autoload
(progn
(defgroup eshell-alias nil
  "Command aliases allow for easy definition of alternate commands."
  :tag "Command aliases"
  ;; :link '(info-link "(eshell)Command aliases")
  :group 'eshell-module))

(defcustom eshell-aliases-file (expand-file-name "alias" eshell-directory-name)
  "The file in which aliases are kept.
Whenever an alias is defined by the user, using the `alias' command,
it will be written to this file.  Thus, alias definitions (and
deletions) are always permanent.  This approach was chosen for the
sake of simplicity, since that's pretty much the only benefit to be
gained by using this module."
  :type 'file
  :group 'eshell-alias)

(defcustom eshell-bad-command-tolerance 3
  "The number of failed commands to ignore before creating an alias."
  :type 'integer
  ;; :link '(custom-manual "(eshell)Auto-correction of bad commands")
  :group 'eshell-alias)

(defcustom eshell-alias-load-hook nil
  "A hook that gets run when `eshell-alias' is loaded."
  :version "24.1"			; removed eshell-alias-initialize
  :type 'hook
  :group 'eshell-alias)

(defvar eshell-command-aliases-list nil
  "A list of command aliases currently defined by the user.
Each element of this alias is a list of the form:

  (NAME DEFINITION)

Where NAME is the textual name of the alias, and DEFINITION is the
command string to replace that command with.

Note: this list should not be modified in your init file.
Rather, any desired alias definitions should be declared using
the `alias' command, which will automatically write them to the
file named by `eshell-aliases-file'.")

(put 'eshell-command-aliases-list 'risky-local-variable t)

(defvar eshell-failed-commands-alist nil
  "An alist of command name failures.")

(defun eshell-alias-initialize ()    ;Called from `eshell-mode' via intern-soft!
  "Initialize the alias handling code."
  (make-local-variable 'eshell-failed-commands-alist)
  (add-hook 'eshell-alternate-command-hook #'eshell-fix-bad-commands t t)
  (eshell-read-aliases-list)
  (add-hook 'eshell-named-command-hook #'eshell-maybe-replace-by-alias t t)
  (make-local-variable 'eshell-complex-commands)
  (add-to-list 'eshell-complex-commands 'eshell-command-aliased-p))

(defun eshell-command-aliased-p (name)
  (assoc name eshell-command-aliases-list))

(defun eshell/alias (&optional alias &rest definition)
  "Define an ALIAS in the user's alias list using DEFINITION."
  (if (not alias)
      (dolist (alias eshell-command-aliases-list)
	(eshell-print (apply 'format "alias %s %s\n" alias)))
    (if (not definition)
	(setq eshell-command-aliases-list
	      (delq (assoc alias eshell-command-aliases-list)
		    eshell-command-aliases-list))
      (and (stringp definition)
	   (set-text-properties 0 (length definition) nil definition))
      (let ((def (assoc alias eshell-command-aliases-list))
	    (alias-def (list alias
			     (eshell-flatten-and-stringify definition))))
	(when def
	  (setq eshell-command-aliases-list
		(delq def eshell-command-aliases-list)))
	(setq eshell-command-aliases-list
	      (cons alias-def eshell-command-aliases-list))))
    (eshell-write-aliases-list))
  nil)

(defvar pcomplete-stub)
(autoload 'pcomplete-here "pcomplete")

(defun pcomplete/eshell-mode/alias ()
  "Completion function for Eshell's `alias' command."
  (pcomplete-here (eshell-alias-completions pcomplete-stub)))

(defun eshell-read-aliases-list ()
  "Read in an aliases list from `eshell-aliases-file'."
  (let ((file eshell-aliases-file))
    (when (file-readable-p file)
      (setq eshell-command-aliases-list
	    (with-temp-buffer
	      (let (eshell-command-aliases-list)
		(insert-file-contents file)
		(while (not (eobp))
		  (when (re-search-forward
		         "^alias\\s-+\\(\\S-+\\)\\s-+\\(.+\\)")
		    (setq eshell-command-aliases-list
			  (cons (list (match-string 1)
				      (match-string 2))
				eshell-command-aliases-list)))
		  (forward-line 1))
		eshell-command-aliases-list))))))

(defun eshell-write-aliases-list ()
  "Write out the current aliases into `eshell-aliases-file'."
  (when (file-writable-p (file-name-directory eshell-aliases-file))
    (let ((eshell-current-handles
	   (eshell-create-handles eshell-aliases-file 'overwrite)))
      (eshell/alias)
      (eshell-close-handles 0))))

(defsubst eshell-lookup-alias (name)
  "Check whether NAME is aliased.  Return the alias if there is one."
  (assoc name eshell-command-aliases-list))

(defvar eshell-prevent-alias-expansion nil)

(defun eshell-maybe-replace-by-alias (command _args)
  "Call COMMAND's alias definition, if it exists."
  (unless (and eshell-prevent-alias-expansion
	       (member command eshell-prevent-alias-expansion))
    (let ((alias (eshell-lookup-alias command)))
      (when alias
	(throw 'eshell-replace-command
	       `(let ((eshell-command-name ',eshell-last-command-name)
                      (eshell-command-arguments ',eshell-last-arguments)
                      (eshell-prevent-alias-expansion
                       ',(cons command eshell-prevent-alias-expansion)))
                  ,(eshell-parse-command (nth 1 alias))))))))

(defun eshell-alias-completions (name)
  "Find all possible completions for NAME.
These are all the command aliases which begin with NAME."
  (let (completions)
    (dolist (alias eshell-command-aliases-list)
      (when (string-match (concat "^" name) (car alias))
	(setq completions (cons (car alias) completions))))
    completions))

(defun eshell-fix-bad-commands (name)
  "If the user repeatedly a bad command NAME, make an alias for them."
  (ignore
   (unless (file-name-directory name)
     (let ((entry (assoc name eshell-failed-commands-alist)))
       (if (not entry)
	   (setq eshell-failed-commands-alist
		 (cons (cons name 1) eshell-failed-commands-alist))
	 (if (< (cdr entry) eshell-bad-command-tolerance)
	     (setcdr entry (1+ (cdr entry)))
	   (let ((alias (concat
			 (read-string
			  (format "Define alias for \"%s\": " name))
			 " $*")))
	     (eshell/alias name alias)
	     (throw 'eshell-replace-command
		    (list
		     'let
		     (list
		      (list 'eshell-command-name
			    (list 'quote name))
		      (list 'eshell-command-arguments
			    (list 'quote eshell-last-arguments))
		      (list 'eshell-prevent-alias-expansion
			    (list 'quote
				  (cons name
					eshell-prevent-alias-expansion))))
		     (eshell-parse-command alias))))))))))

(provide 'em-alias)

;; Local Variables:
;; generated-autoload-file: "esh-groups.el"
;; End:

;;; em-alias.el ends here

debug log:

solving 09669477d8 ...
found 09669477d8 in https://yhetil.org/emacs-bugs/1898378843.430816.1599957743795@ichabod.co-bxl/
found 67e0c3fc08 in https://git.savannah.gnu.org/cgit/emacs.git
preparing index
index prepared:
100644 67e0c3fc08bc6772c41396f1f10ca4fa0ae739f1	lisp/eshell/em-alias.el

applying [1/1] https://yhetil.org/emacs-bugs/1898378843.430816.1599957743795@ichabod.co-bxl/
diff --git a/lisp/eshell/em-alias.el b/lisp/eshell/em-alias.el
index 67e0c3fc08..09669477d8 100644

Checking patch lisp/eshell/em-alias.el...
Applied patch lisp/eshell/em-alias.el cleanly.

index at:
100644 09669477d8c009e855deb9eca96730eec869f50a	lisp/eshell/em-alias.el

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).