aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorJim Porter2022-09-25 21:47:26 -0700
committerJim Porter2022-10-17 18:48:52 -0700
commit7c41016fca5ab0638f1e2fed260e2ee41f3400c2 (patch)
tree11fe29e20ed60074578e2f2f10d47ed8bd65d404 /lisp
parentf1caa10f04c980034f5ee6e0748cf3b03f460b2b (diff)
downloademacs-7c41016fca5ab0638f1e2fed260e2ee41f3400c2.tar.gz
emacs-7c41016fca5ab0638f1e2fed260e2ee41f3400c2.zip
Allow setting the values of variable aliases in Eshell
This makes commands like "COLUMNS=40 some-command" work as expected. * lisp/eshell/esh-cmd.el (eshell-subcommand-bindings): Remove 'process-environment' from here... * lisp/eshell/esh-var.el (eshell-var-initialize): ... and add to here, along with 'eshell-variable-aliases-list'. (eshell-inside-emacs): Convert to a 'defvar-local' to make it settable in a particular Eshell buffer. (eshell-variable-aliases-list): Make $?, $$, and $* read-only and update docstring. (eshell-set-variable): New function... (eshell-handle-local-variables, eshell/export, eshell/unset): ... use it. (eshell/set, pcomplete/eshell-mode/set): New functions. (eshell-get-variable): Get the variable alias's getter function when appropriate and use a safer method for checking function arity. * test/lisp/eshell/esh-var-tests.el (esh-var-test/set/env-var) (esh-var-test/set/symbol, esh-var-test/unset/env-var) (esh-var-test/unset/symbol, esh-var-test/setq, esh-var-test/export) (esh-var-test/local-variables, esh-var-test/alias/function) (esh-var-test/alias/function-pair, esh-var-test/alias/string) (esh-var-test/alias/string/prefer-lisp, esh-var-test/alias/symbol) (esh-var-test/alias/symbol-pair, esh-var-test/alias/export) (esh-var-test/alias/local-variables): New tests. * doc/misc/eshell.texi (Built-ins): Add 'set' and update 'unset' documentation. (Variables): Expand documentation of how to get/set variables.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/eshell/esh-cmd.el4
-rw-r--r--lisp/eshell/esh-var.el141
2 files changed, 107 insertions, 38 deletions
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 3f3a1616eee..c5ceb3ffd17 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -261,9 +261,9 @@ the command."
261(defcustom eshell-subcommand-bindings 261(defcustom eshell-subcommand-bindings
262 '((eshell-in-subcommand-p t) 262 '((eshell-in-subcommand-p t)
263 (eshell-in-pipeline-p nil) 263 (eshell-in-pipeline-p nil)
264 (default-directory default-directory) 264 (default-directory default-directory))
265 (process-environment (eshell-copy-environment)))
266 "A list of `let' bindings for subcommand environments." 265 "A list of `let' bindings for subcommand environments."
266 :version "29.1" ; removed `process-environment'
267 :type 'sexp 267 :type 'sexp
268 :risky t) 268 :risky t)
269 269
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index 3c09fc52fb5..caf143e1a1a 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -113,7 +113,7 @@
113(require 'pcomplete) 113(require 'pcomplete)
114(require 'ring) 114(require 'ring)
115 115
116(defconst eshell-inside-emacs (format "%s,eshell" emacs-version) 116(defvar-local eshell-inside-emacs (format "%s,eshell" emacs-version)
117 "Value for the `INSIDE_EMACS' environment variable.") 117 "Value for the `INSIDE_EMACS' environment variable.")
118 118
119(defgroup eshell-var nil 119(defgroup eshell-var nil
@@ -162,8 +162,8 @@ if they are quoted with a backslash."
162 (car (last eshell-last-arguments)) 162 (car (last eshell-last-arguments))
163 (eshell-apply-indices eshell-last-arguments 163 (eshell-apply-indices eshell-last-arguments
164 indices quoted)))) 164 indices quoted))))
165 ("?" eshell-last-command-status) 165 ("?" (eshell-last-command-status . nil))
166 ("$" eshell-last-command-result) 166 ("$" (eshell-last-command-result . nil))
167 167
168 ;; for em-alias.el and em-script.el 168 ;; for em-alias.el and em-script.el
169 ("0" eshell-command-name) 169 ("0" eshell-command-name)
@@ -176,7 +176,7 @@ if they are quoted with a backslash."
176 ("7" ,(lambda () (nth 6 eshell-command-arguments)) nil t) 176 ("7" ,(lambda () (nth 6 eshell-command-arguments)) nil t)
177 ("8" ,(lambda () (nth 7 eshell-command-arguments)) nil t) 177 ("8" ,(lambda () (nth 7 eshell-command-arguments)) nil t)
178 ("9" ,(lambda () (nth 8 eshell-command-arguments)) nil t) 178 ("9" ,(lambda () (nth 8 eshell-command-arguments)) nil t)
179 ("*" eshell-command-arguments)) 179 ("*" (eshell-command-arguments . nil)))
180 "This list provides aliasing for variable references. 180 "This list provides aliasing for variable references.
181Each member is of the following form: 181Each member is of the following form:
182 182
@@ -186,6 +186,11 @@ NAME defines the name of the variable, VALUE is a Lisp value used to
186compute the string value that will be returned when the variable is 186compute the string value that will be returned when the variable is
187accessed via the syntax `$NAME'. 187accessed via the syntax `$NAME'.
188 188
189If VALUE is a cons (GET . SET), then variable references to NAME
190will use GET to get the value, and SET to set it. GET and SET
191can be one of the forms described below. If SET is nil, the
192variable is read-only.
193
189If VALUE is a function, its behavior depends on the value of 194If VALUE is a function, its behavior depends on the value of
190SIMPLE-FUNCTION. If SIMPLE-FUNCTION is nil, call VALUE with two 195SIMPLE-FUNCTION. If SIMPLE-FUNCTION is nil, call VALUE with two
191arguments: the list of the indices that were used in the reference, 196arguments: the list of the indices that were used in the reference,
@@ -193,23 +198,30 @@ and either t or nil depending on whether or not the variable was
193quoted with double quotes. For example, if `NAME' were aliased 198quoted with double quotes. For example, if `NAME' were aliased
194to a function, a reference of `$NAME[10][20]' would result in that 199to a function, a reference of `$NAME[10][20]' would result in that
195function being called with the arguments `((\"10\") (\"20\"))' and 200function being called with the arguments `((\"10\") (\"20\"))' and
196nil. 201nil. If SIMPLE-FUNCTION is non-nil, call the function with no
197If SIMPLE-FUNCTION is non-nil, call the function with no arguments 202arguments and then pass its return value to `eshell-apply-indices'.
198and then pass its return value to `eshell-apply-indices'. 203
204When VALUE is a function, it's read-only by default. To make it
205writeable, use the (GET . SET) form described above. If SET is a
206function, it takes two arguments: a list of indices (currently
207always nil, but reserved for future enhancement), and the new
208value to set.
199 209
200If VALUE is a string, return the value for the variable with that 210If VALUE is a string, get/set the value for the variable with
201name in the current environment. If no variable with that name exists 211that name in the current environment. When getting the value, if
202in the environment, but if a symbol with that same name exists and has 212no variable with that name exists in the environment, but if a
203a value bound to it, return that symbol's value instead. You can 213symbol with that same name exists and has a value bound to it,
204prefer symbol values over environment values by setting the value 214return that symbol's value instead. You can prefer symbol values
205of `eshell-prefer-lisp-variables' to t. 215over environment values by setting the value of
216`eshell-prefer-lisp-variables' to t.
206 217
207If VALUE is a symbol, return the value bound to it. 218If VALUE is a symbol, get/set the value bound to it.
208 219
209If VALUE has any other type, signal an error. 220If VALUE has any other type, signal an error.
210 221
211Additionally, if COPY-TO-ENVIRONMENT is non-nil, the alias should be 222Additionally, if COPY-TO-ENVIRONMENT is non-nil, the alias should be
212copied (a.k.a. \"exported\") to the environment of created subprocesses." 223copied (a.k.a. \"exported\") to the environment of created subprocesses."
224 :version "29.1"
213 :type '(repeat (list string sexp 225 :type '(repeat (list string sexp
214 (choice (const :tag "Copy to environment" t) 226 (choice (const :tag "Copy to environment" t)
215 (const :tag "Use only in Eshell" nil)) 227 (const :tag "Use only in Eshell" nil))
@@ -234,6 +246,11 @@ copied (a.k.a. \"exported\") to the environment of created subprocesses."
234 ;; changing a variable will affect all of Emacs. 246 ;; changing a variable will affect all of Emacs.
235 (unless eshell-modify-global-environment 247 (unless eshell-modify-global-environment
236 (setq-local process-environment (eshell-copy-environment))) 248 (setq-local process-environment (eshell-copy-environment)))
249 (setq-local eshell-subcommand-bindings
250 (append
251 '((process-environment (eshell-copy-environment))
252 (eshell-variable-aliases-list eshell-variable-aliases-list))
253 eshell-subcommand-bindings))
237 254
238 (setq-local eshell-special-chars-inside-quoting 255 (setq-local eshell-special-chars-inside-quoting
239 (append eshell-special-chars-inside-quoting '(?$))) 256 (append eshell-special-chars-inside-quoting '(?$)))
@@ -282,9 +299,9 @@ copied (a.k.a. \"exported\") to the environment of created subprocesses."
282 (while (string-match setvar command) 299 (while (string-match setvar command)
283 (nconc 300 (nconc
284 l (list 301 l (list
285 (list 'setenv (match-string 1 command) 302 (list 'eshell-set-variable
286 (match-string 2 command) 303 (match-string 1 command)
287 (= (length (match-string 2 command)) 0)))) 304 (match-string 2 command))))
288 (setq command (eshell-stringify (car args)) 305 (setq command (eshell-stringify (car args))
289 args (cdr args))) 306 args (cdr args)))
290 (cdr l)) 307 (cdr l))
@@ -328,12 +345,11 @@ This function is explicit for adding to `eshell-parse-argument-hook'."
328 345
329(defun eshell/export (&rest sets) 346(defun eshell/export (&rest sets)
330 "This alias allows the `export' command to act as bash users expect." 347 "This alias allows the `export' command to act as bash users expect."
331 (while sets 348 (dolist (set sets)
332 (if (and (stringp (car sets)) 349 (when (and (stringp set)
333 (string-match "^\\([^=]+\\)=\\(.*\\)" (car sets))) 350 (string-match "^\\([^=]+\\)=\\(.*\\)" set))
334 (setenv (match-string 1 (car sets)) 351 (eshell-set-variable (match-string 1 set)
335 (match-string 2 (car sets)))) 352 (match-string 2 set)))))
336 (setq sets (cdr sets))))
337 353
338(defun pcomplete/eshell-mode/export () 354(defun pcomplete/eshell-mode/export ()
339 "Completion function for Eshell's `export'." 355 "Completion function for Eshell's `export'."
@@ -343,16 +359,28 @@ This function is explicit for adding to `eshell-parse-argument-hook'."
343 (eshell-envvar-names))))) 359 (eshell-envvar-names)))))
344 360
345(defun eshell/unset (&rest args) 361(defun eshell/unset (&rest args)
346 "Unset an environment variable." 362 "Unset one or more variables.
347 (while args 363This is equivalent to calling `eshell/set' for all of ARGS with
348 (if (stringp (car args)) 364the values of nil for each."
349 (setenv (car args) nil t)) 365 (dolist (arg args)
350 (setq args (cdr args)))) 366 (eshell-set-variable arg nil)))
351 367
352(defun pcomplete/eshell-mode/unset () 368(defun pcomplete/eshell-mode/unset ()
353 "Completion function for Eshell's `unset'." 369 "Completion function for Eshell's `unset'."
354 (while (pcomplete-here (eshell-envvar-names)))) 370 (while (pcomplete-here (eshell-envvar-names))))
355 371
372(defun eshell/set (&rest args)
373 "Allow command-ish use of `set'."
374 (let (last-value)
375 (while args
376 (setq last-value (eshell-set-variable (car args) (cadr args))
377 args (cddr args)))
378 last-value))
379
380(defun pcomplete/eshell-mode/set ()
381 "Completion function for Eshell's `set'."
382 (while (pcomplete-here (eshell-envvar-names))))
383
356(defun eshell/setq (&rest args) 384(defun eshell/setq (&rest args)
357 "Allow command-ish use of `setq'." 385 "Allow command-ish use of `setq'."
358 (let (last-value) 386 (let (last-value)
@@ -566,18 +594,21 @@ INDICES is a list of index-lists (see `eshell-parse-indices').
566If QUOTED is non-nil, this was invoked inside double-quotes." 594If QUOTED is non-nil, this was invoked inside double-quotes."
567 (if-let ((alias (assoc name eshell-variable-aliases-list))) 595 (if-let ((alias (assoc name eshell-variable-aliases-list)))
568 (let ((target (nth 1 alias))) 596 (let ((target (nth 1 alias)))
597 (when (and (not (functionp target))
598 (consp target))
599 (setq target (car target)))
569 (cond 600 (cond
570 ((functionp target) 601 ((functionp target)
571 (if (nth 3 alias) 602 (if (nth 3 alias)
572 (eshell-apply-indices (funcall target) indices quoted) 603 (eshell-apply-indices (funcall target) indices quoted)
573 (condition-case nil 604 (let ((max-arity (cdr (func-arity target))))
574 (funcall target indices quoted) 605 (if (or (eq max-arity 'many) (>= max-arity 2))
575 (wrong-number-of-arguments 606 (funcall target indices quoted)
576 (display-warning 607 (display-warning
577 :warning (concat "Function for `eshell-variable-aliases-list' " 608 :warning (concat "Function for `eshell-variable-aliases-list' "
578 "entry should accept two arguments: INDICES " 609 "entry should accept two arguments: INDICES "
579 "and QUOTED.'")) 610 "and QUOTED.'"))
580 (funcall target indices))))) 611 (funcall target indices)))))
581 ((symbolp target) 612 ((symbolp target)
582 (eshell-apply-indices (symbol-value target) indices quoted)) 613 (eshell-apply-indices (symbol-value target) indices quoted))
583 (t 614 (t
@@ -594,6 +625,44 @@ If QUOTED is non-nil, this was invoked inside double-quotes."
594 (getenv name))) 625 (getenv name)))
595 indices quoted))) 626 indices quoted)))
596 627
628(defun eshell-set-variable (name value)
629 "Set the variable named NAME to VALUE.
630NAME can be a string (in which case it refers to an environment
631variable or variable alias) or a symbol (in which case it refers
632to a Lisp variable)."
633 (if-let ((alias (assoc name eshell-variable-aliases-list)))
634 (let ((target (nth 1 alias)))
635 (cond
636 ((functionp target)
637 (setq target nil))
638 ((consp target)
639 (setq target (cdr target))))
640 (cond
641 ((functionp target)
642 (funcall target nil value))
643 ((null target)
644 (unless eshell-in-subcommand-p
645 (error "Variable `%s' is not settable" (eshell-stringify name)))
646 (push `(,name ,(lambda () value) t t)
647 eshell-variable-aliases-list)
648 value)
649 ;; Since getting a variable alias with a string target and
650 ;; `eshell-prefer-lisp-variables' non-nil gets the
651 ;; corresponding Lisp variable, make sure setting does the
652 ;; same.
653 ((and eshell-prefer-lisp-variables
654 (stringp target))
655 (eshell-set-variable (intern target) value))
656 (t
657 (eshell-set-variable target value))))
658 (cond
659 ((stringp name)
660 (setenv name value))
661 ((symbolp name)
662 (set name value))
663 (t
664 (error "Unknown variable `%s'" (eshell-stringify name))))))
665
597(defun eshell-apply-indices (value indices &optional quoted) 666(defun eshell-apply-indices (value indices &optional quoted)
598 "Apply to VALUE all of the given INDICES, returning the sub-result. 667 "Apply to VALUE all of the given INDICES, returning the sub-result.
599The format of INDICES is: 668The format of INDICES is: