dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

ob-eval.el (6865B)


      1 ;;; ob-eval.el --- Babel Functions for External Code Evaluation -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2009-2023 Free Software Foundation, Inc.
      4 
      5 ;; Author: Eric Schulte
      6 ;; Keywords: literate programming, reproducible research, comint
      7 ;; URL: https://orgmode.org
      8 
      9 ;; This file is part of GNU Emacs.
     10 
     11 ;; GNU Emacs is free software: you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; GNU Emacs is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;; These functions build existing Emacs support for executing external
     27 ;; shell commands.
     28 
     29 ;;; Code:
     30 
     31 (require 'org-macs)
     32 (org-assert-version)
     33 
     34 (eval-when-compile (require 'subr-x))  ; For `string-empty-p', Emacs < 29
     35 
     36 (defvar org-babel-error-buffer-name "*Org-Babel Error Output*")
     37 (declare-function org-babel-temp-file "ob-core" (prefix &optional suffix))
     38 
     39 (defun org-babel-eval-error-notify (exit-code stderr)
     40   "Open a buffer to display STDERR and a message with the value of EXIT-CODE."
     41   (let ((buf (get-buffer-create org-babel-error-buffer-name)))
     42     (with-current-buffer buf
     43       (goto-char (point-max))
     44       (save-excursion
     45         (unless (bolp) (insert "\n"))
     46         (insert stderr)
     47         (insert (format "[ Babel evaluation exited with code %S ]" exit-code))))
     48     (display-buffer buf))
     49   (message "Babel evaluation exited with code %S" exit-code))
     50 
     51 (defun org-babel-eval (command query)
     52   "Run COMMAND on QUERY.
     53 Return standard output produced by COMMAND.  If COMMAND exits
     54 with a non-zero code or produces error output, show it with
     55 `org-babel-eval-error-notify'.
     56 
     57 Writes QUERY into a temp-buffer that is processed with
     58 `org-babel--shell-command-on-region'."
     59   (let ((error-buffer (get-buffer-create " *Org-Babel Error*")) exit-code)
     60     (with-current-buffer error-buffer (erase-buffer))
     61     (with-temp-buffer
     62       (insert query)
     63       (setq exit-code
     64             (org-babel--shell-command-on-region
     65              command error-buffer))
     66       (let ((stderr (with-current-buffer error-buffer (buffer-string))))
     67         (if (or (not (numberp exit-code))
     68                 (> exit-code 0)
     69                 (not (string-empty-p stderr)))
     70             (progn
     71               (org-babel-eval-error-notify exit-code stderr)
     72               (save-excursion
     73                 (when (get-buffer org-babel-error-buffer-name)
     74                   (with-current-buffer org-babel-error-buffer-name
     75                     (unless (derived-mode-p 'compilation-mode)
     76                       (compilation-mode))
     77                     ;; Compilation-mode enforces read-only, but
     78                     ;; Babel expects the buffer modifiable.
     79                     (setq buffer-read-only nil))))
     80               ;; Return output, if any.
     81               (buffer-string))
     82           (buffer-string))))))
     83 
     84 (defun org-babel-eval-read-file (file)
     85   "Return the contents of FILE as a string."
     86   (with-temp-buffer (insert-file-contents file)
     87 		    (buffer-string)))
     88 
     89 (defun org-babel--shell-command-on-region (command error-buffer)
     90   "Execute COMMAND in an inferior shell with region as input.
     91 Stripped down version of `shell-command-on-region' for internal use in
     92 Babel only.  This lets us work around errors in the original function
     93 in various versions of Emacs.  This expects the query to be run to be
     94 in the current temp buffer.  This is written into
     95 input-file.  ERROR-BUFFER is the name of the file which
     96 `org-babel-eval' has created to use for any error messages that are
     97 returned."
     98 
     99   (let ((input-file (org-babel-temp-file "ob-input-"))
    100 	(error-file (if error-buffer (org-babel-temp-file "ob-error-") nil))
    101 	(shell-file-name (org-babel--get-shell-file-name))
    102 	exit-status)
    103     ;; There is an error in `process-file' when `error-file' exists.
    104     ;; This is fixed in Emacs trunk as of 2012-12-21; let's use this
    105     ;; workaround for now.
    106     (unless (file-remote-p default-directory)
    107       (delete-file error-file))
    108     ;; we always call this with 'replace, remove conditional
    109     ;; Replace specified region with output from command.
    110     (org-babel--write-temp-buffer-input-file input-file)
    111     (setq exit-status
    112 	  (process-file shell-file-name input-file
    113 			(if error-file
    114 			    (list t error-file)
    115 			  t)
    116 			nil shell-command-switch command))
    117 
    118     (when (and input-file (file-exists-p input-file)
    119 	       ;; bind org-babel--debug-input around the call to keep
    120 	       ;; the temporary input files available for inspection
    121 	       (not (when (boundp 'org-babel--debug-input)
    122 		      org-babel--debug-input)))
    123       (delete-file input-file))
    124 
    125     (when (and error-file (file-exists-p error-file))
    126       (when (< 0 (file-attribute-size (file-attributes error-file)))
    127 	(with-current-buffer (get-buffer-create error-buffer)
    128 	  (let ((pos-from-end (- (point-max) (point))))
    129 	    (or (bobp)
    130 		(insert "\f\n"))
    131 	    ;; Do no formatting while reading error file,
    132 	    ;; because that can run a shell command, and we
    133 	    ;; don't want that to cause an infinite recursion.
    134 	    (format-insert-file error-file nil)
    135 	    ;; Put point after the inserted errors.
    136 	    (goto-char (- (point-max) pos-from-end)))
    137 	  (current-buffer)))
    138       (delete-file error-file))
    139     exit-status))
    140 
    141 (defun org-babel--write-temp-buffer-input-file (input-file)
    142   "Write the contents of the current temp buffer into INPUT-FILE."
    143   (let ((start (point-min))
    144         (end (point-max)))
    145     (goto-char start)
    146     (push-mark (point) 'nomsg)
    147     (write-region start end input-file)
    148     (delete-region start end)
    149     (exchange-point-and-mark)))
    150 
    151 (defun org-babel-eval-wipe-error-buffer ()
    152   "Delete the contents of the Org code block error buffer.
    153 This buffer is named by `org-babel-error-buffer-name'."
    154   (when (get-buffer org-babel-error-buffer-name)
    155     (with-current-buffer org-babel-error-buffer-name
    156       (delete-region (point-min) (point-max)))))
    157 
    158 (defun org-babel--get-shell-file-name ()
    159   "Return system `shell-file-name', defaulting to /bin/sh.
    160 Unfortunately, `executable-find' does not support file name
    161 handlers.  Therefore, we could use it in the local case only."
    162   ;; FIXME: Since Emacs 27, `executable-find' accepts optional second
    163   ;; argument supporting remote hosts.
    164   (cond ((and (not (file-remote-p default-directory))
    165 	      (executable-find shell-file-name))
    166 	 shell-file-name)
    167 	((file-executable-p
    168 	  (concat (file-remote-p default-directory) shell-file-name))
    169 	 shell-file-name)
    170 	("/bin/sh")))
    171 
    172 (provide 'ob-eval)
    173 
    174 ;;; ob-eval.el ends here