geiser-racket moved to individual package
[geiser.git] / elisp / geiser-connection.el
blob342ce7f4b3e52f90fb83b4f1ccdf1506e8bc5d14
1 ;;; geiser-connection.el -- talking to a scheme process
3 ;; Copyright (C) 2009, 2010, 2011, 2013 Jose Antonio Ortega Ruiz
5 ;; This program is free software; you can redistribute it and/or
6 ;; modify it under the terms of the Modified BSD License. You should
7 ;; have received a copy of the license along with this program. If
8 ;; not, see <http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5>.
10 ;; Start date: Sat Feb 07, 2009 21:11
12 ;;; Commentary:
14 ;; Connection datatype and functions for managing request queues
15 ;; between emacs and inferior guile processes.
18 ;;; Code:
20 (require 'geiser-log)
21 (require 'geiser-syntax)
22 (require 'geiser-base)
23 (require 'geiser-impl)
25 (require 'tq)
28 ;;; Buffer connections:
30 (make-variable-buffer-local
31 (defvar geiser-con--connection nil))
33 (defun geiser-con--get-connection (buffer/proc)
34 (if (processp buffer/proc)
35 (geiser-con--get-connection (process-buffer buffer/proc))
36 (with-current-buffer buffer/proc geiser-con--connection)))
39 ;;; Request datatype:
41 (defun geiser-con--make-request (con str cont &optional sender-buffer)
42 (list (cons :id (geiser-con--connection-inc-count con))
43 (cons :string str)
44 (cons :continuation cont)
45 (cons :buffer (or sender-buffer (current-buffer)))
46 (cons :connection con)))
48 (defsubst geiser-con--request-id (req)
49 (cdr (assq :id req)))
51 (defsubst geiser-con--request-string (req)
52 (cdr (assq :string req)))
54 (defsubst geiser-con--request-continuation (req)
55 (cdr (assq :continuation req)))
57 (defsubst geiser-con--request-buffer (req)
58 (cdr (assq :buffer req)))
60 (defsubst geiser-con--request-connection (req)
61 (cdr (assq :connection req)))
63 (defsubst geiser-con--request-deactivate (req)
64 (setcdr (assq :continuation req) nil))
66 (defsubst geiser-con--request-deactivated-p (req)
67 (null (cdr (assq :continuation req))))
70 ;;; Connection datatype:
72 (defun geiser-con--tq-create (process)
73 (let ((tq (tq-create process)))
74 (set-process-filter process
75 `(lambda (p s) (geiser-con--tq-filter ',tq s)))
76 tq))
78 (defun geiser-con--tq-filter (tq in)
79 (when (buffer-live-p (tq-buffer tq))
80 (with-current-buffer (tq-buffer tq)
81 (if (tq-queue-empty tq)
82 (progn (geiser-log--error "Unexpected queue input:\n %s" in)
83 (delete-region (point-min) (point-max)))
84 (goto-char (point-max))
85 (insert in)
86 (goto-char (point-min))
87 (when (re-search-forward (tq-queue-head-regexp tq) nil t)
88 (unwind-protect
89 (funcall (tq-queue-head-fn tq)
90 (tq-queue-head-closure tq)
91 (buffer-substring (point-min) (point)))
92 (delete-region (point-min) (point-max))
93 (tq-queue-pop tq)))))))
95 (defun geiser-con--combined-prompt (prompt debug)
96 (format "\\(%s%s\\)" prompt (if debug (format "\\|%s" debug) "")))
98 (defun geiser-con--connection-eot-re (prompt debug)
99 (geiser-con--combined-prompt (format "\n%s" prompt)
100 (and debug (format "\n%s" debug))))
102 (defun geiser-con--make-connection (proc prompt debug-prompt)
103 (list t
104 (cons :filter (process-filter proc))
105 (cons :tq (geiser-con--tq-create proc))
106 (cons :tq-filter (process-filter proc))
107 (cons :eot (geiser-con--connection-eot-re prompt debug-prompt))
108 (cons :prompt prompt)
109 (cons :debug-prompt debug-prompt)
110 (cons :is-debugging nil)
111 (cons :count 0)
112 (cons :completed (make-hash-table :weakness 'value))))
114 (defsubst geiser-con--connection-process (c)
115 (tq-process (cdr (assq :tq c))))
117 (defsubst geiser-con--connection-filter (c)
118 (cdr (assq :filter c)))
120 (defsubst geiser-con--connection-tq-filter (c)
121 (cdr (assq :tq-filter c)))
123 (defsubst geiser-con--connection-tq (c)
124 (cdr (assq :tq c)))
126 (defsubst geiser-con--connection-eot (c)
127 (cdr (assq :eot c)))
129 (defsubst geiser-con--connection-prompt (c)
130 (cdr (assq :prompt c)))
132 (defsubst geiser-con--connection-debug-prompt (c)
133 (cdr (assq :debug-prompt c)))
135 (defsubst geiser-con--connection-is-debugging (c)
136 (cdr (assq :is-debugging c)))
138 (defsubst geiser-con--connection-set-debugging (c d)
139 (setcdr (assq :is-debugging c) d))
141 (defun geiser-con--connection-update-debugging (c txt)
142 (let* ((dp (geiser-con--connection-debug-prompt c))
143 (is-d (and (stringp dp) (string-match dp txt))))
144 (geiser-con--connection-set-debugging c is-d)
145 is-d))
147 (defsubst geiser-con--connection-completed (c r)
148 (geiser-con--request-deactivate r)
149 (puthash (geiser-con--request-id r) r (cdr (assoc :completed c))))
151 (defsubst geiser-con--connection-completed-p (c id)
152 (gethash id (cdr (assoc :completed c))))
154 (defun geiser-con--connection-inc-count (c)
155 (let* ((cnt (assoc :count c))
156 (new (1+ (cdr cnt))))
157 (setcdr cnt new)
158 new))
160 (defun geiser-con--has-entered-debugger (con answer)
161 (and (not (geiser-con--connection-is-debugging con))
162 (let ((p (car (last (split-string answer "\n" t)))))
163 (and p (geiser-con--connection-update-debugging con p)))))
165 (defun geiser-con--connection-eot-p (con txt)
166 (and txt
167 (string-match-p (geiser-con--connection-eot con) txt)))
169 (defun geiser-con--connection-close (con)
170 (let ((tq (geiser-con--connection-tq con)))
171 (and tq (tq-close tq))))
173 (defvar geiser-con--startup-prompt nil)
174 (defun geiser-con--startup-prompt (p s)
175 (setq geiser-con--startup-prompt
176 (concat geiser-con--startup-prompt s))
177 nil)
179 (defun geiser-con--connection-deactivate (c &optional no-wait)
180 (when (car c)
181 (let* ((tq (geiser-con--connection-tq c))
182 (proc (geiser-con--connection-process c))
183 (proc-filter (geiser-con--connection-filter c)))
184 (unless no-wait
185 (while (and (not (tq-queue-empty tq))
186 (accept-process-output proc 0.1))))
187 (set-process-filter proc proc-filter)
188 (setcar c nil))))
190 (defun geiser-con--connection-activate (c)
191 (when (not (car c))
192 (let* ((tq (geiser-con--connection-tq c))
193 (proc (geiser-con--connection-process c))
194 (tq-filter (geiser-con--connection-tq-filter c)))
195 (while (accept-process-output proc 0.01))
196 (set-process-filter proc tq-filter)
197 (setcar c t))))
200 ;;; Requests handling:
202 (defun geiser-con--req-form (req answer)
203 (let ((con (geiser-con--request-connection req)))
204 (if (geiser-con--has-entered-debugger con answer)
205 `((error (key . geiser-debugger))
206 (output . ,answer))
207 (condition-case err
208 (let ((start (string-match "((\\(?:result)?\\|error\\) " answer)))
209 (or (and start (car (read-from-string answer start)))
210 `((error (key . retort-syntax)) (output . ,answer))))
211 (error `((error (key . geiser-con-error))
212 (output . ,(format "%s\n(%s)"
213 answer (error-message-string err)))))))))
215 (defun geiser-con--process-completed-request (req answer)
216 (let ((cont (geiser-con--request-continuation req))
217 (id (geiser-con--request-id req))
218 (rstr (geiser-con--request-string req))
219 (form (geiser-con--req-form req answer))
220 (buffer (or (geiser-con--request-buffer req) (current-buffer)))
221 (con (geiser-con--request-connection req)))
222 (if (not cont)
223 (geiser-log--warn "<%s> Dropping result for request %S: %s"
224 id rstr form)
225 (condition-case cerr
226 (with-current-buffer buffer
227 (funcall cont form)
228 (geiser-log--info "<%s>: processed" id))
229 (error (geiser-log--error
230 "<%s>: continuation failed %S \n\t%s" id rstr cerr))))
231 (geiser-con--connection-completed con req)))
233 (defun geiser-con--connection-add-request (c r)
234 (let ((rstr (geiser-con--request-string r)))
235 (geiser-log--info "REQUEST: <%s>: %s"
236 (geiser-con--request-id r)
237 rstr)
238 (geiser-con--connection-activate c)
239 (tq-enqueue (geiser-con--connection-tq c)
240 (concat rstr "\n")
241 (geiser-con--connection-eot c)
243 'geiser-con--process-completed-request
244 t)))
247 ;;; Message sending interface:
249 (defun geiser-con--send-string (con str cont &optional sbuf)
250 (let ((req (geiser-con--make-request con str cont sbuf)))
251 (geiser-con--connection-add-request con req)
252 req))
254 (defvar geiser-connection-timeout 30000
255 "Time limit, in msecs, blocking on synchronous evaluation requests")
257 (defun geiser-con--send-string/wait (con str cont &optional timeout sbuf)
258 (save-current-buffer
259 (let ((proc (and con (geiser-con--connection-process con))))
260 (unless proc (error "Geiser connection not active"))
261 (let* ((req (geiser-con--send-string con str cont sbuf))
262 (id (geiser-con--request-id req))
263 (timeout (/ (or timeout geiser-connection-timeout) 1000.0)))
264 (with-timeout (timeout (geiser-con--request-deactivate req))
265 (condition-case nil
266 (while (and (geiser-con--connection-process con)
267 (not (geiser-con--connection-completed-p con id)))
268 (accept-process-output proc (/ timeout 10)))
269 (error (geiser-con--request-deactivate req))))))))
272 (provide 'geiser-connection)