Skip to content

Commit

Permalink
Support nushell (fixes #109)
Browse files Browse the repository at this point in the history
  • Loading branch information
purcell committed Apr 11, 2024
1 parent fcc0944 commit b2e9932
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 30 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
steps:
- uses: purcell/setup-emacs@master
with:
version: 29.1
version: 29.3
- uses: actions/checkout@v2
- name: Run tests
run: make package-lint
Expand All @@ -21,7 +21,7 @@ jobs:
strategy:
matrix:
emacs_version:
- 24.1
- 24.4
- 24.5
- 25.1
- 25.3
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@ variables of interest, then copying them into the Emacs environment.

## Compatibility

If the path printed by evaluating `(getenv "SHELL")` in Emacs points at `bash`
or `zsh`, this should work fine.
Supported shells:

At a minimum, this package assumes that your shell is at least UNIX-y: if
`(getenv "SHELL")` evaluates to something like `".../cmdproxy.exe"`, this
package probably isn't for you.

Further, if you use a non-POSIX-standard shell such as `tcsh` or `fish`, your
shell will be asked to execute `sh` as a subshell in order to print
out the variables in a format which can be reliably parsed. `sh` must
be a POSIX-compliant shell in this case.
* `zsh`
* `bash`
* `tcsh`
* `fish`
* `nu`

Note that shell variables which have not been exported as environment
variables (e.g. using the "export" keyword) may not be visible to
`exec-path-from-shell'.
`exec-path-from-shell`.

If you experience issues, enable the variable
`exec-path-from-shell-debug` before runnin functions from the package:
this will produce detailed logging in `*Messages*` about the shell
command line and output.

## Installation

Expand Down
79 changes: 62 additions & 17 deletions exec-path-from-shell.el
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
;; Keywords: unix, environment
;; URL: https://github.com/purcell/exec-path-from-shell
;; Package-Version: 2.1
;; Package-Requires: ((emacs "24.1") (cl-lib "0.6"))
;; Package-Requires: ((emacs "24.4"))

;; This file is not part of GNU Emacs.

Expand Down Expand Up @@ -76,6 +76,7 @@
;; Satisfy the byte compiler
(eval-when-compile (require 'eshell))
(require 'cl-lib)
(require 'json)

(defgroup exec-path-from-shell nil
"Make Emacs use shell-defined values for $PATH etc."
Expand Down Expand Up @@ -135,6 +136,10 @@ The default value denotes an interactive login shell."
(when exec-path-from-shell-debug
(apply 'message msg args)))

(defun exec-path-from-shell--nushell-p (shell)
"Return non-nil if SHELL is nu."
(string-match-p "nu$" shell))

(defun exec-path-from-shell--standard-shell-p (shell)
"Return non-nil iff SHELL supports the standard ${VAR-default} syntax."
(not (string-match-p "\\(fish\\|nu\\|t?csh\\)$" shell)))
Expand Down Expand Up @@ -188,29 +193,69 @@ shell-escaped, so they may contain $ etc."
(match-string 1)
(error "Expected printf output from shell, but got: %S" (buffer-string))))))

(defun exec-path-from-shell-getenvs--nushell (names)
"Use nushell to get the value of env vars with the given NAMES.
Execute the shell according to `exec-path-from-shell-arguments'.
The result is a list of (NAME . VALUE) pairs."
(let* ((shell (exec-path-from-shell--shell))
(expr (format "[ %s ] | to json"
(string-join
(mapcar (lambda (name)
(format "$env.%s?" (exec-path-from-shell--double-quote name)))
names)
", ")))
(shell-args (append exec-path-from-shell-arguments (list "-c" expr))))
(with-temp-buffer
(exec-path-from-shell--debug "Invoking shell %s with args %S" shell shell-args)
(let ((exit-code (exec-path-from-shell--warn-duration
(apply #'call-process shell nil t nil shell-args))))
(exec-path-from-shell--debug "Shell printed: %S" (buffer-string))
(unless (zerop exit-code)
(error "Non-zero exit code from shell %s invoked with args %S. Output was:\n%S"
shell shell-args (buffer-string))))
(goto-char (point-min))
(let ((json-array-type 'list)
(json-null :null))
(let ((values (json-read-array))
result)
(while values
(let ((value (car values)))
(push (cons (car names)
(unless (eq :null value)
(if (listp value)
(string-join value path-separator)
value)))
result))
(setq values (cdr values)
names (cdr names)))
result)))))

(defun exec-path-from-shell-getenvs (names)
"Get the environment variables with NAMES from the user's shell.
Execute the shell according to `exec-path-from-shell-arguments'.
The result is a list of (NAME . VALUE) pairs."
(when (file-remote-p default-directory)
(error "You cannot run exec-path-from-shell from a remote buffer (Tramp, etc.)"))
(let* ((random-default (md5 (format "%s%s%s" (emacs-pid) (random) (current-time))))
(dollar-names (mapcar (lambda (n) (format "${%s-%s}" n random-default)) names))
(values (split-string (exec-path-from-shell-printf
(mapconcat #'identity (make-list (length names) "%s") "\\000")
dollar-names) "\0")))
(let (result)
(while names
(prog1
(let ((value (car values)))
(push (cons (car names)
(unless (string-equal random-default value)
value))
result))
(setq values (cdr values)
names (cdr names))))
result)))
(if (exec-path-from-shell--nushell-p (exec-path-from-shell--shell))
(exec-path-from-shell-getenvs--nushell names)
(let* ((random-default (md5 (format "%s%s%s" (emacs-pid) (random) (current-time))))
(dollar-names (mapcar (lambda (n) (format "${%s-%s}" n random-default)) names))
(values (split-string (exec-path-from-shell-printf
(mapconcat #'identity (make-list (length names) "%s") "\\000")
dollar-names) "\0")))
(let (result)
(while names
(prog1
(let ((value (car values)))
(push (cons (car names)
(unless (string-equal random-default value)
value))
result))
(setq values (cdr values)
names (cdr names))))
result))))

(defun exec-path-from-shell-getenv (name)
"Get the environment variable NAME from the user's shell.
Expand Down

0 comments on commit b2e9932

Please sign in to comment.