1 ;;; fuel-connection.el -- asynchronous comms with the fuel listener
3 ;; Copyright (C) 2008, 2009 Jose Antonio Ortega Ruiz
4 ;; See http://factorcode.org/license.txt for BSD license.
6 ;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
7 ;; Keywords: languages, fuel, factor
8 ;; Start date: Thu Dec 11, 2008 03:10
12 ;; Handling communications via a comint buffer running a factor
24 ;;; Default connection:
26 (make-variable-buffer-local
27 (defvar fuel-con--connection nil
))
29 (defun fuel-con--get-connection (buffer/proc
)
30 (if (processp buffer
/proc
)
31 (fuel-con--get-connection (process-buffer buffer
/proc
))
32 (with-current-buffer buffer
/proc fuel-con--connection
)))
35 ;;; Request and connection datatypes:
37 (defun fuel-con--connection-queue-request (c r
)
38 (let ((reqs (assoc :requests c
)))
39 (setcdr reqs
(append (cdr reqs
) (list r
)))))
41 (defun fuel-con--make-request (str cont
&optional sender-buffer
)
42 (list :fuel-connection-request
45 (cons :continuation cont
)
46 (cons :buffer
(or sender-buffer
(current-buffer)))))
48 (defsubst fuel-con--request-p
(req)
49 (and (listp req
) (eq (car req
) :fuel-connection-request
)))
51 (defsubst fuel-con--request-id
(req)
52 (cdr (assoc :id req
)))
54 (defsubst fuel-con--request-string
(req)
55 (cdr (assoc :string req
)))
57 (defsubst fuel-con--request-continuation
(req)
58 (cdr (assoc :continuation req
)))
60 (defsubst fuel-con--request-buffer
(req)
61 (cdr (assoc :buffer req
)))
63 (defsubst fuel-con--request-deactivate
(req)
64 (setcdr (assoc :continuation req
) nil
))
66 (defsubst fuel-con--request-deactivated-p
(req)
67 (null (cdr (assoc :continuation req
))))
69 (defsubst fuel-con--make-connection
(buffer)
70 (list :fuel-connection
71 (cons :requests
(list))
73 (cons :completed
(make-hash-table :weakness
'value
))
77 (defsubst fuel-con--connection-p
(c)
78 (and (listp c
) (eq (car c
) :fuel-connection
)))
80 (defsubst fuel-con--connection-requests
(c)
81 (cdr (assoc :requests c
)))
83 (defsubst fuel-con--connection-current-request
(c)
84 (cdr (assoc :current c
)))
86 (defun fuel-con--connection-clean-current-request (c)
87 (let* ((cell (assoc :current c
))
90 (puthash (fuel-con--request-id req
) req
(cdr (assoc :completed c
)))
93 (defsubst fuel-con--connection-completed-p
(c id
)
94 (gethash id
(cdr (assoc :completed c
))))
96 (defsubst fuel-con--connection-buffer
(c)
97 (cdr (assoc :buffer c
)))
99 (defun fuel-con--connection-pop-request (c)
100 (let ((reqs (assoc :requests c
))
101 (current (assoc :current c
)))
102 (setcdr current
(prog1 (cadr reqs
) (setcdr reqs
(cddr reqs
))))
103 (if (and (cdr current
)
104 (fuel-con--request-deactivated-p (cdr current
)))
105 (fuel-con--connection-pop-request c
)
108 (defun fuel-con--connection-start-timer (c)
109 (let ((cell (assoc :timer c
)))
110 (when (cdr cell
) (cancel-timer (cdr cell
)))
111 (setcdr cell
(run-at-time t
0.5 'fuel-con--process-next c
))))
113 (defun fuel-con--connection-cancel-timer (c)
114 (let ((cell (assoc :timer c
)))
115 (when (cdr cell
) (cancel-timer (cdr cell
)))))
118 ;;; Connection setup:
120 (defun fuel-con--cleanup-connection (c)
121 (fuel-con--connection-cancel-timer c
))
123 (defun fuel-con--setup-connection (buffer)
125 (fuel-con--cleanup-connection fuel-con--connection
)
126 (setq fuel-con--connection nil
)
127 (let ((conn (fuel-con--make-connection buffer
)))
128 (fuel-con--setup-comint)
129 (fuel-con--establish-connection conn buffer
)))
131 (defconst fuel-con--prompt-regex
"( .+ ) ")
132 (defconst fuel-con--eot-marker
"<~FUEL~>")
133 (defconst fuel-con--init-stanza
"USE: fuel fuel-retort")
135 (defconst fuel-con--comint-finished-regex-connected
136 (format "^%s$" fuel-con--eot-marker
))
138 (defvar fuel-con--comint-finished-regex fuel-con--prompt-regex
)
140 (defun fuel-con--setup-comint ()
141 (set (make-local-variable 'comint-redirect-insert-matching-regexp
) t
)
142 (add-hook 'comint-redirect-filter-functions
143 'fuel-con--comint-preoutput-filter nil t
)
144 (add-hook 'comint-redirect-hook
145 'fuel-con--comint-redirect-hook nil t
))
147 (defadvice comint-redirect-setup
(after fuel-con--advice activate
)
148 (setq comint-redirect-finished-regexp fuel-con--comint-finished-regex
))
150 (defun fuel-con--comint-preoutput-filter (str)
151 (when (string-match fuel-con--comint-finished-regex str
)
152 (setq comint-redirect-finished-regexp fuel-con--prompt-regex
))
155 (defun fuel-con--establish-connection (conn buffer
)
156 (with-current-buffer (fuel-con--comint-buffer) (erase-buffer))
157 (with-current-buffer buffer
158 (setq fuel-con--connection conn
)
159 (setq fuel-con--comint-finished-regex fuel-con--prompt-regex
)
160 (fuel-con--send-string/wait buffer
161 fuel-con--init-stanza
162 'fuel-con--establish-connection-cont
166 (defun fuel-con--establish-connection-cont (ignore)
167 (let ((str (with-current-buffer (fuel-con--comint-buffer) (buffer-string))))
168 (if (string-match fuel-con--eot-marker str
)
170 (setq fuel-con--comint-finished-regex
171 fuel-con--comint-finished-regex-connected
)
172 (fuel-con--connection-start-timer conn
)
173 (message "FUEL listener up and running!"))
174 (fuel-con--connection-clean-current-request fuel-con--connection
)
175 (setq fuel-con--connection nil
)
176 (message "An error occurred initialising FUEL's Factor library!")
177 (pop-to-buffer (fuel-con--comint-buffer)))))
180 ;;; Requests handling:
182 (defsubst fuel-con--comint-buffer
()
183 (get-buffer-create " *fuel connection retort*"))
185 (defun fuel-con--comint-buffer-form ()
186 (with-current-buffer (fuel-con--comint-buffer)
187 (goto-char (point-min))
189 (let ((form (read (current-buffer))))
190 (if (listp form
) form
191 (list 'fuel-con-error
(buffer-string))))
192 (error (list 'fuel-con-error
(buffer-string))))))
194 (defun fuel-con--process-next (con)
195 (when (not (fuel-con--connection-current-request con
))
196 (let* ((buffer (fuel-con--connection-buffer con
))
197 (req (fuel-con--connection-pop-request con
))
198 (str (and req
(fuel-con--request-string req
)))
199 (cbuf (with-current-buffer (fuel-con--comint-buffer)
202 (if (not (buffer-live-p buffer
))
203 (fuel-con--connection-cancel-timer con
)
204 (when (and buffer req str
)
206 (fuel-log--info "<%s>: %s" (fuel-con--request-id req
) str
)
207 (comint-redirect-send-command (format "%s" str
) cbuf nil t
))))))
209 (defun fuel-con--process-completed-request (req)
210 (let ((cont (fuel-con--request-continuation req
))
211 (id (fuel-con--request-id req
))
212 (rstr (fuel-con--request-string req
))
213 (buffer (fuel-con--request-buffer req
)))
215 (fuel-log--warn "<%s> Droping result for request %S (%s)"
218 (with-current-buffer (or buffer
(current-buffer))
219 (funcall cont
(fuel-con--comint-buffer-form))
220 (fuel-log--info "<%s>: processed" id
))
221 (error (fuel-log--error
222 "<%s>: continuation failed %S \n\t%s" id rstr cerr
))))))
224 (defun fuel-con--comint-redirect-hook ()
225 (if (not fuel-con--connection
)
226 (fuel-log--error "No connection in buffer")
227 (let ((req (fuel-con--connection-current-request fuel-con--connection
)))
228 (if (not req
) (fuel-log--error "No current request")
229 (fuel-con--process-completed-request req
)
230 (fuel-con--connection-clean-current-request fuel-con--connection
)))))
233 ;;; Message sending interface:
235 (defconst fuel-con--error-message
"FUEL connection not active")
237 (defun fuel-con--send-string (buffer/proc str cont
&optional sender-buffer
)
239 (let ((con (fuel-con--get-connection buffer
/proc
)))
240 (unless con
(error fuel-con--error-message
))
241 (let ((req (fuel-con--make-request str cont sender-buffer
)))
242 (fuel-con--connection-queue-request con req
)
243 (fuel-con--process-next con
)
246 (defvar fuel-connection-timeout
30000
247 "Time limit, in msecs, blocking on synchronous evaluation requests")
249 (defun fuel-con--send-string/wait
(buffer/proc str cont
&optional timeout sbuf
)
251 (let ((con (fuel-con--get-connection buffer
/proc
)))
252 (unless con
(error fuel-con--error-message
))
253 (let* ((req (fuel-con--send-string buffer
/proc str cont sbuf
))
254 (id (and req
(fuel-con--request-id req
)))
255 (time (or timeout fuel-connection-timeout
))
257 (waitsecs (/ step
1000.0)))
260 (while (and (> time
0)
261 (not (fuel-con--connection-completed-p con id
)))
262 (accept-process-output nil waitsecs
)
263 (setq time
(- time step
)))
264 (error (setq time
0)))
266 (fuel-con--request-deactivate req
)
270 (provide 'fuel-connection
)
271 ;;; fuel-connection.el ends here