emacs-elpa-diffs
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]