Consolidate Eshell module loading/unloading code

This also adds the ability to suppress module loading/unloading
messages, which will be necessary to support running Eshell scripts as
batch scripts.

* lisp/eshell/esh-mode.el (eshell-mode): Move module
loading/initialization to...

* lisp/eshell/esh-module.el (eshell-load-modules)
(eshell-initialize-modules): ... here.
(eshell-module-loading-messages): New option.
(eshell-module--feature-name): Improve docstring.
(eshell-unload-modules): Display a real warning if unable to unload a
module.

* test/lisp/eshell/eshell-tests-helpers.el (with-temp-eshell)
(eshell-command-result-equal):
* test/lisp/eshell/eshell-tests-unload.el (load-eshell): Silence Eshell
loading messages.
This commit is contained in:
Jim Porter 2023-03-10 19:02:26 -08:00
parent 98149ad31e
commit 6c2f21e4a6
4 changed files with 72 additions and 41 deletions

View file

@ -372,36 +372,15 @@ and the hook `eshell-exit-hook'."
;; strong R2L character.
(setq bidi-paragraph-direction 'left-to-right)
;; load extension modules into memory. This will cause any global
;; variables they define to be visible, since some of the core
;; modules sometimes take advantage of their functionality if used.
(dolist (module eshell-modules-list)
(let ((module-fullname (symbol-name module))
module-shortname)
(if (string-match "^eshell-\\(.*\\)" module-fullname)
(setq module-shortname
(concat "em-" (match-string 1 module-fullname))))
(unless module-shortname
(error "Invalid Eshell module name: %s" module-fullname))
(unless (featurep (intern module-shortname))
(condition-case nil
(load module-shortname)
(error (lwarn 'eshell :error
"Unable to load module `%s' (defined in `eshell-modules-list')"
module-fullname))))))
;; Load extension modules into memory.
(eshell-load-modules eshell-modules-list)
(unless (file-exists-p eshell-directory-name)
(eshell-make-private-directory eshell-directory-name t))
;; Load core Eshell modules, then extension modules, for this session.
(dolist (module (append (eshell-subgroups 'eshell) eshell-modules-list))
(let ((load-hook (intern-soft (format "%s-load-hook" module)))
(initfunc (intern-soft (format "%s-initialize" module))))
(when (and load-hook (boundp load-hook))
(if (memq initfunc (symbol-value load-hook)) (setq initfunc nil))
(run-hooks load-hook))
;; So we don't need the -initialize functions on the hooks (bug#5375).
(and initfunc (fboundp initfunc) (funcall initfunc))))
;; Initialize core Eshell modules, then extension modules, for this session.
(eshell-initialize-modules (eshell-subgroups 'eshell))
(eshell-initialize-modules eshell-modules-list)
(if eshell-send-direct-to-subprocesses
(add-hook 'pre-command-hook #'eshell-intercept-commands t t))

View file

@ -49,6 +49,12 @@ customizing the variable `eshell-modules-list'."
:group 'eshell-module)
(make-obsolete-variable 'eshell-module-unload-hook nil "30.1")
(defcustom eshell-module-loading-messages t
"If non-nil, display messages when loading/unloading Eshell modules."
:type 'boolean
:group 'eshell-module
:version "30.1")
(defcustom eshell-modules-list
'(eshell-alias
eshell-banner
@ -87,7 +93,9 @@ Changes will only take effect in future Eshell buffers."
;;; Code:
(defsubst eshell-module--feature-name (module &optional kind)
"Get the feature name for the specified Eshell MODULE."
"Get the feature name for the specified Eshell MODULE.
KIND can be either `core' for a core module or `extension' for an
extension module; if nil, KIND defaults to `extension'."
(let ((module-name (symbol-name module))
(prefix (cond ((eq kind 'core) "esh-")
((memq kind '(extension nil)) "em-")
@ -102,17 +110,57 @@ The MODULE should be a symbol corresponding to that module's
customization group. Example: `eshell-cmpl' for that module."
(memq module eshell-modules-list))
(defun eshell-unload-modules (modules &optional kind)
"Try to unload the specified Eshell MODULES."
(defun eshell-load-modules (modules)
"Load Eshell MODULES into memory.
This will cause any global variables they define to be visible so
that other modules can take advantage of their functionality if
desired."
(let ((verbose eshell-module-loading-messages))
(dolist (module modules)
(let ((module-feature-name (eshell-module--feature-name module)))
(unless (featurep (intern module-feature-name))
(when verbose (message "Loading %s..." module))
(condition-case-unless-debug nil
(progn
(load module-feature-name nil t)
(when verbose (message "Loading %s...done" module)))
(error (when verbose (message "Loading %s...failed" module))
(lwarn 'eshell :error
"Unable to load Eshell module `%s'"
module))))))))
(defun eshell-initialize-modules (modules)
"Initialize Eshell MODULES.
This calls `MODULE-load-hook' and `MODULE-initialize' for each
MODULE, if they're defined."
(dolist (module modules)
(let ((module-feature (intern (eshell-module--feature-name module kind))))
(when (featurep module-feature)
(message "Unloading %s..." (symbol-name module))
(condition-case-unless-debug _
(progn
(unload-feature module-feature)
(message "Unloading %s...done" (symbol-name module)))
(error (message "Unloading %s...failed" (symbol-name module))))))))
(let ((load-hook (intern-soft (format "%s-load-hook" module)))
(initfunc (intern-soft (format "%s-initialize" module))))
(when (and load-hook (boundp load-hook))
(if (memq initfunc (symbol-value load-hook)) (setq initfunc nil))
(run-hooks load-hook))
;; So we don't need the -initialize functions on the hooks (bug#5375).
(and initfunc (fboundp initfunc) (funcall initfunc)))))
(defun eshell-unload-modules (modules &optional kind)
"Try to unload the specified Eshell MODULES.
KIND can be either `core' for core modules or `extension' for
extension modules; if nil, KIND defaults to `extension'."
;; We're about to unload this module, but we need to remember whether
;; to print messages.
(let ((verbose eshell-module-loading-messages))
(dolist (module modules)
(let ((module-feature (intern (eshell-module--feature-name module kind))))
(when (featurep module-feature)
(when verbose (message "Unloading %s..." module))
(condition-case-unless-debug nil
(progn
(unload-feature module-feature)
(when verbose (message "Unloading %s...done" module)))
(error (when verbose (message "Unloading %s...failed" module))
(lwarn 'eshell :error
"Unable to unload Eshell module `%s'"
module))))))))
(defun eshell-unload-extension-modules ()
"Try to unload all currently-loaded Eshell extension modules."

View file

@ -63,6 +63,7 @@ beginning of the test file."
(eshell-debug-command (cons 'process eshell-debug-command))
(eshell-history-file-name nil)
(eshell-last-dir-ring-file-name nil)
(eshell-module-loading-messages nil)
(eshell-buffer (eshell t)))
(unwind-protect
(with-current-buffer eshell-buffer
@ -183,10 +184,11 @@ inserting the command."
(defun eshell-command-result-equal (command result)
"Execute COMMAND non-interactively and compare it to RESULT."
(ert-info (#'eshell-get-debug-logs :prefix "Command logs: ")
(should (eshell-command-result--equal
command
(eshell-test-command-result command)
result))))
(let ((eshell-module-loading-messages nil))
(should (eshell-command-result--equal
command
(eshell-test-command-result command)
result)))))
(provide 'eshell-tests-helpers)

View file

@ -33,6 +33,7 @@
(defvar eshell-history-file-name)
(defvar eshell-last-dir-ring-file-name)
(defvar eshell-modules-list)
(defvar eshell-module-loading-messages)
(declare-function eshell-module--feature-name "esh-module"
(module &optional kind))
@ -51,6 +52,7 @@ See `unload-eshell'.")
(process-environment (cons "HISTFILE" process-environment))
(eshell-history-file-name nil)
(eshell-last-dir-ring-file-name nil)
(eshell-module-loading-messages nil)
(eshell-buffer (eshell t)))
(let (kill-buffer-query-functions)
(kill-buffer eshell-buffer))))))