summaryrefslogtreecommitdiff
path: root/lisp/eshell/esh-var.el
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2022-09-25 16:15:16 -0400
committerStefan Monnier <monnier@iro.umontreal.ca>2022-09-25 16:15:16 -0400
commit650c20f1ca4e07591a727e1cfcc74b3363d15985 (patch)
tree85d11f6437cde22f410c25e0e5f71a3131ebd07d /lisp/eshell/esh-var.el
parent8869332684c2302b5ba1ead4568bbc7ba1c0183e (diff)
parent4b85ae6a24380fb67a3315eaec9233f17a872473 (diff)
downloademacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.gz
emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.bz2
emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.zip
Merge 'master' into noverlay
Diffstat (limited to 'lisp/eshell/esh-var.el')
-rw-r--r--lisp/eshell/esh-var.el484
1 files changed, 283 insertions, 201 deletions
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index d038609d957..36e59cd5a41 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -1,6 +1,6 @@
;;; esh-var.el --- handling of variables -*- lexical-binding:t -*-
-;; Copyright (C) 1999-2017 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2022 Free Software Foundation, Inc.
;; Author: John Wiegley <johnw@gnu.org>
@@ -34,18 +34,14 @@
;;
;; "-" is a valid part of a variable name.
;;
-;; $<MYVAR>-TOO
+;; $\"MYVAR\"-TOO
+;; $'MYVAR'-TOO
;;
;; Only "MYVAR" is part of the variable name in this case.
;;
-;; $#VARIABLE
-;;
-;; Returns the length of the value of VARIABLE. This could also be
-;; done using the `length' Lisp function.
-;;
;; $(lisp)
;;
-;; Returns result of lisp evaluation. Note: Used alone like this, it
+;; Returns result of Lisp evaluation. Note: Used alone like this, it
;; is identical to just saying (lisp); but with the variable expansion
;; form, the result may be interpolated a larger string, such as
;; '$(lisp)/other'.
@@ -55,38 +51,40 @@
;; Returns the value of an eshell subcommand. See the note above
;; regarding Lisp evaluations.
;;
-;; $ANYVAR[10]
+;; $<command>
+;;
+;; Evaluates an eshell subcommand, redirecting the output to a
+;; temporary file, and returning the file name.
+;;
+;; $EXPR[10]
;;
-;; Return the 10th element of ANYVAR. If ANYVAR's value is a string,
-;; it will be split in order to make it a list. The splitting will
-;; occur at whitespace.
+;; Return the 10th element of $EXPR, which can be any dollar
+;; expression. If $EXPR's value is a string, it will be split in
+;; order to make it a list. The splitting will occur at whitespace.
;;
-;; $ANYVAR[: 10]
+;; $EXPR[10 20]
;;
-;; As above, except that splitting occurs at the colon now.
+;; As above, but instead of returning a single element, it now returns a
+;; list of two elements.
;;
-;; $ANYVAR[: 10 20]
+;; $EXPR[: 10]
;;
-;; As above, but instead of returning just a string, it now returns a
-;; list of two strings. If the result is being interpolated into a
-;; larger string, this list will be flattened into one big string,
-;; with each element separated by a space.
+;; Like $EXPR[10], except that splitting occurs at the colon now.
;;
-;; $ANYVAR["\\\\" 10]
+;; $EXPR["\\\\" 10]
;;
;; Separate on backslash characters. Actually, the first argument --
-;; if it doesn't have the form of a number, or a plain variable name
-;; -- can be any regular expression. So to split on numbers, use
-;; '$ANYVAR["[0-9]+" 10 20]'.
+;; if it doesn't have the form of a number -- can be any regular
+;; expression. So to split on numbers, use '$EXPR["[0-9]+" 10 20]'.
;;
-;; $ANYVAR[hello]
+;; $EXPR[hello]
;;
-;; Calls `assoc' on ANYVAR with 'hello', expecting it to be an alist.
+;; Calls `assoc' on $EXPR with 'hello', expecting it to be an alist.
;;
-;; $#ANYVAR[hello]
+;; $#EXPR
;;
-;; Returns the length of the cdr of the element of ANYVAR who car is
-;; equal to "hello".
+;; Returns the length of the value of $EXPR. This could also be
+;; done using the `length' Lisp function.
;;
;; There are also a few special variables defined by Eshell. '$$' is
;; the value of the last command (t or nil, in the case of an external
@@ -105,16 +103,19 @@
;;; Code:
-(provide 'esh-var)
-
(require 'esh-util)
(require 'esh-cmd)
(require 'esh-opt)
+(require 'esh-module)
+(require 'esh-arg)
+(require 'esh-io)
(require 'pcomplete)
-(require 'env)
(require 'ring)
+(defconst eshell-inside-emacs (format "%s,eshell" emacs-version)
+ "Value for the `INSIDE_EMACS' environment variable.")
+
(defgroup eshell-var nil
"Variable interpolation is introduced whenever the `$' character
appears unquoted in any argument (except when that argument is
@@ -128,107 +129,127 @@ variable value, a subcommand, or even the result of a Lisp form."
(defcustom eshell-var-load-hook nil
"A list of functions to call when loading `eshell-var'."
:version "24.1" ; removed eshell-var-initialize
- :type 'hook
- :group 'eshell-var)
+ :type 'hook)
(defcustom eshell-prefer-lisp-variables nil
"If non-nil, prefer Lisp variables to environment variables."
- :type 'boolean
- :group 'eshell-var)
+ :type 'boolean)
(defcustom eshell-complete-export-definition t
"If non-nil, completing names for `export' shows current definition."
- :type 'boolean
- :group 'eshell-var)
+ :type 'boolean)
(defcustom eshell-modify-global-environment nil
"If non-nil, using `export' changes Emacs's global environment."
- :type 'boolean
- :group 'eshell-var)
+ :type 'boolean)
(defcustom eshell-variable-name-regexp "[A-Za-z0-9_-]+"
"A regexp identifying what constitutes a variable name reference.
Note that this only applies for `$NAME'. If the syntax `$<NAME>' is
used, then NAME can contain any character, including angle brackets,
if they are quoted with a backslash."
- :type 'regexp
- :group 'eshell-var)
+ :type 'regexp)
(defcustom eshell-variable-aliases-list
- '(;; for eshell.el
- ("COLUMNS" (lambda (indices) (window-width)) t)
- ("LINES" (lambda (indices) (window-height)) t)
+ `(;; for eshell.el
+ ("COLUMNS" ,(lambda () (window-body-width nil 'remap)) t t)
+ ("LINES" ,(lambda () (window-body-height nil 'remap)) t t)
+ ("INSIDE_EMACS" eshell-inside-emacs t)
;; for eshell-cmd.el
- ("_" (lambda (indices)
- (if (not indices)
- (car (last eshell-last-arguments))
- (eshell-apply-indices eshell-last-arguments
- indices))))
+ ("_" ,(lambda (indices quoted)
+ (if (not indices)
+ (car (last eshell-last-arguments))
+ (eshell-apply-indices eshell-last-arguments
+ indices quoted))))
("?" eshell-last-command-status)
("$" eshell-last-command-result)
+
+ ;; for em-alias.el and em-script.el
("0" eshell-command-name)
- ("1" (lambda (indices) (nth 0 eshell-command-arguments)))
- ("2" (lambda (indices) (nth 1 eshell-command-arguments)))
- ("3" (lambda (indices) (nth 2 eshell-command-arguments)))
- ("4" (lambda (indices) (nth 3 eshell-command-arguments)))
- ("5" (lambda (indices) (nth 4 eshell-command-arguments)))
- ("6" (lambda (indices) (nth 5 eshell-command-arguments)))
- ("7" (lambda (indices) (nth 6 eshell-command-arguments)))
- ("8" (lambda (indices) (nth 7 eshell-command-arguments)))
- ("9" (lambda (indices) (nth 8 eshell-command-arguments)))
- ("*" (lambda (indices)
- (if (not indices)
- eshell-command-arguments
- (eshell-apply-indices eshell-command-arguments
- indices)))))
+ ("1" ,(lambda () (nth 0 eshell-command-arguments)) nil t)
+ ("2" ,(lambda () (nth 1 eshell-command-arguments)) nil t)
+ ("3" ,(lambda () (nth 2 eshell-command-arguments)) nil t)
+ ("4" ,(lambda () (nth 3 eshell-command-arguments)) nil t)
+ ("5" ,(lambda () (nth 4 eshell-command-arguments)) nil t)
+ ("6" ,(lambda () (nth 5 eshell-command-arguments)) nil t)
+ ("7" ,(lambda () (nth 6 eshell-command-arguments)) nil t)
+ ("8" ,(lambda () (nth 7 eshell-command-arguments)) nil t)
+ ("9" ,(lambda () (nth 8 eshell-command-arguments)) nil t)
+ ("*" eshell-command-arguments))
"This list provides aliasing for variable references.
-It is very similar in concept to what `eshell-user-aliases-list' does
-for commands. Each member of this defines the name of a command,
-and the Lisp value to return for that variable if it is accessed
-via the syntax `$NAME'.
-
-If the value is a function, that function will be called with two
-arguments: the list of the indices that was used in the reference, and
-whether the user is requesting the length of the ultimate element.
-For example, a reference of `$NAME[10][20]' would result in the
-function for alias `NAME' being called (assuming it were aliased to a
-function), and the arguments passed to this function would be the list
-'(10 20)', and nil."
+Each member is of the following form:
+
+ (NAME VALUE [COPY-TO-ENVIRONMENT] [SIMPLE-FUNCTION])
+
+NAME defines the name of the variable, VALUE is a Lisp value used to
+compute the string value that will be returned when the variable is
+accessed via the syntax `$NAME'.
+
+If VALUE is a function, its behavior depends on the value of
+SIMPLE-FUNCTION. If SIMPLE-FUNCTION is nil, call VALUE with two
+arguments: the list of the indices that were used in the reference,
+and either t or nil depending on whether or not the variable was
+quoted with double quotes. For example, if `NAME' were aliased
+to a function, a reference of `$NAME[10][20]' would result in that
+function being called with the arguments `((\"10\") (\"20\"))' and
+nil.
+If SIMPLE-FUNCTION is non-nil, call the function with no arguments
+and then pass its return value to `eshell-apply-indices'.
+
+If VALUE is a string, return the value for the variable with that
+name in the current environment. If no variable with that name exists
+in the environment, but if a symbol with that same name exists and has
+a value bound to it, return that symbol's value instead. You can
+prefer symbol values over environment values by setting the value
+of `eshell-prefer-lisp-variables' to t.
+
+If VALUE is a symbol, return the value bound to it.
+
+If VALUE has any other type, signal an error.
+
+Additionally, if COPY-TO-ENVIRONMENT is non-nil, the alias should be
+copied (a.k.a. \"exported\") to the environment of created subprocesses."
:type '(repeat (list string sexp
(choice (const :tag "Copy to environment" t)
- (const :tag "Use only in Eshell" nil))))
- :group 'eshell-var)
+ (const :tag "Use only in Eshell" nil))
+ (choice (const :tag "Call without argument" t)
+ (const :tag "Call with 2 arguments" nil))))
+ :risky t)
-(put 'eshell-variable-aliases-list 'risky-local-variable t)
+(defvar-keymap eshell-var-mode-map
+ "C-c M-v" #'eshell-insert-envvar)
;;; Functions:
-(defun eshell-var-initialize ()
+(define-minor-mode eshell-var-mode
+ "Minor mode for the esh-var module.
+
+\\{eshell-var-mode-map}"
+ :keymap eshell-var-mode-map)
+
+(defun eshell-var-initialize () ;Called from `eshell-mode' via intern-soft!
"Initialize the variable handle code."
;; Break the association with our parent's environment. Otherwise,
;; changing a variable will affect all of Emacs.
(unless eshell-modify-global-environment
- (set (make-local-variable 'process-environment)
- (eshell-copy-environment)))
-
- (define-key eshell-command-map [(meta ?v)] 'eshell-insert-envvar)
+ (setq-local process-environment (eshell-copy-environment)))
- (set (make-local-variable 'eshell-special-chars-inside-quoting)
+ (setq-local eshell-special-chars-inside-quoting
(append eshell-special-chars-inside-quoting '(?$)))
- (set (make-local-variable 'eshell-special-chars-outside-quoting)
+ (setq-local eshell-special-chars-outside-quoting
(append eshell-special-chars-outside-quoting '(?$)))
- (add-hook 'eshell-parse-argument-hook 'eshell-interpolate-variable t t)
+ (add-hook 'eshell-parse-argument-hook #'eshell-interpolate-variable t t)
(add-hook 'eshell-prepare-command-hook
- 'eshell-handle-local-variables nil t)
+ #'eshell-handle-local-variables nil t)
(when (eshell-using-module 'eshell-cmpl)
(add-hook 'pcomplete-try-first-hook
- 'eshell-complete-variable-reference nil t)
+ #'eshell-complete-variable-reference nil t)
(add-hook 'pcomplete-try-first-hook
- 'eshell-complete-variable-assignment nil t)))
+ #'eshell-complete-variable-assignment nil t)))
(defun eshell-handle-local-variables ()
"Allow for the syntax `VAR=val <command> <args>'."
@@ -340,9 +361,11 @@ This function is explicit for adding to `eshell-parse-argument-hook'."
(defun pcomplete/eshell-mode/setq ()
"Completion function for Eshell's `setq'."
(while (and (pcomplete-here (all-completions pcomplete-stub
- obarray 'boundp))
+ obarray #'boundp))
(pcomplete-here))))
+;; FIXME the real "env" command does more than this, it runs a program
+;; in a modified environment.
(defun eshell/env (&rest args)
"Implementation of `env' in Lisp."
(eshell-init-print-buffer)
@@ -363,9 +386,8 @@ This function is explicit for adding to `eshell-parse-argument-hook'."
(defun eshell-envvar-names (&optional environment)
"Return a list of currently visible environment variable names."
- (mapcar (function
- (lambda (x)
- (substring x 0 (string-match "=" x))))
+ (mapcar (lambda (x)
+ (substring x 0 (string-search "=" x)))
(or environment process-environment)))
(defun eshell-environment-variables ()
@@ -391,44 +413,63 @@ process any indices that come after the variable reference."
(let* ((get-len (when (eq (char-after) ?#)
(forward-char) t))
value indices)
- (setq value (eshell-parse-variable-ref)
+ (setq value (eshell-parse-variable-ref get-len)
indices (and (not (eobp))
(eq (char-after) ?\[)
(eshell-parse-indices))
- value `(let ((indices ',indices)) ,value))
- (if get-len
- `(length ,value)
- value)))
-
-(defun eshell-parse-variable-ref ()
+ ;; This is an expression that will be evaluated by `eshell-do-eval',
+ ;; which only support let-binding of dynamically-scoped vars
+ value `(let ((indices (eshell-eval-indices ',indices))) ,value))
+ (when get-len
+ (setq value `(length ,value)))
+ (when eshell-current-quoted
+ (setq value `(eshell-stringify ,value)))
+ value))
+
+(defun eshell-parse-variable-ref (&optional modifier-p)
"Eval a variable reference.
Returns a Lisp form which, if evaluated, will return the value of the
variable.
-Possible options are:
+If MODIFIER-P is non-nil, the value of the variable will be
+modified by some function. If MODIFIER-P is nil, the value will be
+used as-is; this allows optimization of some kinds of variable
+references.
+
+Possible variable references are:
NAME an environment or Lisp variable value
- <LONG-NAME> disambiguates the length of the name
+ \"LONG-NAME\" disambiguates the length of the name
+ `LONG-NAME' as above
{COMMAND} result of command is variable's value
- (LISP-FORM) result of Lisp form is variable's value"
+ (LISP-FORM) result of Lisp form is variable's value
+ <COMMAND> write the output of command to a temporary file;
+ result is the file name"
(cond
((eq (char-after) ?{)
(let ((end (eshell-find-delimiter ?\{ ?\})))
(if (not end)
(throw 'eshell-incomplete ?\{)
+ (forward-char)
(prog1
- (list 'eshell-convert
- (list 'eshell-command-to-value
- (list 'eshell-as-subcommand
- (eshell-parse-command
- (cons (1+ (point)) end)))))
+ `(eshell-apply-indices
+ (eshell-convert
+ (eshell-command-to-value
+ (eshell-as-subcommand
+ ,(let ((subcmd (or (eshell-unescape-inner-double-quote end)
+ (cons (point) end)))
+ (eshell-current-quoted nil))
+ (eshell-parse-command subcmd))))
+ ;; If this is a simple double-quoted form like
+ ;; "${COMMAND}" (i.e. no indices after the subcommand
+ ;; and no `#' modifier before), ensure we convert to a
+ ;; single string. This avoids unnecessary work
+ ;; (e.g. splitting the output by lines) when it would
+ ;; just be joined back together afterwards.
+ ,(when (and (not modifier-p) eshell-current-quoted)
+ '(not indices)))
+ indices ,eshell-current-quoted)
(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)
@@ -437,37 +478,53 @@ Possible options are:
(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)))
+ `(let ((eshell-current-handles
+ (eshell-create-handles ,temp 'overwrite)))
+ (progn
+ (eshell-as-subcommand
+ ,(let ((eshell-current-quoted nil))
+ (eshell-parse-command cmd)))
+ (ignore
+ (nconc eshell-this-command-hook
+ ;; Quote this lambda; it will be evaluated
+ ;; by `eshell-do-eval', which requires very
+ ;; particular forms in order to work
+ ;; properly. See bug#54190.
+ (list (function
+ (lambda ()
+ (delete-file ,temp)
+ (when-let ((buffer (get-file-buffer ,temp)))
+ (kill-buffer buffer)))))))
+ (eshell-apply-indices ,temp indices ,eshell-current-quoted)))
(goto-char (1+ end)))))))
((eq (char-after) ?\()
(condition-case nil
- (list 'eshell-command-to-value
- (list 'eshell-lisp-command
- (list 'quote (read (current-buffer)))))
+ `(eshell-apply-indices
+ (eshell-command-to-value
+ (eshell-lisp-command
+ ',(read (or (eshell-unescape-inner-double-quote (point-max))
+ (current-buffer)))))
+ indices ,eshell-current-quoted)
(end-of-file
(throw 'eshell-incomplete ?\())))
+ ((looking-at (rx-to-string
+ `(or "'" ,(if eshell-current-quoted "\\\"" "\""))))
+ (eshell-with-temp-command
+ (or (eshell-unescape-inner-double-quote (point-max))
+ (cons (point) (point-max)))
+ (let ((name (if (eq (char-after) ?\')
+ (eshell-parse-literal-quote)
+ (eshell-parse-double-quote))))
+ (when name
+ `(eshell-get-variable ,(eval name) indices ,eshell-current-quoted)))))
((assoc (char-to-string (char-after))
eshell-variable-aliases-list)
(forward-char)
- (list 'eshell-get-variable
- (char-to-string (char-before)) 'indices))
+ `(eshell-get-variable ,(char-to-string (char-before)) indices
+ ,eshell-current-quoted))
((looking-at eshell-variable-name-regexp)
(prog1
- (list 'eshell-get-variable (match-string 0) 'indices)
+ `(eshell-get-variable ,(match-string 0) indices ,eshell-current-quoted)
(goto-char (match-end 0))))
(t
(error "Invalid variable reference"))))
@@ -475,44 +532,64 @@ Possible options are:
(defvar eshell-glob-function)
(defun eshell-parse-indices ()
- "Parse and return a list of list of indices."
+ "Parse and return a list of index-lists.
+
+For example, \"[0 1][2]\" becomes:
+ ((\"0\" \"1\") (\"2\")."
(let (indices)
(while (eq (char-after) ?\[)
(let ((end (eshell-find-delimiter ?\[ ?\])))
(if (not end)
(throw 'eshell-incomplete ?\[)
(forward-char)
- (let (eshell-glob-function)
- (setq indices (cons (eshell-parse-arguments (point) end)
- indices)))
+ (eshell-with-temp-command (or (eshell-unescape-inner-double-quote end)
+ (cons (point) end))
+ (let (eshell-glob-function (eshell-current-quoted nil))
+ (setq indices (cons (eshell-parse-arguments
+ (point-min) (point-max))
+ indices))))
(goto-char (1+ end)))))
(nreverse indices)))
-(defun eshell-get-variable (name &optional indices)
- "Get the value for the variable NAME."
- (let* ((alias (assoc name eshell-variable-aliases-list))
- (var (if alias
- (cadr alias)
- name)))
- (if (and alias (functionp var))
- (funcall var indices)
- (eshell-apply-indices
- (cond
- ((stringp var)
- (let ((sym (intern-soft var)))
- (if (and sym (boundp sym)
- (or eshell-prefer-lisp-variables
- (memq sym eshell--local-vars) ; bug#15372
- (not (getenv var))))
- (symbol-value sym)
- (getenv var))))
- ((symbolp var)
- (symbol-value var))
- (t
- (error "Unknown variable `%s'" (eshell-stringify var))))
- indices))))
-
-(defun eshell-apply-indices (value indices)
+(defun eshell-eval-indices (indices)
+ "Evaluate INDICES, a list of index-lists generated by `eshell-parse-indices'."
+ (mapcar (lambda (i) (mapcar #'eval i)) indices))
+
+(defun eshell-get-variable (name &optional indices quoted)
+ "Get the value for the variable NAME.
+INDICES is a list of index-lists (see `eshell-parse-indices').
+If QUOTED is non-nil, this was invoked inside double-quotes."
+ (if-let ((alias (assoc name eshell-variable-aliases-list)))
+ (let ((target (nth 1 alias)))
+ (cond
+ ((functionp target)
+ (if (nth 3 alias)
+ (eshell-apply-indices (funcall target) indices quoted)
+ (condition-case nil
+ (funcall target indices quoted)
+ (wrong-number-of-arguments
+ (display-warning
+ :warning (concat "Function for `eshell-variable-aliases-list' "
+ "entry should accept two arguments: INDICES "
+ "and QUOTED.'"))
+ (funcall target indices)))))
+ ((symbolp target)
+ (eshell-apply-indices (symbol-value target) indices quoted))
+ (t
+ (eshell-get-variable target indices quoted))))
+ (unless (stringp name)
+ (error "Unknown variable `%s'" (eshell-stringify name)))
+ (eshell-apply-indices
+ (let ((sym (intern-soft name)))
+ (if (and sym (boundp sym)
+ (or eshell-prefer-lisp-variables
+ (memq sym eshell--local-vars) ; bug#15372
+ (not (getenv name))))
+ (symbol-value sym)
+ (getenv name)))
+ indices quoted)))
+
+(defun eshell-apply-indices (value indices &optional quoted)
"Apply to VALUE all of the given INDICES, returning the sub-result.
The format of INDICES is:
@@ -521,12 +598,18 @@ The format of INDICES is:
Each member of INDICES represents a level of nesting. If the first
member of a sublist is not an integer or name, and the value it's
-reference is a string, that will be used as the regexp with which is
-to divide the string into sub-parts. The default is whitespace.
+referencing is a string, that will be used as the regexp with which
+is to divide the string into sub-parts. The default is whitespace.
Otherwise, each INT-OR-NAME refers to an element of the list value.
Integers imply a direct index, and names, an associate lookup using
`assoc'.
+If QUOTED is non-nil, this was invoked inside double-quotes.
+This affects the behavior of splitting strings: without quoting,
+the split values are converted to numbers via
+`eshell-convert-to-number' if possible; with quoting, they're
+left as strings.
+
For example, to retrieve the second element of a user's record in
'/etc/passwd', the variable reference would look like:
@@ -534,16 +617,14 @@ For example, to retrieve the second element of a user's record in
(while indices
(let ((refs (car indices)))
(when (stringp value)
- (let (separator)
- (if (not (or (not (stringp (caar indices)))
- (string-match
- (concat "^" eshell-variable-name-regexp "$")
- (caar indices))))
- (setq separator (caar indices)
- refs (cdr refs)))
- (setq value
- (mapcar 'eshell-convert
- (split-string value separator)))))
+ (let (separator (index (caar indices)))
+ (when (and (stringp index)
+ (not (get-text-property 0 'number index)))
+ (setq separator index
+ refs (cdr refs)))
+ (setq value (split-string value separator))
+ (unless quoted
+ (setq value (mapcar #'eshell-convert-to-number value)))))
(cond
((< (length refs) 0)
(error "Invalid array variable index: %s"
@@ -565,23 +646,24 @@ For example, to retrieve the second element of a user's record in
"Reference VALUE using the given INDEX."
(when (and (stringp index) (get-text-property 0 'number index))
(setq index (string-to-number index)))
- (if (stringp index)
- (cdr (assoc index value))
- (cond
- ((ring-p value)
- (if (> index (ring-length value))
- (error "Index exceeds length of ring")
- (ring-ref value index)))
- ((listp value)
- (if (> index (length value))
- (error "Index exceeds length of list")
- (nth index value)))
- ((vectorp value)
- (if (> index (length value))
- (error "Index exceeds length of vector")
- (aref value index)))
- (t
- (error "Invalid data type for indexing")))))
+ (if (integerp index)
+ (cond
+ ((ring-p value)
+ (if (> index (ring-length value))
+ (error "Index exceeds length of ring")
+ (ring-ref value index)))
+ ((listp value)
+ (if (> index (length value))
+ (error "Index exceeds length of list")
+ (nth index value)))
+ ((vectorp value)
+ (if (> index (length value))
+ (error "Index exceeds length of vector")
+ (aref value index)))
+ (t
+ (error "Invalid data type for indexing")))
+ ;; INDEX is some non-integer value, so treat VALUE as an alist.
+ (cdr (assoc index value))))
;;;_* Variable name completion
@@ -605,14 +687,13 @@ For example, to retrieve the second element of a user's record in
(sort
(append
(mapcar
- (function
- (lambda (varname)
- (let ((value (eshell-get-variable varname)))
- (if (and value
- (stringp value)
- (file-directory-p value))
- (concat varname "/")
- varname))))
+ (lambda (varname)
+ (let ((value (eshell-get-variable varname)))
+ (if (and value
+ (stringp value)
+ (file-directory-p value))
+ (concat varname "/")
+ varname)))
(eshell-envvar-names (eshell-environment-variables)))
(all-completions argname obarray 'boundp)
completions)
@@ -628,4 +709,5 @@ For example, to retrieve the second element of a user's record in
(setq pcomplete-stub (substring arg pos))
(throw 'pcomplete-completions (pcomplete-entries)))))
+(provide 'esh-var)
;;; esh-var.el ends here