commit | author | age
|
76bbd0
|
1 |
;;; ob-comint.el --- Babel Functions for Interaction with Comint Buffers -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2009-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Eric Schulte |
|
6 |
;; Keywords: literate programming, reproducible research, comint |
|
7 |
;; Homepage: 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 on comint to ease the sending and receiving |
|
27 |
;; of commands and results from comint buffers. |
|
28 |
|
|
29 |
;; Note that the buffers in this file are analogous to sessions in |
|
30 |
;; org-babel at large. |
|
31 |
|
|
32 |
;;; Code: |
|
33 |
(require 'ob-core) |
|
34 |
(require 'org-compat) |
|
35 |
(require 'comint) |
|
36 |
|
|
37 |
(defun org-babel-comint-buffer-livep (buffer) |
|
38 |
"Check if BUFFER is a comint buffer with a live process." |
|
39 |
(let ((buffer (if buffer (get-buffer buffer)))) |
|
40 |
(and buffer (buffer-live-p buffer) (get-buffer-process buffer) buffer))) |
|
41 |
|
|
42 |
(defmacro org-babel-comint-in-buffer (buffer &rest body) |
|
43 |
"Check BUFFER and execute BODY. |
|
44 |
BUFFER is checked with `org-babel-comint-buffer-livep'. BODY is |
|
45 |
executed inside the protection of `save-excursion' and |
|
46 |
`save-match-data'." |
|
47 |
(declare (indent 1)) |
|
48 |
`(progn |
|
49 |
(unless (org-babel-comint-buffer-livep ,buffer) |
|
50 |
(error "Buffer %s does not exist or has no process" ,buffer)) |
|
51 |
(save-match-data |
|
52 |
(with-current-buffer ,buffer |
|
53 |
(save-excursion |
|
54 |
(let ((comint-input-filter (lambda (_input) nil))) |
|
55 |
,@body)))))) |
|
56 |
(def-edebug-spec org-babel-comint-in-buffer (form body)) |
|
57 |
|
|
58 |
(defmacro org-babel-comint-with-output (meta &rest body) |
|
59 |
"Evaluate BODY in BUFFER and return process output. |
|
60 |
Will wait until EOE-INDICATOR appears in the output, then return |
|
61 |
all process output. If REMOVE-ECHO and FULL-BODY are present and |
|
62 |
non-nil, then strip echo'd body from the returned output. META |
|
63 |
should be a list containing the following where the last two |
|
64 |
elements are optional. |
|
65 |
|
|
66 |
(BUFFER EOE-INDICATOR REMOVE-ECHO FULL-BODY) |
|
67 |
|
|
68 |
This macro ensures that the filter is removed in case of an error |
|
69 |
or user `keyboard-quit' during execution of body." |
|
70 |
(declare (indent 1)) |
|
71 |
(let ((buffer (nth 0 meta)) |
|
72 |
(eoe-indicator (nth 1 meta)) |
|
73 |
(remove-echo (nth 2 meta)) |
|
74 |
(full-body (nth 3 meta))) |
|
75 |
`(org-babel-comint-in-buffer ,buffer |
|
76 |
(let* ((string-buffer "") |
|
77 |
(comint-output-filter-functions |
|
78 |
(cons (lambda (text) (setq string-buffer (concat string-buffer text))) |
|
79 |
comint-output-filter-functions)) |
|
80 |
dangling-text) |
|
81 |
;; got located, and save dangling text |
|
82 |
(goto-char (process-mark (get-buffer-process (current-buffer)))) |
|
83 |
(let ((start (point)) |
|
84 |
(end (point-max))) |
|
85 |
(setq dangling-text (buffer-substring start end)) |
|
86 |
(delete-region start end)) |
|
87 |
;; pass FULL-BODY to process |
|
88 |
,@body |
|
89 |
;; wait for end-of-evaluation indicator |
|
90 |
(while (progn |
|
91 |
(goto-char comint-last-input-end) |
|
92 |
(not (save-excursion |
|
93 |
(and (re-search-forward |
|
94 |
(regexp-quote ,eoe-indicator) nil t) |
|
95 |
(re-search-forward |
|
96 |
comint-prompt-regexp nil t))))) |
|
97 |
(accept-process-output (get-buffer-process (current-buffer))) |
|
98 |
;; thought the following this would allow async |
|
99 |
;; background running, but I was wrong... |
|
100 |
;; (run-with-timer .5 .5 'accept-process-output |
|
101 |
;; (get-buffer-process (current-buffer))) |
|
102 |
) |
|
103 |
;; replace cut dangling text |
|
104 |
(goto-char (process-mark (get-buffer-process (current-buffer)))) |
|
105 |
(insert dangling-text) |
|
106 |
|
|
107 |
;; remove echo'd FULL-BODY from input |
|
108 |
(when (and ,remove-echo ,full-body |
|
109 |
(string-match |
|
110 |
(replace-regexp-in-string |
|
111 |
"\n" "[\r\n]+" (regexp-quote (or ,full-body ""))) |
|
112 |
string-buffer)) |
|
113 |
(setq string-buffer (substring string-buffer (match-end 0)))) |
|
114 |
(split-string string-buffer comint-prompt-regexp))))) |
|
115 |
(def-edebug-spec org-babel-comint-with-output (sexp body)) |
|
116 |
|
|
117 |
(defun org-babel-comint-input-command (buffer cmd) |
|
118 |
"Pass CMD to BUFFER. |
|
119 |
The input will not be echoed." |
|
120 |
(org-babel-comint-in-buffer buffer |
|
121 |
(goto-char (process-mark (get-buffer-process buffer))) |
|
122 |
(insert cmd) |
|
123 |
(comint-send-input) |
|
124 |
(org-babel-comint-wait-for-output buffer))) |
|
125 |
|
|
126 |
(defun org-babel-comint-wait-for-output (buffer) |
|
127 |
"Wait until output arrives from BUFFER. |
|
128 |
Note: this is only safe when waiting for the result of a single |
|
129 |
statement (not large blocks of code)." |
|
130 |
(org-babel-comint-in-buffer buffer |
|
131 |
(while (progn |
|
132 |
(goto-char comint-last-input-end) |
|
133 |
(not (and (re-search-forward comint-prompt-regexp nil t) |
|
134 |
(goto-char (match-beginning 0)) |
|
135 |
(string= (face-name (face-at-point)) |
|
136 |
"comint-highlight-prompt")))) |
|
137 |
(accept-process-output (get-buffer-process buffer))))) |
|
138 |
|
|
139 |
(defun org-babel-comint-eval-invisibly-and-wait-for-file |
|
140 |
(buffer file string &optional period) |
|
141 |
"Evaluate STRING in BUFFER invisibly. |
|
142 |
Don't return until FILE exists. Code in STRING must ensure that |
|
143 |
FILE exists at end of evaluation." |
|
144 |
(unless (org-babel-comint-buffer-livep buffer) |
|
145 |
(error "Buffer %s does not exist or has no process" buffer)) |
|
146 |
(when (file-exists-p file) (delete-file file)) |
|
147 |
(process-send-string |
|
148 |
(get-buffer-process buffer) |
|
149 |
(if (= (aref string (1- (length string))) ?\n) string (concat string "\n"))) |
|
150 |
(while (not (file-exists-p file)) (sit-for (or period 0.25)))) |
|
151 |
|
|
152 |
(provide 'ob-comint) |
|
153 |
|
|
154 |
|
|
155 |
|
|
156 |
;;; ob-comint.el ends here |