[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/buffer-env 51800506d9: Introduce buffer-env-commands
From: |
ELPA Syncer |
Subject: |
[elpa] externals/buffer-env 51800506d9: Introduce buffer-env-commands |
Date: |
Sat, 19 Mar 2022 05:57:18 -0400 (EDT) |
branch: externals/buffer-env
commit 51800506d9ecd7a44e90caf85c2c9915afdc1378
Author: Augusto Stoffel <arstoffel@gmail.com>
Commit: Augusto Stoffel <arstoffel@gmail.com>
Introduce buffer-env-commands
---
README.org | 133 +++++++++++++++++++++++++++++++++++-----------------------
buffer-env.el | 85 +++++++++++++++++++++++--------------
2 files changed, 133 insertions(+), 85 deletions(-)
diff --git a/README.org b/README.org
index 068426d502..f0989b1ccb 100644
--- a/README.org
+++ b/README.org
@@ -1,22 +1,25 @@
#+title: buffer-env --- Buffer-local process environments
+#+html: <p align="center">
#+html: <a href="http://elpa.gnu.org/packages/buffer-env.html"><img alt="GNU
ELPA" src="https://elpa.gnu.org/packages/buffer-env.svg"/></a>
#+html: <a href="https://melpa.org/#/buffer-env"><img alt="MELPA"
src="https://melpa.org/packages/buffer-env-badge.svg"/></a>
+#+html: </p>
With this package, you can teach Emacs to call the correct version of
external programs such as linters, compilers and language servers on a
/per-project/ basis. Thus you can work on several projects in
-parallel without undue interference and switching seamlessly between
+parallel with no undue interference and switch seamlessly between
them.
** Basic setup
+
*** On the project side
Your project settings should go into a shell script named =.envrc=
which exports a suitable =PATH=, as well as any other desired
environment variables. Place this script at the root directory of
your project.
-This follows the ansatz of the popular [[https://direnv.net/][direnv]]
program, and is mostly
+This follows the approach of the popular [[https://direnv.net/][direnv]]
program, and is mostly
compatible with it. However, buffer-env is entirely independent of
direnv so it is not possible to use direnv-specific features in the
=.envrc= scripts --- at least not directly.
@@ -33,30 +36,57 @@ following in your init file:
(add-hook 'hack-local-variables-hook 'buffer-env-update)
#+end_src
-This way, any buffer potentially affected by
[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html][directory-local
variables]]
-will also be affected by buffer-env. It is nonetheless possible to
-call =buffer-env-update= interactively or add it only to specific
-major-mode hooks.
+In this way, any buffer potentially affected by
[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html][directory-local
+variables]] will also be affected by buffer-env. It is also possible to
+add =buffer-env-update= only to specific major-mode hooks, or call it
+interactively.
+
+** How this package works
+When called interactively, =buffer-env-update= asks for a script file
+and executes it (in the sense detailed below). The role of the script
+is to set some environment variables. Then the Emacs variables
+=process-environment= and =exec-path= are made buffer-local and their
+values are set so as to replicate the environment defined by the
+script.
+
+When =buffer-env-update= is called from a hook, a file named
+=buffer-env-script-name= is looked up in the current directory or one
+of its parents. If found, the same procedure as in the interactive
+case takes place. Otherwise, nothing happens.
+
+It remains to clarify what “executing a script” means in the
+paragraphs above. Normally, it simply means to execute the script as
+a shell script and collect all the exported variables. However,
+certain script names are treated specially. These are:
+
+- =.env=: These files, used by Docker, Node.js and others, are simple
+ lists of =VARIABLE=value= pairs. They are still executed as shell
+ scripts (which dictates when and how quotes are to be used, for
+ instance), but no =export= statements are needed.
+- =guix.scm=: The development environment of the Guix package is
+ loaded and exported to Emacs. Make sure you have entered =guix
+ shell= at least once before to install the dependencies, otherwise
+ you may block Emacs for a long time.
+
+For instructions on how to extend this list, see the documentation of
+the variable =buffer-env-commands=.
+
+** Integration with other environment management mechanisms
+
+*** Python virtualenvs
+In most cases, the easiest way to interface with Python virtualenvs is
+to create an =.envrc= file with the following contents:
-** Alternative settings
-This package works as follows. First, a file named =buffer-env-script-name=
-is looked up in the current directory or one of its parents. In case
-of success, =buffer-env-command= is executed in a shell, with the
-found file as argument. This command should print a null-separated
-list of environment variables (and nothing else) to stdout. The
-buffer-local values of =process-environment= and =exec-path= are then
-set based on that.
+#+begin_src bash
+ source path-to-virtualenv/bin/activate
+#+end_src
-With this in sight, it should be possible to integrate with any of the
-numerous environment management tools out there.
+You can also call =buffer-env-activate= interactively and select the
+=activate= script directly.
-*** Python virtualenvs
-One can always source a virtualenv activation script from an =.envrc=
-script, but this additional step can be avoided by calling
-=buffer-env-update= manually and pointing to the =bin/activate=
-script. This can be automated if you create virtualenvs in a
-predictable place, say in a =.venv= directory at the root of each
-project; in this case you can say
+However, if you want to avoid writing =.envrc= scripts and you create
+virtualenvs in a predictable place, say in a =.venv= directory at the
+root of each project, you can say
#+begin_src emacs-lisp
(setq buffer-env-script-name ".venv/bin/activate")
@@ -67,39 +97,31 @@ absolute path for =buffer-env-script-name=, and it is
possible to specify it
as a buffer- or directory-local variable.
*** .env files
-To gather environment variables from =.env= files in the style of
-Docker, Node.js and others, use the following settings:
-
-#+begin_src emacs-lisp
- (setq buffer-env-script-name ".env")
- (setq buffer-env-command "set -a && >&2 . \"$0\" && env -0")
-#+end_src
-
-Obviously, this assumes that the =.env= file is correct when
-interpreted as a shell script, which dictates, for instance, how and
-when quotes are to be used.
+To load the environment defined by a =.env= file, you can select it
+interactively with =buffer-env-update=. To automate the process, set
+=buffer-env-script-name= to =".env"=, either globally, dir-locally or
+buffer-locally.
*** Direnv
-As mentioned above, the default settings are compatible with direnv,
-but only as long as =.envrc= is a regular shell script. If you need
-any direnv extensions, you will probably be better served by the
[[https://github.com/purcell/envrc][envrc]]
-package. It is nonetheless possible to take some advantage of direnv
-by setting
+Buffer-env is /mostly/ compatible with direnv; specifically, it assumes
+=.envrc= is a regular shell script, so you can't directly use anything
+from direnv's library of helper functions. A workaround is to use the
+following configuration:
#+begin_src emacs-lisp
- (setq buffer-env-command "direnv exec . env -0")
+ (with-eval-after-load 'buffer-env
+ (add-to-list 'buffer-env-commands '(".envrc" . "direnv exec . env -0")))
#+end_src
-*** Non-Unix-like systems
-I need to know what the appropriate value of =buffer-env-command= is
-for those. Drop me a line if you can help.
+If you need tighter integration with direnv, you may want to check out
+the [[https://github.com/purcell/envrc][envrc]] package.
** Compatibility issues
Most Emacs packages are not written with the possibility of a
-buffer-local process environment in mind. This can lead to issues
-with a few commands; specifically, those which start an external
-process after switching to a different buffer or remote
-directory. Examples include:
+buffer-local process environment in mind. This leads to issues with a
+few commands; specifically, those which start an external process
+after switching to a different buffer or remote directory. Examples
+include:
- =shell=, =project-shell= (=C-x p s=) and other REPLs;
- =compile= and =project-compile= (=C-x p c=) in Emacs 27 and older;
@@ -122,6 +144,11 @@ lean, you can just copy the function below and apply it as
an
(apply fn args)))
#+end_src
+*** Non-Unix-like systems
+Currently, this package assumes your system shell is POSIX. For other
+types of operating system, I would need to know what the appropriate
+value of =buffer-env-commands= is. Drop me a line if you can help.
+
** Related packages
This package is essentially a knockoff of the
[[https://github.com/purcell/envrc][envrc]] package by Steve
Purcell. The main difference is that envrc depends on and tightly
@@ -130,14 +157,14 @@ has no extra dependencies.
For a comparison of the buffer-local approach to environment variables
with the global approach used by most of the similar packages, see
-envrc's README.
+[[https://github.com/purcell/envrc#design-notes][envrc's design notes]].
-There is a large number of Emacs packages interfacing with Python's
+There is a large number of Emacs packages interfacing with the Python
virtualenv system. They all seem to take the global approach and,
-therefore, the comparisons and caveats in the envrc README also apply,
-mutatis mutandis.
+therefore, the comparisons and caveats in the envrc design notes also
+apply, mutatis mutandis.
** Contributing
Discussions, suggestions and code contributions are welcome! Since
-this package is part of GNU ELPA, nontrivial contributions (above 15
-lines of code) require a copyright assignment to the FSF.
+this package is part of GNU ELPA, contributions require a copyright
+assignment to the FSF.
diff --git a/buffer-env.el b/buffer-env.el
index 0b54c0cf12..a833777333 100644
--- a/buffer-env.el
+++ b/buffer-env.el
@@ -43,7 +43,7 @@
;;
;; (add-hook 'hack-local-variables-hook 'buffer-env-update)
;;
-;; This way, any buffer potentially affected by directory-local
+;; In this way, any buffer potentially affected by directory-local
;; variables will also be affected by buffer-env. It is nonetheless
;; possible to call `buffer-env-update' interactively or add it only
;; to specific major-mode hooks.
@@ -58,22 +58,36 @@
:group 'processes)
(defcustom buffer-env-script-name ".envrc"
- "Base name of the script to produce environment variables."
+ "File name of the script to produce environment variables."
:type 'string)
(defcustom buffer-env-command ">&2 . \"$0\" && env -0"
"Command to produce environment variables.
-This string is executed as a command in a shell, in the directory
-of the environment script, with its absolute file name as
-argument. The command should print a null-separated list of
-environment variables, and nothing else, to the standard
-output."
+This variable is obsolete. Use `buffer-env-commands' instead."
:type 'string)
+(make-obsolete-variable 'buffer-env-command 'buffer-env-commands "0.3" 'set)
+
+(defcustom buffer-env-commands
+ '((".env" . "set -a && >&2 . \"$0\" && env -0")
+ ("guix.scm" . ">&2 guix shell -D -f \"$0\" -- env -0")
+ ("*" . ">&2 . \"$0\" && env -0"))
+ "Alist of commands used to produce environment variables.
+For each entry, the car is glob pattern and the cdr is a shell
+command. The command specifies how to execute a script and
+collect the environment variables it defines.
+
+Specifically, the command corresponding to the script file name
+is executed in a shell, in the directory of the script, with its
+absolute file name as argument. The command should print a
+null-separated list of environment variables, and nothing else,
+to standard output."
+ :type '(alist :key-type (string :tag "Glob pattern")
+ :value-type (string :tag "Shell command")))
(defcustom buffer-env-safe-files nil
"List of scripts marked as safe to execute.
-Entries are conses consisting of the file name and a hash of its
-content."
+Entries are cons cells consisting of the file name and a hash of
+its content."
:type 'alist)
(defcustom buffer-env-ignored-variables
@@ -135,10 +149,9 @@ Files marked as safe to execute are permanently stored in
;;;###autoload
(defun buffer-env-update (&optional file)
"Update the process environment buffer locally.
-This function executes FILE in a shell, collects the exported
-variables (see `buffer-env-command' for details), and then sets
-the buffer-local values of the variables `exec-path' and
-`process-environment' accordingly.
+FILE is executed in the way prescribed by `buffer-env-commands'
+and the buffer-local values of `process-environment' and
+`exec-path' are set accordingly.
If FILE omitted, a file with base name `buffer-env-script-name'
is looked up in the current directory and its parents; nothing
@@ -151,29 +164,37 @@ When called interactively, ask for a FILE."
(read-file-name (format-prompt "Environment script"
(when file (file-relative-name
file)))
nil file t))))
- (when-let* ((file (if file
- (expand-file-name file)
- (buffer-env--locate-script)))
- (command buffer-env-command)
- ((buffer-env--authorize file))
- (vars (with-temp-buffer
- (let* ((default-directory (file-name-directory file))
- (status (call-process shell-file-name nil t nil
- shell-command-switch
- command file)))
- (if (eq 0 status)
- (split-string (buffer-substring (point-min)
(point-max))
- "\0" t)
- (prog1 nil
- (message "[buffer-env] Error in `%s', exit status
%s"
- file status)))))))
+ (when-let ((file (if file
+ (expand-file-name file)
+ (buffer-env--locate-script)))
+ ((buffer-env--authorize file))
+ (command (or buffer-env-command
+ (seq-some (pcase-lambda (`(,patt . ,command))
+ (when (string-match-p
(wildcard-to-regexp patt)
+
(file-name-nondirectory file))
+ command))
+ buffer-env-commands)))
+ (vars (with-temp-buffer
+ (let* ((default-directory (file-name-directory file))
+ (message-log-max nil)
+ (msg (format-message "[buffer-env] Running
`%s'..." file)) ;; use progress meter?
+ (status (with-temp-message msg
+ (call-process shell-file-name nil t nil
+ shell-command-switch
+ command file))))
+ (if (= status 0)
+ (split-string (buffer-substring (point-min)
(point-max))
+ "\0" t)
+ (prog1 nil
+ (message "[buffer-env] Error in `%s', exit status
%s"
+ file status)))))))
(setq-local process-environment
(nconc (seq-remove (lambda (var)
- (seq-contains-p
buffer-env-ignored-variables var
- 'string-prefix-p))
+ (seq-contains-p
buffer-env-ignored-variables
+ var 'string-prefix-p))
vars)
buffer-env-extra-variables))
- (when-let* ((path (getenv "PATH")))
+ (when-let ((path (getenv "PATH")))
(setq-local exec-path (nconc (split-string path path-separator)
(list exec-directory))))
(when buffer-env-verbose
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] externals/buffer-env 51800506d9: Introduce buffer-env-commands,
ELPA Syncer <=