PageRenderTime 61ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/distel/elisp/erl.el

https://github.com/babo/jungerl
Emacs Lisp | 640 lines | 485 code | 102 blank | 53 comment | 19 complexity | 0b38b70c8e1931978ba508f3ee906e82 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
  1. ;;; erl.el --- Erlang-style process runtime system.
  2. ;;; Commentary:
  3. ;;
  4. ;; This module provides an Erlang-like runtime system in
  5. ;; Emacs. Processes are Emacs buffers with local variables containing
  6. ;; the pid, mailbox, etc.
  7. ;;
  8. ;; When a process is spawned it gets assigned a pid and a new buffer,
  9. ;; and its initial function is called in that buffer. This function
  10. ;; can do some initial processing, and then call (erl-continue K),
  11. ;; where K is a continuation function to called the next time the
  12. ;; process is scheduled. Usually the process won't be scheduled until
  13. ;; it receives a new message, which it can then retreive from its
  14. ;; mailbox. If the process returns without setting a new continuation,
  15. ;; it terminates with 'normal' status.
  16. (eval-when-compile (require 'cl))
  17. (provide 'erl) ; avoid recursive require
  18. (require 'derl)
  19. (require 'erl-service)
  20. (require 'patmatch)
  21. ;; Process ID structure.
  22. ;;
  23. ;; Exactly matches the [ERL-TAG erl-pid NODE ID SERIAL CREATION] vector used
  24. ;; in the `erlext' mapping, so don't change it!
  25. (defstruct (erl-pid
  26. (:type vector)
  27. :named
  28. (:initial-offset 1) ; make room for erl-tag (TYPE)
  29. (:constructor nil) ; no default constructor
  30. (:constructor %make-erl-local-pid (&optional (id (incf erl-pid-counter))
  31. (node erl-node-name)
  32. (serial 0)
  33. (creation 0))))
  34. node id serial creation)
  35. (defun make-erl-local-pid (&optional id)
  36. "Make a node-local pid."
  37. (let ((pid (if id
  38. (%make-erl-local-pid id)
  39. (%make-erl-local-pid))))
  40. ;; Tag the first element of the pid
  41. (setf (elt pid 0) erl-tag)
  42. pid))
  43. ;; Global book keeping state
  44. (defvar erl-node-name nil ; initialised below
  45. "Node name for Emacs.")
  46. (defun erl-determine-hostname ()
  47. "Figure out the short-names hostname."
  48. (let ((fqdn (system-name)))
  49. (if (string-match "[^\\.]+" fqdn)
  50. (match-string 0 fqdn)
  51. (error "erl: Can't determine hostname."))))
  52. (when (null erl-node-name)
  53. (setq erl-node-name
  54. (intern (format "distel_%S@%s" (emacs-pid) (erl-determine-hostname)))))
  55. (defconst erl-null-pid (make-erl-local-pid 0)
  56. "\"Null process\", the /dev/null of erl processes.
  57. Any messages sent to this process are quietly discarded. When code
  58. isn't running in the buffer of a particular process, it's running as
  59. the null process.")
  60. (defvar erl-pid-counter 0
  61. "Counter for PIDs.")
  62. (defvar erl-process-buffer-alist nil
  63. "Automatically-maintained association list of (PID-ID . BUFFER)
  64. mappings for local processes.")
  65. (defvar erl-schedulable-processes nil
  66. "List of processes which can be scheduled to run.")
  67. (defvar erl-in-scheduler-loop nil
  68. "True when the scheduler loop is on the call stack, i.e. when
  69. schedulable processes are guaranteed to be executed before control is
  70. passed back to Emacs.")
  71. (defvar erl-default-group-leader erl-null-pid
  72. ;; Initialized to a real process further down
  73. "Default group_leader for new processes.
  74. Processes spawned by other processes will inherit their GL, but
  75. \"brand new\" ones will use this.")
  76. (defvar erl-popup-on-output t
  77. "Popup *erl-output* when new output arrives.")
  78. (defvar erl-stop-on-error nil
  79. "*When non-nil, prevents the scheduler from catching Elisp errors.
  80. Such errors are allowed to propagate, so you can debug them using
  81. `debug-on-error'.
  82. Because this interrupts the scheduling of processes, you must use the
  83. command `erl-schedule' to continue.")
  84. (defvar erl-ref-counter 0
  85. "Unique reference id counter.")
  86. (defvar erl-nodes nil
  87. "List of nodes that we are currently connected to.")
  88. ;; Process-local variables
  89. (eval-when-compile
  90. (defmacro defprocvar (symbol &optional initvalue docstring)
  91. "Define SYMBOL as a buffer-local process variable."
  92. `(prog1 (defvar ,symbol ,initvalue ,docstring)
  93. (make-variable-buffer-local ',symbol)
  94. ;; stop major modes' `kill-all-local-variables' from rubbing out
  95. ;; the process state
  96. (put ',symbol 'permanent-local t))))
  97. ;; FIXME - what's the right incantation to have defprocvar fontified
  98. ;; as a keyword?
  99. (defprocvar erl-self erl-null-pid
  100. "Current process' pid.
  101. Always bound in a process' buffer, but in
  102. other buffers defaults to `erl-null-pid'.")
  103. (defprocvar erl-mailbox nil
  104. "Process mailbox.
  105. Contains messages for the process, which it's supposed to process and
  106. remove. Messages are ordered from oldest to newest.")
  107. (defprocvar erl-group-leader nil
  108. "Group leader process.")
  109. (defprocvar erl-continuation nil
  110. "Function for the scheduler to call to run the process.")
  111. (defprocvar erl-continuation-args nil
  112. "Arguments for continuation function.")
  113. (defprocvar erl-links nil
  114. "Process links.")
  115. (defprocvar erl-trap-exit nil
  116. "True when trapping exits from linked processes.")
  117. (defprocvar erl-exit-reason nil
  118. "Exit reason, or nil if the process is alive.")
  119. (defprocvar erl-reductions 0
  120. "Number of \"reductions\".
  121. Actually the number of times the process has been run invoked,
  122. typically by the scheduler.")
  123. (defprocvar erl-process-name nil
  124. "Name of the process, a string.
  125. This can be set and then later used to help with debugging.
  126. An example name would be \"Process list for z@cockatoo\"")
  127. (defmacro with-erl-process (pid &rest body)
  128. "Execute BODY in PID's buffer. This is a full context-switch."
  129. `(with-current-buffer (erl-pid->buffer ,pid)
  130. ,@body))
  131. ;; Bindings capture helpers
  132. (defmacro capture-bindings (&rest vars)
  133. "Create a data structure representing the bidings of VARS.
  134. These bindings can be restored by `with-bindings' or
  135. `call-with-bindings'."
  136. `(list ',vars (list ,@vars)))
  137. (defmacro with-bindings (bindings &rest body)
  138. "Run BODY with BINDINGS restored.
  139. BINDINGS is created by `capture-bindings'"
  140. `(call-with-bindings ,bindings (lambda () ,@body)))
  141. (defun call-with-bindings (bindings fn)
  142. "Call FN with BINDINGS restored.
  143. BINDINGS is created by `capture-bindings'"
  144. (let ((vars (car bindings))
  145. (vals (cadr bindings)))
  146. (eval `(apply (lambda ,vars (funcall ,fn)) ',vals))))
  147. ;; Process API functions
  148. (defmacro erl-spawn-async (&rest body)
  149. "Spawn a new erl process and have it execute BODY.
  150. Since the process may be scheduled for later, BODY is not guaranteed
  151. to run in the current dynamic environment; see `erl-spawn' for an
  152. alternative."
  153. `(erl-spawn-fun (lambda () ,@body)))
  154. (defmacro erl-spawn (&rest body)
  155. "Spawn a new process and run it in the current dynamic environment."
  156. `(erl-spawn-fun (lambda () ,@body) t))
  157. (defmacro erl-spawn-link-async (&rest body)
  158. "Same as `erl-spawn-async', but links with the process."
  159. `(erl-spawn-fun (lambda () ,@body) nil t))
  160. (defmacro erl-spawn-link (&rest body)
  161. "Same as `erl-spawn', but links with the process."
  162. `(erl-spawn-fun (lambda () ,@body) t t))
  163. (defun erl-link (pid)
  164. "Link the current process with PID."
  165. (unless (equal pid erl-self)
  166. (erl-add-link erl-self pid)
  167. (erl-add-link pid erl-self)))
  168. (defun erl-unlink (pid)
  169. "Unlink the current process from PID."
  170. (erl-remove-link erl-self pid)
  171. (erl-remove-link pid erl-self))
  172. (defun erl-send (who message)
  173. "Send the term MESSAGE to the process WHO.
  174. WHO can be a pid, a registered name (symbol), or a tuple of
  175. \[REGISTERED-NAME NODE]."
  176. (cond ((erl-null-pid-p who)
  177. (erl-lose-msg message))
  178. ((erl-local-pid-p who)
  179. (when (erl-local-pid-alive-p who)
  180. (erl-deliver-message who message)))
  181. ((erl-remote-pid-p who)
  182. (erl-dist-send who message))
  183. ((symbolp who)
  184. (let ((proc (erl-whereis who)))
  185. (if proc
  186. (erl-send proc message)
  187. (erl-exit (tuple 'badarg (tuple 'not-registered who))))))
  188. ((tuplep who) ; [tuple NAME NODE]
  189. (erl-dist-reg-send (tuple-elt who 2) (tuple-elt who 1) message))
  190. (t
  191. (error "Bad pid: %S" who))))
  192. (defun erl-exit (why &optional who)
  193. "Exit the current process.
  194. Like the erlang BIF exit/1."
  195. (if who
  196. (erl-send-exit erl-self who why)
  197. (signal 'erl-exit-signal (list why))))
  198. (defun erl-exit/2 (who why)
  199. "Exit a process.
  200. Like the erlang BIF exit/2."
  201. (erl-exit why who))
  202. (defun erl-continue (k &rest args)
  203. "Yield control and arrange for K to be called with ARGS the next
  204. time this process is scheduled. Note that the process is not
  205. \"scheduled out\" automatically, and the caller should return
  206. normally."
  207. (setq erl-continuation k)
  208. (setq erl-continuation-args args))
  209. (defun erl-reschedule ()
  210. "Return immedaitely (via `throw') to the scheduler.
  211. Also makes the current process immediately reschedulable."
  212. ;; the scheduler loop will catch this and know what to do
  213. (throw 'schedule-out 'reschedule))
  214. (defun erl-idle ()
  215. (erl-receive ()
  216. ()))
  217. (defun erl-make-ref ()
  218. "Make a unique reference object."
  219. (vector erl-tag 'erl-ref erl-node-name (incf erl-ref-counter) 0))
  220. ;; receive
  221. (defmacro erl-receive (vars clauses &rest after)
  222. "Receive a message, matched by pattern.
  223. If the mailbox contains a matching message, the pattern's body is
  224. executed immediately. Otherwise, `erl-continue' is used to make the
  225. process continue matching when new messages arrive. The crucial
  226. difference from Erlang's receive is that erl-receive returns
  227. immediately when nothing is matched, but will automatically resume
  228. after a new message arrives and the process is rescheduled.
  229. Since the the process may return and be rescheduled before the
  230. matching message is received, the clause's body might not be executed
  231. in the original dynamic environment. Consequently, any local variable
  232. bindings that need to be preserved should be named in VARS.
  233. After a pattern has been matched and executed, the AFTER forms are
  234. then executed.
  235. The overall syntax for receive is:
  236. (erl-receive (VAR-NAME ...)
  237. ((PATTERN . BODY)
  238. ...)
  239. . AFTER)
  240. The pattern syntax is the same as `pmatch'."
  241. `(erl-start-receive (capture-bindings ,@vars)
  242. ,(mcase-parse-clauses clauses)
  243. (lambda () ,@after)))
  244. (defun erl-start-receive (bs clauses after)
  245. ;; Setup a continuation and immediately return to the scheduler
  246. ;; loop, which will call us back.
  247. (when (equal erl-self erl-null-pid)
  248. (error "No process context for erl-receive"))
  249. (erl-continue #'erl-receive* bs clauses after)
  250. (erl-reschedule))
  251. (defun erl-receive* (bs clauses after)
  252. (erl-receive-loop bs clauses after erl-mailbox))
  253. (defun erl-receive-loop (bs clauses after msgs &optional acc)
  254. (if (null msgs)
  255. (erl-continue #'erl-receive* bs clauses after)
  256. (let ((action
  257. ;; We restore the bindings incase they are referred to in patterns
  258. (with-bindings bs
  259. (mcase-choose (car msgs) clauses))))
  260. (if (null action)
  261. (erl-receive-loop bs clauses after (cdr msgs) (cons (car msgs) acc))
  262. (setq erl-mailbox (append (reverse acc) (cdr msgs)))
  263. (with-bindings bs
  264. (funcall action)
  265. (funcall after))))))
  266. (defun erl-register (name &optional process)
  267. "Register PROCESS with NAME."
  268. (if (get-buffer (regname-to-bufname name))
  269. (erl-exit (tuple 'badarg (tuple 'already-registered name)))
  270. (with-erl-process (or process erl-self)
  271. (rename-buffer (regname-to-bufname name)))))
  272. (defun erl-whereis (name)
  273. "Get the PID of the process registered with NAME, or nil if the name
  274. is unregistered."
  275. (let ((buf (get-buffer (regname-to-bufname name))))
  276. (if buf
  277. (with-current-buffer buf erl-self))))
  278. (defun regname-to-bufname (name)
  279. (format "*reg %S*" name))
  280. (defalias 'erl-term-to-binary #'erlext-term-to-binary)
  281. (defalias 'erl-binary-to-term #'erlext-binary-to-term)
  282. (defun erl-group-leader ()
  283. (or erl-group-leader erl-default-group-leader))
  284. ;; Guts
  285. ;;
  286. ;; The scheduler works without ever being explicitly called. It runs
  287. ;; when a message arrives from a remote node or when a new process is
  288. ;; spawned, and it continues to schedule processes until none are
  289. ;; runnable.
  290. ;;
  291. ;; The %ugly-variable-names are to avoid shadowing any existing
  292. ;; dynamic bindings between the caller and the process being invoked -
  293. ;; that's a bastard to debug.
  294. (put 'erl-exit-signal
  295. 'error-conditions
  296. '(error erl-exit-signal))
  297. (defun erl-spawn-fun (%init-function &optional %run-now-p %link)
  298. "Spawn a new erl process to call INIT-FUNCTION.
  299. If RUN-NOW-P is true, the process is called immediately with the
  300. current dynamic environment/bindings. Otherwise, the process is made
  301. schedulable. The scheduler loop is entered if we aren't being called
  302. by it already.
  303. If LINK is true, the process is linked before being run."
  304. (let* ((%pid (make-erl-local-pid))
  305. (%buffer (get-buffer-create (erl-pid-buffer-name %pid)))
  306. (%gl (or erl-group-leader erl-default-group-leader)))
  307. (with-current-buffer %buffer
  308. (setq erl-self %pid)
  309. (setq erl-group-leader %gl)
  310. (setq erl-continuation %init-function)
  311. (setq erl-continuation-args nil)
  312. (erl-enroll-process))
  313. (when %link (erl-link %pid))
  314. (if %run-now-p
  315. (let ((erl-in-scheduler-loop t))
  316. (erl-run-process %pid))
  317. (erl-make-schedulable %pid))
  318. (erl-maybe-schedule)
  319. %pid))
  320. (defun erl-deliver-message (pid message)
  321. "Deliver MESSAGE to the mailbox of the local process PID.
  322. Invokes the scheduler if necessary."
  323. (with-erl-process pid
  324. (setq erl-mailbox (append erl-mailbox (list message))))
  325. (erl-make-schedulable pid)
  326. (erl-maybe-schedule))
  327. (defun erl-schedule ()
  328. "Enter scheduler loop until no process is runnable."
  329. (interactive)
  330. (let ((erl-in-scheduler-loop t))
  331. (while (erl-schedule-once)))
  332. ;; post-condition
  333. (assert (null erl-schedulable-processes)))
  334. (defun erl-schedule-once ()
  335. "Schedule the next process to run. Returns true if a process was scheduled."
  336. (when erl-schedulable-processes
  337. (erl-schedule-process (pop erl-schedulable-processes))
  338. t))
  339. (defun erl-maybe-schedule ()
  340. "Schedule processes, unless the scheduler is already running."
  341. (unless erl-in-scheduler-loop
  342. (erl-schedule)))
  343. (defun erl-schedule-process (%pid)
  344. (cond ((not (erl-local-pid-alive-p %pid))
  345. (message "STRANGE: %S scheduled but dead; removing" %pid)
  346. (erl-make-unschedulable %pid))
  347. ((with-erl-process %pid (null erl-continuation))
  348. (message "STRANGE: %S is a zombie! killing" %pid)
  349. (with-erl-process %pid (erl-terminate 'zombie)))
  350. (t
  351. (while (eq 'reschedule (erl-run-process %pid))))))
  352. (defun erl-run-process (%pid)
  353. "Run a process.
  354. Calls the current continuation from within the process' buffer."
  355. (with-erl-process %pid
  356. (incf erl-reductions)
  357. ;; The %ugly-names are to avoid shadowing the caller's dynamic
  358. ;; bindings.
  359. (let ((%k erl-continuation)
  360. (%kargs erl-continuation-args)
  361. (%buffer (current-buffer)))
  362. (setq erl-continuation nil)
  363. (setq erl-continuation-args nil)
  364. (if erl-stop-on-error
  365. (erl-invoke %k %kargs)
  366. (condition-case data
  367. (erl-invoke %k %kargs)
  368. (error (erl-terminate `[emacs-error ,(format "%S" data)])))))))
  369. (defun erl-invoke (%k %kargs)
  370. (condition-case data
  371. (prog1 (catch 'schedule-out
  372. (prog1 nil (save-current-buffer (apply %k %kargs))))
  373. (unless erl-continuation
  374. (erl-terminate 'normal)))
  375. (erl-exit-signal (erl-terminate (cadr data)))))
  376. (defun erl-make-schedulable (pid)
  377. "Add PID to the list of runnable processes, so that it will execute
  378. during the next `erl-schedule'."
  379. (unless (member pid erl-schedulable-processes)
  380. (setq erl-schedulable-processes
  381. (append erl-schedulable-processes (list pid)))))
  382. (defun erl-make-unschedulable (pid)
  383. "Remove PID from the list of schedulable processes."
  384. (setq erl-schedulable-processes
  385. (remove pid erl-schedulable-processes)))
  386. (defun erl-terminate (why)
  387. "Exit the current process."
  388. (unless (eq why 'normal)
  389. (message "EXIT: %s %S %s" (erl-pid-to-string erl-self) why
  390. (if erl-process-name
  391. (concat "\n Process name: " erl-process-name)
  392. "")))
  393. (setq erl-exit-reason why)
  394. (erl-make-unschedulable erl-self)
  395. (kill-buffer (erl-pid->buffer erl-self)))
  396. (defun erl-add-link (from to)
  397. "Unidirectionally add a link."
  398. (unless (erl-null-pid-p from)
  399. (with-erl-process from
  400. (add-to-list 'erl-links to))))
  401. (defun erl-remove-link (from to)
  402. "Unidirectionally remove a link."
  403. (unless (erl-null-pid-p from)
  404. (with-erl-process from
  405. (setq erl-links (remove to erl-links)))))
  406. (defun erl-set-name (fmt &rest args)
  407. "Set `erl-process-name' to (apply 'format (FMT . ARGS))."
  408. (setq erl-process-name (apply 'format (cons fmt args))))
  409. ;; PID utilities
  410. (defun erl-pid-buffer-name (pid)
  411. (unless (equal (erl-pid-node pid) erl-node-name)
  412. (error "Not a local pid: %S" pid))
  413. (format "*pid <%S.%S.%S>*"
  414. 0
  415. (erl-pid-id pid)
  416. (erl-pid-serial pid)))
  417. (defun erl-pid->buffer (pid)
  418. "Get PID's buffer."
  419. (or (cdr (assoc (erl-pid-id pid) erl-process-buffer-alist))
  420. (error "No buffer for pid %S" pid)))
  421. (defun erl-null-pid-p (p)
  422. (equal p erl-null-pid))
  423. (defun erl-local-pid-alive-p (pid)
  424. "Is PID a live local process?"
  425. (when (erl-local-pid-p pid)
  426. (let ((buffer (cdr (assoc (erl-pid-id pid) erl-process-buffer-alist))))
  427. (and buffer
  428. (buffer-live-p buffer)
  429. (with-erl-process pid
  430. (null erl-exit-reason))))))
  431. (defun erl-local-pid-p (x)
  432. "True iff X is the pid of a local process."
  433. (and (erl-pid-p x)
  434. (equal (erl-pid-node x) erl-node-name)))
  435. (defun erl-remote-pid-p (x)
  436. "True iff X is the pid of a remote process."
  437. (and (erl-pid-p x)
  438. (not (erl-local-pid-p x))))
  439. (defun erl-pid-to-string (pid)
  440. ;; FIXME: number nodes
  441. (let ((n (if (eq (erl-pid-node pid) erl-node-name)
  442. "0" ; local
  443. "?")))
  444. (format "<%s.%S.%S>" n (erl-pid-id pid) (erl-pid-serial pid))))
  445. (defun erl-lose-msg (msg)
  446. "Log and discard a message sent to the null process."
  447. (with-current-buffer (get-buffer-create "*erl-lost-msgs*")
  448. (goto-char (point-max))
  449. (insert (format "%S\n" msg))))
  450. (defun erl-enroll-process ()
  451. "Setup pid->buffer mapping state for the current process."
  452. (push (cons (erl-pid-id erl-self) (current-buffer))
  453. erl-process-buffer-alist)
  454. (make-local-variable 'kill-buffer-hook)
  455. (put 'kill-buffer-hook 'permanent-local t)
  456. (add-hook 'kill-buffer-hook 'erl-unenroll-process)
  457. (add-hook 'kill-buffer-hook 'erl-propagate-exit))
  458. (defun erl-remove-if (predicate list)
  459. "Return a copy of LIST with all items satisfying PREDICATE removed."
  460. (let (out)
  461. (while list
  462. (unless (funcall predicate (car list))
  463. (push (car list) out))
  464. (setq list (cdr list)))
  465. (nreverse out)))
  466. (defun erl-unenroll-process ()
  467. (setq erl-process-buffer-alist
  468. (erl-remove-if #'(lambda (x) (eq (erl-pid-id erl-self) (car x)))
  469. erl-process-buffer-alist)))
  470. (defun erl-propagate-exit ()
  471. (when (null erl-exit-reason)
  472. (setq erl-exit-reason 'killed))
  473. (unless (eq erl-exit-reason 'normal)
  474. (mapc #'(lambda (proc) (erl-send-exit erl-self proc erl-exit-reason))
  475. erl-links)))
  476. (defun erl-send-exit (from to rsn)
  477. (cond ((erl-local-pid-alive-p to)
  478. (erl-deliver-exit from to rsn))
  479. ((erl-remote-pid-p to)
  480. (erl-dist-exit from to rsn))))
  481. (defun erl-deliver-exit (from to rsn)
  482. (with-erl-process to
  483. (cond (erl-exit-reason ; already terminated?
  484. t)
  485. (erl-trap-exit
  486. (erl-deliver-message to (tuple 'EXIT from rsn))
  487. (erl-unlink from))
  488. (t
  489. (erl-terminate rsn)))))
  490. (defun erl-nodedown-exit (local remote)
  491. "Send an exit to LOCAL from REMOTE caused by a communications failure."
  492. (when (erl-local-pid-alive-p local)
  493. (with-erl-process local
  494. (setq erl-links (remove remote erl-links))
  495. (erl-deliver-exit remote local 'noconnection))))
  496. (defun impossible (&optional reason)
  497. "Raise an error because something \"impossible\" has happened."
  498. (if reason
  499. (error "Impossible: %s" reason)
  500. (error "The impossible has occured")))
  501. (defun nyi ()
  502. (error "Not yet implemented!"))
  503. ;; Initialisation
  504. (defun erl-nodeup (node proc)
  505. (pushnew node erl-nodes)
  506. (message "nodeup: %S" node))
  507. (defun erl-nodedown (node)
  508. (setq erl-nodes (remove node erl-nodes))
  509. (message "nodedown: %S" node))
  510. ;; These hooks are defined in derl.el
  511. (add-hook 'erl-nodeup-hook 'erl-nodeup)
  512. (add-hook 'erl-nodedown-hook 'erl-nodedown)
  513. ;; Emacs indentation
  514. (put 'with-erl-process 'lisp-indent-function 1)
  515. (put 'erl-spawn 'lisp-indent-function 'defun)
  516. (put 'erl-spawn-async 'lisp-indent-function 'defun)
  517. (put 'erl-receive 'lisp-indent-function 2)
  518. (put 'with-bindings 'lisp-indent-function 1)
  519. ;; Initial processes
  520. (defun &erl-group-leader-loop ()
  521. (erl-receive ()
  522. ((['put_chars s]
  523. (condition-case err
  524. (save-excursion
  525. (with-current-buffer (get-buffer-create "*erl-output*")
  526. (save-selected-window
  527. (if erl-popup-on-output
  528. (select-window (or (get-buffer-window (current-buffer))
  529. (display-buffer (current-buffer)))))
  530. (goto-char (point-max))
  531. (insert s))))
  532. (error (message "Error in group leader: %S" err)))))
  533. (&erl-group-leader-loop)))
  534. (when (null erl-group-leader)
  535. (setq erl-default-group-leader
  536. (erl-spawn
  537. (erl-register 'group-leader)
  538. (&erl-group-leader-loop))))