diff options
-rw-r--r-- | guix-shell.el | 102 |
1 files changed, 67 insertions, 35 deletions
diff --git a/guix-shell.el b/guix-shell.el index bf5640f..61478c9 100644 --- a/guix-shell.el +++ b/guix-shell.el @@ -20,15 +20,15 @@ "Project environment variable cache.") (defun guix-shell--current-directory () - (project-root (project-current))) - -(defvar guix-shell--hooks '(post-command-hook before-hack-local-variables-hook) - "Hooks that guix shell should hook into.") - -(defvar guix-shell--last-directory nil - "The last directory used for running guix shell.") + "Return the project directory for the current buffer." + (let ((project (project-current))) + (if project + (project-root project) + default-directory))) (defun guix-shell--search-paths-for-directory (directory) + "Execute 'guix shell' in DIRECTORY, parse the output, and return +an alist of search path environment variables." (let ((buffer (get-buffer-create "*guix-shell-temp*")) (names '())) (with-current-buffer buffer @@ -77,51 +77,83 @@ env-vars)))) (defun guix-shell--update-search-paths-for-directory (directory) + "Refresh the cached 'guix shell' search paths for DIRECTORY." (when directory (let ((search-paths (guix-shell--search-paths-for-directory directory))) (setq guix-shell--search-paths - (cons (cons directory search-paths) + (cons (cons directory (or search-paths 'none)) (assoc-delete-all directory guix-shell--search-paths))) search-paths))) (defun guix-shell--cached-search-paths-for-directory (directory) + "Return an alist of 'guix shell' search paths cached for +DIRECTORY. If there are no cached search paths, nil is returned. +If the search paths are cached but DIRECTORY has no 'guix shell' +search paths present, 'none' is returned.." (cdr (assoc directory guix-shell--search-paths))) +;; From envrc.el +(defun guix-shell--merged-environment (process-env pairs) + "Make a `process-environment' value that merges PROCESS-ENV with PAIRS. +PAIRS is an alist obtained from direnv's output. Values from +PROCESS-ENV will be included, but their values will be masked by +Emacs' handling of `process-environment' if they also appear in +PAIRS." + (append (mapcar (lambda (pair) + (format "%s=%s" (car pair) (cdr pair))) + pairs) + process-env)) + (defun guix-shell--apply-search-paths (search-paths) - (dolist (pair search-paths) - (let ((name (car pair)) - (search-path (cdr pair))) - (setenv name search-path) - (when (string-equal name "PATH") - (setq exec-path (append (parse-colon-path search-path) - (list exec-directory))))))) + "Apply SEARCH-PATHS to the environment of the current buffer." + (setq-local process-environment + (guix-shell--merged-environment process-environment search-paths)) + (setq-local exec-path (append (parse-colon-path (getenv "PATH")) + (list exec-directory))) + (when (derived-mode-p 'eshell-mode) + (setq-local eshell-path-env path))) (defun guix-shell--apply-search-paths-for-directory (directory) - ;; TODO: What if the search paths have been updated and need to be - ;; re-applied? This doesn't account for that case currently. The only way - ;; to update would be to switch to a buffer in a different project and then - ;; change back. - (when (and directory - (not (string-equal directory guix-shell--last-directory))) - (guix-shell--apply-search-paths - (or (guix-shell--cached-search-paths-for-directory directory) - (guix-shell--update-search-paths-for-directory directory))) - (setq guix-shell--last-directory directory))) + "Set search path environment variables from 'guix shell' for +DIRECTORY." + (when directory + (let ((search-paths + (or (guix-shell--cached-search-paths-for-directory directory) + (guix-shell--update-search-paths-for-directory directory)))) + (unless (eq search-paths 'none) + (guix-shell--apply-search-paths search-paths))))) (defun guix-shell--apply-search-paths-for-current-directory () + "Set search path environment variables from 'guix shell' in the +context of the current directory." (guix-shell--apply-search-paths-for-directory (guix-shell--current-directory))) -(defun guix-shell-enable () - (interactive) - (dolist (hook guix-shell--hooks) - (add-hook hook #'guix-shell--apply-search-paths-for-current-directory))) - -(defun guix-shell-disable () - (interactive) - (dolist (hook guix-shell--hooks) - (remove-hook hook #'guix-shell--apply-search-paths-for-current-directory))) +(defun guix-shell--clear (buffer) + "Remove any effects of `guix-shell-mode' from BUFFER." + (with-current-buffer buffer + (kill-local-variable 'exec-path) + (kill-local-variable 'process-environment) + (kill-local-variable 'eshell-path-env))) (defun guix-shell-update () + "Update the search paths set by 'guix shell'." (interactive) (guix-shell--update-search-paths-for-directory - (guix-shell--current-directory))) + (guix-shell--current-directory)) + (guix-shell--clear (current-buffer)) + (guix-shell-apply-search-paths-for-current-directory)) + +;;;###autoload +(define-minor-mode guix-shell-mode + "A local minor mode in which environment variables are set by +'guix shell'." + :init-value nil + (if guix-shell-mode + (guix-shell--apply-search-paths-for-current-directory) + (guix-shell--clear (current-buffer)))) + +;;;###autoload +(define-globalized-minor-mode guix-shell-global-mode guix-shell-mode + (lambda () + (unless (or (minibufferp) (file-remote-p default-directory)) + (guix-shell-mode 1)))) |