2013-09-12 Stefan Monnier <monnier@iro.umontreal.ca>
+ Cleanup Eshell to rely less on dynamic scoping.
+ * eshell/esh-opt.el (eshell-eval-using-options): Don't bind usage-msg,
+ last-value, and ext-command here. Bind `args' closer to `body'.
+ (temp-args, last-value, usage-msg, ext-command, args): Don't defvar.
+ (eshell--args): Declare new dynamic var.
+ (eshell-do-opt): Add argument `args'. Bind our own usage-msg,
+ last-value, and ext-command. Pass `args' to `body'.
+ (eshell-process-args): Bind eshell--args.
+ (eshell-set-option): Use eshell--args.
+ * eshell/eshell.el (eshell): Use derived-mode-p.
+ * eshell/esh-var.el (eshell-parse-variable): Use backquote.
+ (eshell-parse-variable-ref): Remove unused vars `end' and `err'.
+ (eshell-glob-function): Declare.
+ * eshell/esh-util.el: Require cl-lib.
+ (eshell-read-hosts-file): Avoid add-to-list.
+ * eshell/esh-cmd.el (eshell-parse-lisp-argument): Remove unused var
+ `err'.
+ * eshell/em-unix.el (compilation-scroll-output, locate-history-list):
+ Declare.
+ (eshell/diff): Remove unused var `err'.
+ * eshell/em-rebind.el (eshell-delete-backward-char): Remove unused arg
+ `killflag'.
+ * eshell/em-pred.el (eshell-parse-modifiers): Remove unused var `err'.
+ * eshell/em-ls.el (eshell-ls-highlight-alist): Move defvars before
+ first use.
+ * eshell/em-glob.el (eshell-glob-matches, message-shown):
+ Move declaration before first use.
+ * eshell/em-alias.el (eshell-maybe-replace-by-alias): Use backquotes.
* autorevert.el (auto-revert-notify-handler): Use `cl-dolist' since we
rely on cl-return.
(let ((alias (eshell-lookup-alias command)))
(if alias
(throw 'eshell-replace-command
- (list
- 'let
- (list
- (list 'eshell-command-name
- (list 'quote eshell-last-command-name))
- (list 'eshell-command-arguments
- (list 'quote eshell-last-arguments))
- (list 'eshell-prevent-alias-expansion
- (list 'quote
- (cons command
- eshell-prevent-alias-expansion))))
- (eshell-parse-command (nth 1 alias))))))))
+ `(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.
(goto-char (1+ end))))))))))
(defvar eshell-glob-chars-regexp nil)
+(defvar eshell-glob-matches)
+(defvar message-shown)
(defun eshell-glob-regexp (pattern)
"Convert glob-pattern PATTERN to a regular expression.
(error "No matches found: %s" glob)
glob))))
-(defvar eshell-glob-matches)
-(defvar message-shown)
-
;; FIXME does this really need to abuse eshell-glob-matches, message-shown?
(defun eshell-glob-entries (path globs &optional recurse-p)
"Glob the entries in PATHS, possibly recursing if RECURSE-P is non-nil."
:type '(repeat (cons function face))
:group 'eshell-ls)
+(defvar block-size)
+(defvar dereference-links)
+(defvar dir-literal)
+(defvar error-func)
+(defvar flush-func)
+(defvar human-readable)
+(defvar ignore-pattern)
+(defvar insert-func)
+(defvar listing-style)
+(defvar numeric-uid-gid)
+(defvar reverse-list)
+(defvar show-all)
+(defvar show-almost-all)
+(defvar show-recursive)
+(defvar show-size)
+(defvar sort-method)
+(defvar ange-cache)
+(defvar dired-flag)
+
;;; Functions:
(defun eshell-ls-insert-directory
(put 'eshell/ls 'eshell-no-numeric-conversions t)
-(defvar block-size)
-(defvar dereference-links)
-(defvar dir-literal)
-(defvar error-func)
-(defvar flush-func)
-(defvar human-readable)
-(defvar ignore-pattern)
-(defvar insert-func)
-(defvar listing-style)
-(defvar numeric-uid-gid)
-(defvar reverse-list)
-(defvar show-all)
-(defvar show-almost-all)
-(defvar show-recursive)
-(defvar show-size)
-(defvar sort-method)
-(defvar ange-cache)
-(defvar dired-flag)
-
(declare-function eshell-glob-regexp "em-glob" (pattern))
(defun eshell-do-ls (&rest args)
succeeds; MOD-FUNCS take any string and preform a modification,
returning the resultant string."
(let (result negate follow preds mods)
- (condition-case err
+ (condition-case nil
(while (not (eobp))
(let ((char (char-after)))
(cond
(cdar bindings))
(setq bindings (cdr bindings)))))
-(defun eshell-delete-backward-char (n &optional killflag)
+(defun eshell-delete-backward-char (n)
"Delete the last character, unless it's part of the output."
(interactive "P")
(let ((count (prefix-numeric-value n)))
(goto-char (point-min))
(resize-temp-buffer-window))))))
+(defvar compilation-scroll-output)
+
(defun eshell-grep (command args &optional maybe-use-occur)
"Generic service function for the various grep aliases.
It calls Emacs's grep utility if the command is not redirecting output,
(setq args nil)
(setcdr (last args 3) nil))
(with-current-buffer
- (condition-case err
+ (condition-case nil
(diff-no-select
old new
(nil-blank-string (eshell-flatten-and-stringify args)))
(put 'eshell/diff 'eshell-no-numeric-conversions t)
+(defvar locate-history-list)
+
(defun eshell/locate (&rest args)
"Alias \"locate\" to call Emacs `locate' function."
(if (or eshell-plain-locate-behavior
(eshell-resolve-current-argument)
eshell-current-argument))
-(defsubst eshell-operator (&rest args)
+(defsubst eshell-operator (&rest _args)
"A stub function that generates an error if a floating operator is found."
(error "Unhandled operator in input text"))
(looking-at eshell-lisp-regexp))
(let* ((here (point))
(obj
- (condition-case err
+ (condition-case nil
(read (current-buffer))
(end-of-file
(throw 'eshell-incomplete ?\()))))
(require 'esh-ext)
;; Unused.
-;;; (defgroup eshell-opt nil
-;;; "The options processing code handles command argument parsing for
-;;; Eshell commands implemented in Lisp."
-;;; :tag "Command options processing"
-;;; :group 'eshell)
+;; (defgroup eshell-opt nil
+;; "The options processing code handles command argument parsing for
+;; Eshell commands implemented in Lisp."
+;; :tag "Command options processing"
+;; :group 'eshell)
;;; User Functions:
macro-args
(list 'eshell-stringify-list
(list 'eshell-flatten-list macro-args)))))
- (let ,(append (delq nil (mapcar (lambda (opt)
+ (let ,(delq nil (mapcar (lambda (opt)
(and (listp opt) (nth 3 opt)))
(cadr options)))
- '(usage-msg last-value ext-command args))
;; FIXME: `options' ends up hiding some variable names under `quote',
;; which is incompatible with lexical scoping!!
- (eshell-do-opt ,name ,options (lambda () ,@body-forms)))))
+ (eshell-do-opt ,name ,options (lambda (args) ,@body-forms) temp-args))))
;;; Internal Functions:
-(defvar temp-args)
-(defvar last-value)
-(defvar usage-msg)
-(defvar ext-command)
;; Documented part of the interface; see eshell-eval-using-options.
-(defvar args)
+(defvar eshell--args)
-(defun eshell-do-opt (name options body-fun)
+(defun eshell-do-opt (name options body-fun args)
"Helper function for `eshell-eval-using-options'.
This code doesn't really need to be macro expanded everywhere."
- (setq args temp-args)
- (if (setq
- ext-command
+ (let* (last-value
+ (ext-command
(catch 'eshell-ext-command
- (when (setq
- usage-msg
+ (let ((usage-msg
(catch 'eshell-usage
(setq last-value nil)
(if (and (= (length args) 0)
(throw 'eshell-usage
(eshell-show-usage name options)))
(setq args (eshell-process-args name args options)
- last-value (funcall body-fun))
- nil))
- (error "%s" usage-msg))))
+ last-value (funcall body-fun args))
+ nil)))
+ (when usage-msg
+ (error "%s" usage-msg))))))
+ (if ext-command
(throw 'eshell-external
(eshell-external-command ext-command args))
- last-value))
+ last-value)))
(defun eshell-show-usage (name options)
"Display the usage message for NAME, using OPTIONS."
(if (not (nth 3 opt))
(eshell-show-usage name options)
(if (eq (nth 2 opt) t)
- (if (> ai (length args))
+ (if (> ai (length eshell--args))
(error "%s: missing option argument" name)
- (set (nth 3 opt) (nth ai args))
+ (set (nth 3 opt) (nth ai eshell--args))
(if (> ai 0)
- (setcdr (nthcdr (1- ai) args) (nthcdr (1+ ai) args))
- (setq args (cdr args))))
+ (setcdr (nthcdr (1- ai) eshell--args)
+ (nthcdr (1+ ai) eshell--args))
+ (setq eshell--args (cdr eshell--args))))
(set (nth 3 opt) (or (nth 2 opt) t)))))
(defun eshell-process-option (name switch kind ai options)
(setq extcmd (eshell-search-path (cadr extcmd)))
(if extcmd
(throw 'eshell-ext-command extcmd)
- (if (characterp switch)
- (error "%s: unrecognized option -%c" name switch)
- (error "%s: unrecognized option --%s" name switch))))))))
+ (error (if (characterp switch) "%s: unrecognized option -%c"
+ "%s: unrecognized option --%s")
+ name switch)))))))
(defun eshell-process-args (name args options)
"Process the given ARGS using OPTIONS.
This assumes that symbols have been intern'd by `eshell-eval-using-options'."
- (let ((ai 0) arg)
+ (let ((ai 0) arg
+ (eshell--args args))
(while (< ai (length args))
(setq arg (nth ai args))
(if (not (and (stringp arg)
;;; Code:
+(eval-when-compile (require 'cl-lib))
+
(defgroup eshell-util nil
"This is general utility code, meant for use by Eshell itself."
:tag "General utilities"
(while (re-search-forward
"^\\([^#[:space:]]+\\)\\s-+\\(\\S-+\\)\\(\\s-*\\(\\S-+\\)\\)?" nil t)
(if (match-string 1)
- (add-to-list 'hosts (match-string 1)))
+ (cl-pushnew (match-string 1) hosts :test #'equal))
(if (match-string 2)
- (add-to-list 'hosts (match-string 2)))
+ (cl-pushnew (match-string 2) hosts :test #'equal))
(if (match-string 4)
- (add-to-list 'hosts (match-string 4)))))
- (sort hosts 'string-lessp)))
+ (cl-pushnew (match-string 4) hosts :test #'equal))))
+ (sort hosts #'string-lessp)))
(defun eshell-read-hosts (file result-var timestamp-var)
"Read the contents of /etc/passwd for user names."
indices (and (not (eobp))
(eq (char-after) ?\[)
(eshell-parse-indices))
- value (list 'let
- (list (list 'indices
- (list 'quote indices)))
- value))
+ value `(let ((indices ',indices)) ,value))
(if get-len
- (list 'length value)
+ `(length ,value)
value)))
(defun eshell-parse-variable-ref ()
<LONG-NAME> disambiguates the length of the name
{COMMAND} result of command is variable's value
(LISP-FORM) result of Lisp form is variable's value"
- (let (end)
- (cond
- ((eq (char-after) ?{)
- (let ((end (eshell-find-delimiter ?\{ ?\})))
- (if (not end)
- (throw 'eshell-incomplete ?\{)
- (prog1
- (list 'eshell-convert
- (list 'eshell-command-to-value
- (list 'eshell-as-subcommand
- (eshell-parse-command
- (cons (1+ (point)) end)))))
- (goto-char (1+ end))))))
- ((memq (char-after) '(?\' ?\"))
- (let ((name (if (eq (char-after) ?\')
- (eshell-parse-literal-quote)
- (eshell-parse-double-quote))))
- (if name
+ (cond
+ ((eq (char-after) ?{)
+ (let ((end (eshell-find-delimiter ?\{ ?\})))
+ (if (not end)
+ (throw 'eshell-incomplete ?\{)
+ (prog1
+ (list 'eshell-convert
+ (list 'eshell-command-to-value
+ (list 'eshell-as-subcommand
+ (eshell-parse-command
+ (cons (1+ (point)) end)))))
+ (goto-char (1+ end))))))
+ ((memq (char-after) '(?\' ?\"))
+ (let ((name (if (eq (char-after) ?\')
+ (eshell-parse-literal-quote)
+ (eshell-parse-double-quote))))
+ (if name
(list 'eshell-get-variable (eval name) 'indices))))
- ((eq (char-after) ?\<)
- (let ((end (eshell-find-delimiter ?\< ?\>)))
- (if (not end)
- (throw 'eshell-incomplete ?\<)
- (let* ((temp (make-temp-file temporary-file-directory))
- (cmd (concat (buffer-substring (1+ (point)) end)
- " > " temp)))
- (prog1
- (list
- 'let (list (list 'eshell-current-handles
- (list 'eshell-create-handles temp
- (list 'quote 'overwrite))))
- (list
- 'progn
- (list 'eshell-as-subcommand
- (eshell-parse-command cmd))
- (list 'ignore
- (list 'nconc 'eshell-this-command-hook
- (list 'list
- (list 'function
- (list 'lambda nil
- (list 'delete-file temp))))))
- (list 'quote temp)))
- (goto-char (1+ end)))))))
- ((eq (char-after) ?\()
- (condition-case err
- (list 'eshell-command-to-value
- (list 'eshell-lisp-command
- (list 'quote (read (current-buffer)))))
- (end-of-file
- (throw 'eshell-incomplete ?\())))
- ((assoc (char-to-string (char-after))
- eshell-variable-aliases-list)
- (forward-char)
- (list 'eshell-get-variable
- (char-to-string (char-before)) 'indices))
- ((looking-at eshell-variable-name-regexp)
- (prog1
- (list 'eshell-get-variable (match-string 0) 'indices)
- (goto-char (match-end 0))))
- (t
- (error "Invalid variable reference")))))
+ ((eq (char-after) ?\<)
+ (let ((end (eshell-find-delimiter ?\< ?\>)))
+ (if (not end)
+ (throw 'eshell-incomplete ?\<)
+ (let* ((temp (make-temp-file temporary-file-directory))
+ (cmd (concat (buffer-substring (1+ (point)) end)
+ " > " temp)))
+ (prog1
+ (list
+ 'let (list (list 'eshell-current-handles
+ (list 'eshell-create-handles temp
+ (list 'quote 'overwrite))))
+ (list
+ 'progn
+ (list 'eshell-as-subcommand
+ (eshell-parse-command cmd))
+ (list 'ignore
+ (list 'nconc 'eshell-this-command-hook
+ (list 'list
+ (list 'function
+ (list 'lambda nil
+ (list 'delete-file temp))))))
+ (list 'quote temp)))
+ (goto-char (1+ end)))))))
+ ((eq (char-after) ?\()
+ (condition-case nil
+ (list 'eshell-command-to-value
+ (list 'eshell-lisp-command
+ (list 'quote (read (current-buffer)))))
+ (end-of-file
+ (throw 'eshell-incomplete ?\())))
+ ((assoc (char-to-string (char-after))
+ eshell-variable-aliases-list)
+ (forward-char)
+ (list 'eshell-get-variable
+ (char-to-string (char-before)) 'indices))
+ ((looking-at eshell-variable-name-regexp)
+ (prog1
+ (list 'eshell-get-variable (match-string 0) 'indices)
+ (goto-char (match-end 0))))
+ (t
+ (error "Invalid variable reference"))))
+
+(defvar eshell-glob-function)
(defun eshell-parse-indices ()
"Parse and return a list of list of indices."
(get-buffer-create eshell-buffer-name)))))
(cl-assert (and buf (buffer-live-p buf)))
(pop-to-buffer-same-window buf)
- (unless (eq major-mode 'eshell-mode)
+ (unless (derived-mode-p 'eshell-mode)
(eshell-mode))
buf))