From 0140976d32f6fef3c2e2fa181f210dbd026008fe Mon Sep 17 00:00:00 2001 Message-Id: <0140976d32f6fef3c2e2fa181f210dbd026008fe.1674486794.git.yantar92@posteo.net> From: Ihor Radchenko Date: Mon, 23 Jan 2023 18:06:46 +0300 Subject: [PATCH] Provide a uniform way to inform users about missing third-party packages * lisp/org-macs.el (org-require-package): New macro trying to load a library and displaying custom error message or warning on failure. The actual package (not library) name can be provided as optional argument. * lisp/ob-R.el (org-babel-R-initiate-session): * lisp/ob-clojure.el (ob-clojure-eval-with-inf-clojure): (ob-clojure-eval-with-cider): (ob-clojure-eval-with-slime): * lisp/ob-forth.el (org-babel-forth-session-execute): * lisp/ob-gnuplot.el (org-babel-execute:gnuplot): (org-babel-gnuplot-initiate-session): * lisp/ob-haskell.el (org-babel-interpret-haskell): (org-babel-haskell-initiate-session): * lisp/ob-js.el (org-babel-execute:js): (org-babel-js-initiate-session): * lisp/ob-julia.el (org-babel-julia-initiate-session): * lisp/ob-lisp.el (org-babel-execute:lisp): * lisp/ob-ocaml.el (org-babel-prep-session:ocaml): * lisp/ob-octave.el (org-babel-octave-initiate-session): * lisp/ob-processing.el (org-babel-processing-view-sketch): * lisp/ob-ruby.el (org-babel-execute:ruby): (org-babel-ruby-initiate-session): * lisp/ol-bbdb.el (org-bbdb-open): (org-bbdb-complete-link): (org-bbdb-anniv-export-ical): * lisp/org-agenda.el: * lisp/org-plot.el (org-plot/gnuplot): * lisp/org.el: * lisp/ox-ascii.el (org-ascii-table): * lisp/ox-html.el (org-html-htmlize-generate-css): * lisp/ox-org.el: Use the new macro. * lisp/oc-csl.el: Add FIXME. oc-csl uses a custom function doing similar job. * lisp/ob-js.el (org-babel-js-initiate-session): Add FIXME noting that the third-party package is outdated. --- lisp/ob-R.el | 2 +- lisp/ob-clojure.el | 9 +++------ lisp/ob-forth.el | 2 +- lisp/ob-gnuplot.el | 4 ++-- lisp/ob-haskell.el | 4 ++-- lisp/ob-js.el | 10 ++++++---- lisp/ob-julia.el | 3 ++- lisp/ob-lisp.el | 6 +++--- lisp/ob-ocaml.el | 2 +- lisp/ob-octave.el | 6 ++++-- lisp/ob-processing.el | 2 +- lisp/ob-ruby.el | 4 ++-- lisp/oc-csl.el | 1 + lisp/ol-bbdb.el | 8 ++++---- lisp/org-agenda.el | 3 +-- lisp/org-macs.el | 9 +++++++++ lisp/org-plot.el | 2 +- lisp/org.el | 2 +- lisp/ox-ascii.el | 2 +- lisp/ox-html.el | 3 +-- lisp/ox-org.el | 3 +-- 21 files changed, 48 insertions(+), 39 deletions(-) diff --git a/lisp/ob-R.el b/lisp/ob-R.el index 4ee091118..2d22a4657 100644 --- a/lisp/ob-R.el +++ b/lisp/ob-R.el @@ -276,7 +276,7 @@ (defun org-babel-R-initiate-session (session params) (when (get-buffer session) ;; Session buffer exists, but with dead process (set-buffer session)) - (require 'ess) (R) + (org-require-package 'ess "ESS") (R) (let ((R-proc (get-process (or ess-local-process-name ess-current-process-name)))) (while (process-get R-proc 'callbacks) diff --git a/lisp/ob-clojure.el b/lisp/ob-clojure.el index d993e0cb7..5f589db00 100644 --- a/lisp/ob-clojure.el +++ b/lisp/ob-clojure.el @@ -186,8 +186,7 @@ (defvar comint-prompt-regexp) (defvar inf-clojure-comint-prompt-regexp) (defun ob-clojure-eval-with-inf-clojure (expanded params) "Evaluate EXPANDED code block with PARAMS using inf-clojure." - (condition-case nil (require 'inf-clojure) - (user-error "inf-clojure not available")) + (org-require-package 'inf-clojure) ;; Maybe initiate the inf-clojure session (unless (and inf-clojure-buffer (buffer-live-p (get-buffer inf-clojure-buffer))) @@ -228,8 +227,7 @@ (defun ob-clojure-eval-with-inf-clojure (expanded params) (defun ob-clojure-eval-with-cider (expanded params) "Evaluate EXPANDED code block with PARAMS using cider." - (condition-case nil (require 'cider) - (user-error "cider not available")) + (org-require-package 'cider "Cider") (let ((connection (cider-current-connection (cdr (assq :target params)))) (result-params (cdr (assq :result-params params))) result0) @@ -256,8 +254,7 @@ (defun ob-clojure-eval-with-cider (expanded params) (defun ob-clojure-eval-with-slime (expanded params) "Evaluate EXPANDED code block with PARAMS using slime." - (condition-case nil (require 'slime) - (user-error "slime not available")) + (org-require-package 'slime "SLIME") (with-temp-buffer (insert expanded) (slime-eval diff --git a/lisp/ob-forth.el b/lisp/ob-forth.el index e5dcad6d0..bd9ec04a4 100644 --- a/lisp/ob-forth.el +++ b/lisp/ob-forth.el @@ -55,7 +55,7 @@ (defun org-babel-execute:forth (body params) (car (last all-results)))))) (defun org-babel-forth-session-execute (body params) - (require 'forth-mode) + (org-require-package 'forth-mode) (let ((proc (forth-proc)) (rx " \\(\n:\\|compiled\n\\|ok\n\\)") (result-start)) diff --git a/lisp/ob-gnuplot.el b/lisp/ob-gnuplot.el index e3e42918c..79770c7fd 100644 --- a/lisp/ob-gnuplot.el +++ b/lisp/ob-gnuplot.el @@ -198,7 +198,7 @@ (defun org-babel-expand-body:gnuplot (body params) (defun org-babel-execute:gnuplot (body params) "Execute a block of Gnuplot code. This function is called by `org-babel-execute-src-block'." - (require 'gnuplot) + (org-require-package 'gnuplot) (let ((session (cdr (assq :session params))) (result-type (cdr (assq :results params))) (body (org-babel-expand-body:gnuplot body params)) @@ -262,7 +262,7 @@ (defun org-babel-gnuplot-initiate-session (&optional session _params) If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session. The current `gnuplot-mode' doesn't provide support for multiple sessions." - (require 'gnuplot) + (org-require-package 'gnuplot) (unless (string= session "none") (save-window-excursion (gnuplot-send-string-to-gnuplot "" "line") diff --git a/lisp/ob-haskell.el b/lisp/ob-haskell.el index 7185ed61f..2b1441c2a 100644 --- a/lisp/ob-haskell.el +++ b/lisp/ob-haskell.el @@ -122,7 +122,7 @@ (defun org-babel-haskell-execute (body params) (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))))) (defun org-babel-interpret-haskell (body params) - (require 'inf-haskell) + (org-require-package 'inf-haskell "haskell-mode") (add-hook 'inferior-haskell-hook (lambda () (setq-local comint-prompt-regexp @@ -167,7 +167,7 @@ (defun org-babel-haskell-initiate-session (&optional _session _params) "Initiate a haskell session. If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session." - (require 'inf-haskell) + (org-require-package 'inf-haskell "haskell-mode") (or (get-buffer "*haskell*") (save-window-excursion (run-haskell) (sleep-for 0.25) (current-buffer)))) diff --git a/lisp/ob-js.el b/lisp/ob-js.el index 910c11686..e19a6e543 100644 --- a/lisp/ob-js.el +++ b/lisp/ob-js.el @@ -96,7 +96,7 @@ (defun org-babel-execute:js (body params) ;; Indium Node REPL. Separate case because Indium ;; REPL is not inherited from Comint mode. ((string= session "*JS REPL*") - (require 'indium-repl) + (org-require-package 'indium-repl "indium") (unless (get-buffer session) (indium-run-node org-babel-js-cmd)) (indium-eval full-body)) @@ -168,7 +168,7 @@ (defun org-babel-js-initiate-session (&optional session _params) ((string= session "none") (warn "Session evaluation of ob-js is not supported")) ((string= "*skewer-repl*" session) - (require 'skewer-repl) + (org-require-package 'skewer-repl "skewer-mode") (let ((session-buffer (get-buffer "*skewer-repl*"))) (if (and session-buffer (org-babel-comint-buffer-livep (get-buffer session-buffer)) @@ -180,7 +180,7 @@ (defun org-babel-js-initiate-session (&optional session _params) (skewer-repl) session-buffer))) ((string= "*Javascript REPL*" session) - (require 'js-comint) + (org-require-package 'js-comint) (let ((session-buffer "*Javascript REPL*")) (if (and (org-babel-comint-buffer-livep (get-buffer session-buffer)) (comint-check-proc session-buffer)) @@ -189,7 +189,9 @@ (defun org-babel-js-initiate-session (&optional session _params) (sit-for .5) session-buffer))) ((string= "mozrepl" org-babel-js-cmd) - (require 'moz) + ;; FIXME: According to https://github.com/bard/mozrepl, this REPL + ;; is outdated and does not work for Firefox >54. + (org-require-package 'moz "mozrepl") (let ((session-buffer (save-window-excursion (run-mozilla nil) (rename-buffer session) diff --git a/lisp/ob-julia.el b/lisp/ob-julia.el index cb5c7fa3b..495169da1 100644 --- a/lisp/ob-julia.el +++ b/lisp/ob-julia.el @@ -196,7 +196,8 @@ (defun org-babel-julia-initiate-session (session params) (when (get-buffer session) ;; Session buffer exists, but with dead process (set-buffer session)) - (require 'ess) (set-buffer (julia)) + (org-require-package 'ess "ESS") + (set-buffer (julia)) (rename-buffer (if (bufferp session) (buffer-name session) diff --git a/lisp/ob-lisp.el b/lisp/ob-lisp.el index 048ef883c..03f23c82d 100644 --- a/lisp/ob-lisp.el +++ b/lisp/ob-lisp.el @@ -90,9 +90,9 @@ (defun org-babel-execute:lisp (body params) "Execute a block of Common Lisp code with Babel. BODY is the contents of the block, as a string. PARAMS is a property list containing the parameters of the block." - (require (pcase org-babel-lisp-eval-fn - (`slime-eval 'slime) - (`sly-eval 'sly))) + (pcase org-babel-lisp-eval-fn + (`slime-eval (org-require-package 'slime "SLIME")) + (`sly-eval (org-require-package 'sly "SLY"))) (org-babel-reassemble-table (let ((result (funcall (if (member "output" (cdr (assq :result-params params))) diff --git a/lisp/ob-ocaml.el b/lisp/ob-ocaml.el index 09224b98b..4ab58e150 100644 --- a/lisp/ob-ocaml.el +++ b/lisp/ob-ocaml.el @@ -109,7 +109,7 @@ (defun org-babel-execute:ocaml (body params) (defvar tuareg-interactive-buffer-name) (defun org-babel-prep-session:ocaml (session _params) "Prepare SESSION according to the header arguments in PARAMS." - (require 'tuareg) + (org-require-package 'tuareg) (let ((tuareg-interactive-buffer-name (if (and (not (string= session "none")) (not (string= session "default")) (stringp session)) diff --git a/lisp/ob-octave.el b/lisp/ob-octave.el index 9bf16b984..64eb2a32a 100644 --- a/lisp/ob-octave.el +++ b/lisp/ob-octave.el @@ -154,8 +154,10 @@ (defun org-babel-octave-initiate-session (&optional session _params matlabp) "Create an octave inferior process buffer. If there is not a current inferior-process-buffer in SESSION then create. Return the initialized session." - (if matlabp (require 'matlab) (or (require 'octave-inf nil 'noerror) - (require 'octave))) + (if matlabp + (org-require-package 'matlab "matlab-mode") + (or (require 'octave-inf nil 'noerror) + (require 'octave))) (unless (string= session "none") (let ((session (or session (if matlabp "*Inferior Matlab*" "*Inferior Octave*")))) diff --git a/lisp/ob-processing.el b/lisp/ob-processing.el index 4eeaf98e0..460e6e381 100644 --- a/lisp/ob-processing.el +++ b/lisp/ob-processing.el @@ -78,7 +78,7 @@ (defvar org-babel-processing-processing-js-filename "processing.js" (defun org-babel-processing-view-sketch () "Show the sketch of the Processing block under point in an external viewer." (interactive) - (require 'processing-mode) + (org-require-package 'processing-mode) (let ((info (org-babel-get-src-block-info))) (if (string= (nth 0 info) "processing") (let* ((body (nth 1 info)) diff --git a/lisp/ob-ruby.el b/lisp/ob-ruby.el index b94bc73dd..ba8697731 100644 --- a/lisp/ob-ruby.el +++ b/lisp/ob-ruby.el @@ -86,7 +86,7 @@ (defun org-babel-execute:ruby (body params) body params (org-babel-variable-assignments:ruby params))) (result (if (member "xmp" result-params) (with-temp-buffer - (require 'rcodetools) + (org-require-package 'rcodetools "rcodetools (gem package)") (insert full-body) (xmp (cdr (assq :xmp-option params))) (buffer-string)) @@ -161,7 +161,7 @@ (defun org-babel-ruby-initiate-session (&optional session params) If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session." (unless (string= session "none") - (require 'inf-ruby) + (org-require-package 'inf-ruby) (let* ((command (cdr (or (assq :ruby params) (assoc inf-ruby-default-implementation inf-ruby-implementations)))) diff --git a/lisp/oc-csl.el b/lisp/oc-csl.el index 432738a97..94c2ed94c 100644 --- a/lisp/oc-csl.el +++ b/lisp/oc-csl.el @@ -417,6 +417,7 @@ (defconst org-cite-csl--label-regexp ;;; Internal functions +;; FIXME: We use `org-require-package' in other places. (defun org-cite-csl--barf-without-citeproc () "Raise an error if Citeproc library is not loaded." (unless (featurep 'citeproc) diff --git a/lisp/ol-bbdb.el b/lisp/ol-bbdb.el index 47bd9d98c..915d83df2 100644 --- a/lisp/ol-bbdb.el +++ b/lisp/ol-bbdb.el @@ -255,7 +255,7 @@ (defun org-bbdb-export (path desc format _) (defun org-bbdb-open (name _) "Follow a BBDB link to NAME." - (require 'bbdb-com) + (org-require-package 'bbdb-com "bbdb") (let ((inhibit-redisplay (not debug-on-error))) (if (fboundp 'bbdb-name) (org-bbdb-open-old name) @@ -369,7 +369,7 @@ (defun org-bbdb-anniversaries () "Extract anniversaries from BBDB for display in the agenda. When called programmatically, this function expects the `date' variable to be globally bound." - (require 'bbdb) + (org-require-package 'bbdb) (require 'diary-lib) (unless (hash-table-p org-bbdb-anniv-hash) (setq org-bbdb-anniv-hash @@ -500,7 +500,7 @@ (defun org-bbdb-anniversaries-future (&optional n) (defun org-bbdb-complete-link () "Read a bbdb link with name completion." - (require 'bbdb-com) + (org-require-package 'bbdb-com "bbdb") (let ((rec (bbdb-completing-read-record "Name: "))) (concat "bbdb:" (bbdb-record-name (if (listp rec) @@ -509,7 +509,7 @@ (defun org-bbdb-complete-link () (defun org-bbdb-anniv-export-ical () "Extract anniversaries from BBDB and convert them to icalendar format." - (require 'bbdb) + (org-require-package 'bbdb) (require 'diary-lib) (unless (hash-table-p org-bbdb-anniv-hash) (setq org-bbdb-anniv-hash diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index d2aa8f3f2..3f2b5dcc8 100644 --- a/lisp/org-agenda.el +++ b/lisp/org-agenda.el @@ -3627,8 +3627,7 @@ (defun org-agenda-write (file &optional open nosettings agenda-bufname) (kill-buffer (current-buffer)) (message "Org file written to %s" file))) ((member extension '("html" "htm")) - (or (require 'htmlize nil t) - (error "Please install htmlize from https://github.com/hniksic/emacs-htmlize")) + (org-require-package 'htmlize) (declare-function htmlize-buffer "htmlize" (&optional buffer)) (set-buffer (htmlize-buffer (current-buffer))) (when org-agenda-export-html-style diff --git a/lisp/org-macs.el b/lisp/org-macs.el index cda9c5e03..9083b82f8 100644 --- a/lisp/org-macs.el +++ b/lisp/org-macs.el @@ -107,6 +107,15 @@ (defvar org-fold-core-style) ;;; Macros +(defmacro org-require-package (symbol &optional name noerror) + "Try to load library SYMBOL and display error otherwise. +With optional parameter NAME, use NAME as package name instead of +SYMBOL. Show warning instead of error when NOERROR is non-nil." + `(unless (require ,symbol nil t) + (,(if noerror 'warn 'user-error) + "`%s' failed to load required package \"%s\"" + this-command ,(or name symbol)))) + (defmacro org-with-gensyms (symbols &rest body) (declare (debug (sexp body)) (indent 1)) `(let ,(mapcar (lambda (s) diff --git a/lisp/org-plot.el b/lisp/org-plot.el index fe61e9ace..d39ffc4b4 100644 --- a/lisp/org-plot.el +++ b/lisp/org-plot.el @@ -641,7 +641,7 @@ (defun org-plot/gnuplot (&optional params) If not given options will be taken from the +PLOT line directly before or after the table." (interactive) - (require 'gnuplot) + (org-require-package 'gnuplot) (save-window-excursion (delete-other-windows) (when (get-buffer "*gnuplot*") ; reset *gnuplot* if it already running diff --git a/lisp/org.el b/lisp/org.el index 00674d1fc..958305382 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -15436,7 +15436,7 @@ (define-minor-mode org-cdlatex-mode \\{org-cdlatex-mode-map}" :lighter " OCDL" (when org-cdlatex-mode - (require 'cdlatex) + (org-require-package 'cdlatex) (run-hooks 'cdlatex-mode-hook) (cdlatex-compute-tables)) (unless org-cdlatex-texmathp-advice-is-done diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el index 9c4424b14..e5bdf92cb 100644 --- a/lisp/ox-ascii.el +++ b/lisp/ox-ascii.el @@ -1856,7 +1856,7 @@ (defun org-ascii-table (table contents info) (cond ((eq (org-element-property :type table) 'org) contents) ((and (plist-get info :ascii-table-use-ascii-art) (eq (plist-get info :ascii-charset) 'utf-8) - (require 'ascii-art-to-unicode nil t)) + (org-require-package 'ascii-art-to-unicode nil 'noerror)) (with-temp-buffer (insert (org-remove-indentation (org-element-property :value table))) diff --git a/lisp/ox-html.el b/lisp/ox-html.el index 5e58ccba3..37c474409 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -1823,8 +1823,7 @@ (defun org-html-htmlize-generate-css () to the function `org-html-htmlize-region-for-paste' will produce code that uses these same face definitions." (interactive) - (unless (require 'htmlize nil t) - (error "htmlize library missing. Aborting")) + (org-require-package 'htmlize) (and (get-buffer "*html*") (kill-buffer "*html*")) (with-temp-buffer (let ((fl (face-list)) diff --git a/lisp/ox-org.el b/lisp/ox-org.el index ed72cf4f2..9329eb159 100644 --- a/lisp/ox-org.el +++ b/lisp/ox-org.el @@ -320,8 +320,7 @@ (defun org-org-publish-to-org (plist filename pub-dir) Return output file name." (org-publish-org-to 'org filename ".org" plist pub-dir) (when (plist-get plist :htmlized-source) - (or (require 'htmlize nil t) - (error "Please install htmlize from https://github.com/hniksic/emacs-htmlize")) + (org-require-package 'htmlize) (require 'ox-html) (let* ((org-inhibit-startup t) (htmlize-output-type 'css) -- 2.39.1