From f2bf4b2cda05fe8c32ee74ce3b5cce743d81de61 Mon Sep 17 00:00:00 2001 From: Jose Antonio Ortega Ruiz Date: Tue, 9 Nov 2010 21:35:50 +0100 Subject: Racket: remote REPLs --- .gitignore | 1 + Makefile.am | 2 +- bin/Makefile.am | 15 +++++++++++ bin/geiser-racket.sh | 19 +++++++++++++ configure.ac | 1 + doc/repl.texi | 60 ++++++++++++++++++++++++++++------------- elisp/geiser-connection.el | 57 +++++++++++++++++---------------------- elisp/geiser-racket.el | 12 +++++++++ scheme/racket/geiser/server.rkt | 24 +++++++++++++++++ 9 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 bin/Makefile.am create mode 100755 bin/geiser-racket.sh create mode 100644 scheme/racket/geiser/server.rkt diff --git a/.gitignore b/.gitignore index 15ac980..444b523 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ /doc/geiser.info /doc/texinfo.tex /autom4te.cache/ +/bin/Makefile.in diff --git a/Makefile.am b/Makefile.am index 0ef180e..4f5a16b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . elisp scheme doc +SUBDIRS = . elisp scheme bin doc EXTRA_DIST = THANKS diff --git a/bin/Makefile.am b/bin/Makefile.am new file mode 100644 index 0000000..9f6ee2b --- /dev/null +++ b/bin/Makefile.am @@ -0,0 +1,15 @@ +EXTRA_DIST = geiser-racket.sh +dist_bin_SCRIPTS = geiser-racket +noinst_SCRIPTS = geiser-racket-noinst + +CLEANFILES = $(dist_bin_SCRIPTS) $(noinst_SCRIPTS) + +geiser-racket: $(srcdir)/geiser-racket.sh + @sed -e "s|top=\".*\"|top=$(datarootdir)/geiser|" \ + $(srcdir)/geiser-racket.sh >$@ + @chmod +x $@ + +geiser-racket-noinst: $(srcdir)/geiser-racket.sh + @sed -e "s|top=\".*\"|top=$(abs_top_srcdir)/scheme|" \ + $(srcdir)/geiser-racket.sh >$@ + @chmod +x $@ diff --git a/bin/geiser-racket.sh b/bin/geiser-racket.sh new file mode 100755 index 0000000..4f16383 --- /dev/null +++ b/bin/geiser-racket.sh @@ -0,0 +1,19 @@ +#!/bin/bash +#| +top="$(dirname $0)/../scheme" +exec racket -i -S "$top/racket" -l errortrace -cu "$0" ${1+"$@"} +|# + +#lang racket/base + +(require (lib "cmdline.rkt")) + +(define port (make-parameter 1969)) + +(command-line + "run-racket.sh" (current-command-line-arguments) + (once-each + (("-p" "--port") p "Geiser server port" (port (string->number p))))) + +(and ((dynamic-require 'geiser/server 'start-geiser) (port)) + (printf "Geiser server running at port ~a~%" (port))) diff --git a/configure.ac b/configure.ac index 1a70214..ddeec3f 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,7 @@ elisp/Makefile elisp/geiser-version.el elisp/geiser-load.el scheme/Makefile +bin/Makefile doc/Makefile ]) diff --git a/doc/repl.texi b/doc/repl.texi index e707e61..03fb42a 100644 --- a/doc/repl.texi +++ b/doc/repl.texi @@ -45,27 +45,49 @@ evaluation when they're complete, and will indent new lines properly until then. It will also keep track of your input, maintaining a history file that will be reloaded whenever you restart the REPL. +@subsubheading Connecting to an external Scheme @cindex remote REPL @cindex connect to server -If you use Guile, there's an alternative way of starting a Geiser REPL: -you can connect to a remote Guile process, provided the latter is -running a REPL server. For that to happen, you just need to start your -Guile process (outside Emacs) passing to it the flag @code{--listen}. -Then, come back to Emacs and execute @kbd{M-x connect-to-guile}. You'll -be asked for a host and a port, with suitable default values (Guile's -@code{--listen} flag accepts an optional port as argument (as in -@code{--listen=1969}), if you don't want to use the default). And voila, -you'll have a Geiser REPL that is served by the remote Guile process in -a dedicated thread, meaning that your Guile can go on doing whatever it -was doing while you tinker with it from Emacs. Note, however, -that all Guile threads share the heap, so that you'll be able to -interact with those other threads in the running scheme from Emacs in a -variety of ways. For starters, all you (re)defintions will be visible -everywhere. That's dangerous, but will come in handy when you need to -debug your running webserver. - -Nothing that fanciful this far, but there's more to Geiser's REPL. On -to the next section! +There's an alternative way of starting a Geiser REPL: you can connect to +an external Scheme process, provided it's running a REPL server at some +known port. How to make that happen depends on the Scheme implementation. + +@cindex Guile's REPL server +If you use Guile, you just need to start your Guile process (possibly +outside Emacs) passing to it the flag @code{--listen}. This flag accepts +an optional port as argument (as in @code{--listen=1969}), if you don't +want to use the default. + +@cindex Racket's REPL server +In Racket, you have to use the REPL server that comes with Geiser. To +that end, put Geiser's Racket scheme directory in the Racket's +collection search path and invoke @code{start-geiser} (a procedure in +the module @code{geiser/server}) somewhere in your program, passing it +the desired port. This procedure will start the REPL server in a +separate thread. For an example of how to do that, see the script +@file{bin/geiser-racket.sh} in the source distribution, or, if you've +compiled Geiser, @file{bin/geiser-racket-noinst} in the build directory, +or, if you've installed Geiser, @file{geiser-racket} in +@file{/bin}. These scripts start a new interactive +Racket that is also running a REPL server (they also load the errortrace +library to provide better diagnostics, but that's not strictly needed). + +With your external Scheme process running and serving, come back to +Emacs and execute @kbd{M-x geiser-connect}, @kbd{M-x connect-to-guile} +or @kbd{M-x connect-to-racket}. You'll be asked for a host and a port, +and, voila, you'll have a Geiser REPL that is served by the remote +Scheme process in a dedicated thread, meaning that your external program +can go on doing whatever it was doing while you tinker with it from +Emacs. Note, however, that all Scheme threads share the heap, so that +you'll be able to interact with those other threads in the running +scheme from Emacs in a variety of ways. For starters, all your +(re)defintions will be visible everywhere. That's dangerous, but will +come in handy when you need to debug your running webserver. + +@cindex remote connections +The connection between Emacs and the Scheme process goes over TCP, so it +can be as remote as you need, perhaps with the intervention of an SSH +tunnel. @node First aids, Switching context, Starting the REPL, The REPL @section First aids diff --git a/elisp/geiser-connection.el b/elisp/geiser-connection.el index f146563..dc669aa 100644 --- a/elisp/geiser-connection.el +++ b/elisp/geiser-connection.el @@ -159,15 +159,17 @@ By default, Geiser uses the prompt regexp.") (make-variable-buffer-local (defvar geiser-con--debugging-preamble-regexp nil)) -(defun geiser-con--is-debugging () - (and geiser-con--debugging-prompt-regexp - geiser-con--debugging-inhibits-eval - comint-last-prompt-overlay - (string-match-p geiser-con--debugging-prompt-regexp - (buffer-substring (overlay-start - comint-last-prompt-overlay) - (overlay-end - comint-last-prompt-overlay))))) +(defun geiser-con--is-debugging (&optional con) + (with-current-buffer (or (and con (geiser-con--connection-buffer con)) + (current-buffer)) + (and geiser-con--debugging-prompt-regexp + geiser-con--debugging-inhibits-eval + comint-last-prompt-overlay + (string-match-p geiser-con--debugging-prompt-regexp + (buffer-substring (overlay-start + comint-last-prompt-overlay) + (overlay-end + comint-last-prompt-overlay)))))) (defsubst geiser-con--has-entered-debugger (con) (with-current-buffer (geiser-con--connection-buffer con) @@ -278,35 +280,24 @@ By default, Geiser uses the prompt regexp.") (defvar geiser-connection-timeout 30000 "Time limit, in msecs, blocking on synchronous evaluation requests") -(defun geiser-con--send-string/wait (buffer/proc str cont - &optional timeout sbuf) +(defun geiser-con--send-string/wait (b/p str cont &optional timeout sbuf) (save-current-buffer - (let* ((con (geiser-con--get-connection buffer/proc)) + (let* ((con (geiser-con--get-connection b/p)) (proc (and con (geiser-con--connection-process con)))) - (unless proc - (error geiser-con--error-message)) - (with-current-buffer (geiser-con--connection-buffer con) - (when (geiser-con--is-debugging) - (error "Geiser REPL is in debug mode"))) + (unless proc (error geiser-con--error-message)) + (when (geiser-con--is-debugging con) (error "REPL is in debug mode")) (let* ((req (geiser-con--make-request con str cont sbuf)) - (id (and req (geiser-con--request-id req))) - (time (or timeout geiser-connection-timeout)) - (step 100) - (waitsecs (/ step 1000.0))) - (when id - (geiser-con--connection-add-request con req) - (geiser-con--process-next con) + (id (geiser-con--request-id req)) + (timeout (/ (or timeout geiser-connection-timeout) 1000.0)) + (waitsecs 0.1)) + (geiser-con--connection-add-request con req) + (with-timeout (timeout (geiser-con--request-deactivate req)) (condition-case nil - (while (and (> time 0) - (geiser-con--connection-process con) + (while (and (geiser-con--connection-process con) (not (geiser-con--connection-completed-p con id))) - (unless (accept-process-output nil waitsecs nil nil) - (geiser-con--process-next con) - (setq time (- time step)))) - (error (setq time 0))) - (or (> time 0) - (geiser-con--request-deactivate req) - nil)))))) + (geiser-con--process-next con) + (accept-process-output proc waitsecs nil t)) + (error (geiser-con--request-deactivate req)))))))) (provide 'geiser-connection) diff --git a/elisp/geiser-racket.el b/elisp/geiser-racket.el index 18ed2eb..8c66e67 100644 --- a/elisp/geiser-racket.el +++ b/elisp/geiser-racket.el @@ -222,6 +222,18 @@ This function uses `geiser-racket-init-file' if it exists." (re-search-forward "#lang " nil t)) (geiser-racket--explicit-module))) + +;;; Remote REPLs + +(defun connect-to-racket () + "Start a Racket REPL connected to a remote process. + +The remote process needs to be running a REPL server started +using start-geiser, a procedure in the geiser/server module." + (interactive) + (geiser-connect 'racket)) + + ;;; Implementation definition: diff --git a/scheme/racket/geiser/server.rkt b/scheme/racket/geiser/server.rkt new file mode 100644 index 0000000..cf86b2c --- /dev/null +++ b/scheme/racket/geiser/server.rkt @@ -0,0 +1,24 @@ +;;; server.rkt -- REPL server + +;; Copyright (c) 2010 Jose Antonio Ortega Ruiz + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the Modified BSD License. You should +;; have received a copy of the license along with this program. If +;; not, see . + +;; Start date: Sat Nov 06, 2010 15:15 + +#lang racket/base + +(require geiser/user mzlib/thread) +(provide run-geiser-server start-geiser) + +(define (run-geiser-server port enforce-module-constants) + (run-server port + (lambda (in out) + (run-geiser-repl in out enforce-module-constants)) + #f)) + +(define (start-geiser (port 1969) (enforce-module-constants #f)) + (thread (lambda () (run-geiser-server port enforce-module-constants)))) -- cgit v1.2.3