SLIME and in-package
Pascal J. Bourguignon
pjb at informatimago.com
Sat Feb 6 09:53:55 UTC 2016
On 06/02/16 08:39, Peter Keller wrote:
> Hello,
>
> I have a question, hopefully easy, but I can't seem to find the answer.
>
> I'd like to evaluate a region which contains something like this:
>
> (asdf:load-system '#:foo)
> (in-package #'foo)
>
> and have my slime REPL actually change its current package.
Slime evaluates expressions coming from emacs in a separate thread in
the inferior lisp.
This thread cannot modify the state of the REPL thread, unless there is
specific code (that you should write), to have those threads communicate
and change the binding of *package* in the REPL thread (suppedly, at
some specific points, you wouldn't want to do that at random in the
middle of the evaluation (or worse, of the reading) of an expression in
the REPL).
> I also realize that slime may be LOADing the file, which preserves the
> current package. Since I get a => <PACKAGE "FOO"> in my *slime-events*
> buffer I think that may be also happening.
Yes, this is how LOAD is specified. It binds *package* and *readtable*,
so that modifications of those bindings inside a loaded file won't
affect the caller. However, the loaded files can still _mutate_ the
current package and readtable. Not that I would advise that as a
solution, but theorically, you could "copy" the current package, then
transform it into the package you want for the REPL! :-)
Actually, it might be easier to do something like that conformingly,
than dealing with the REPL thread, only, in the case of slime, the RELP
thread is not the native REPL either, it's a thread created by swank, so
that it would only involve modifying swank.
If you wanted to mutate the package, you would have to be careful to
_move_ the symbols to the new package, and to update the use
dependencies in the other packages. Only direct references to the old
package object couldn't easily be updated everywhere, so it would not be
a good 100% solution, but it could work 99% of the time, if you consider
that the original package would be CL-USER, and that there is probably
no code keeping direct references to CL-USER. Notice that there's no
constraint imposed by the standard on the CL-USER package, you can do
whatever you want with it. On the other hand, you couldn't do that if
the current package was CL (and some people do (in-package :CL), so this
may occur).
> Is there any way to change the current REPL package by using any SLIME
> evaluation methods that involve evaluating a region? Or must I either manually
> type (in-package :foo) in the REPL or use the ,in-package SLIME command helper.
What "current" REPL? You can have any number of REPLs with slime/swank!
Do you want to change the current package in all the REPLs?
This would be very inconvenient, one reason to use multiple REPLs is to
have different bindings for *package*!
How would you associate a buffer with a REPL?
(I'll let you write the function to do that).
> I'm just about to write some elisp to take the region, find the slime repl
> buffer, manually append the region into the REPL buffer, and enter a return to
> execute it! There must be a better way!
So the problem is actually about the swank REPL.
(If you wanted to have the REPL package changed by loading a file, in a
native REPL, then some implementations provide a hook eg. when
displaying the prompt, that you could use to set the current package).
In the case of swank, the repl thread is spawn by
swank-repl::spawn-repl-thread which calls repl-loop which calls
swank::handle-requests. The later uses swank::eval-for-emacs, which
runs *pre-reply-hook* after having evaluated the form. Unfortunately,
changing the package after evaluating a form means that it would be one
expression late.
(in-package :foo) in slime
*package* in repl --> :old-package
*package* in repl --> :foo
We would need a *pre-eval-hook*. So you will have to patch
swank::eval-for-emacs adding (run-hook *pre-eval-hook*) before the call
to eval.
Then you can install a function in the *pre-reply-hook* to save the
*package*, and another function in *pre-eval-hook* to set *package* to
the saved value.
Now, there would be little surprises, like:
(in-package :foo) in slime
in repl, the prompt would be: CL-USER>
*package* in repl --> :foo
now the prompt is FOO>
Now looking further into it, we notice that in swank-repl.lisp, there is
a a repl-eval, stored in *listener-eval-function*, called by the slime
function listener-eval. We can see in contrib/slime-repl.el that
listener-eval the function that is requested to be called from slime
when you evaluate things in the REPL. There's no hook here. In
swank-listener-hooks.lisp there is an alternative %listener-eval that
could be used instead of repl-eval, that has a hook,
*slime-repl-eval-hooks* but it is evaluated after evaluating the forms
sent by emacs, so it doesn't help either.
However, we notice in swank-repl.lisp that changes to *package* are
tracked with the macro track-package which sends a message to slime with
the new package and the new prompt to be displayed in the REPL.
lse> (let ((*package* (find-package :keyword)))
(swank::send-to-emacs (list :new-package (package-name *package*)
(swank::package-string-for-prompt
*package*))))
nil
keyword> cl:*package*
#<Package "KEYWORD">
keyword>
But this cannot be used, because it's not the sender who decides what
slime-repl buffer should be updated. Since emacs is single-threaded,
there's a single current slime-repl buffer handling those :new-package
messages at a time.
So it looks like you would need a new message type like
:new-package-for-repl that would add a handle to the slime-repl
buffer(s) corresponding to the swank repl thread(s) for which the new
package is saved.
Also, notice that I've not considered the various modes of operations of
swank, it can run without multiple threads too, and on the slime side,
there's slime-repl and slime-mrepl.
A last note: what I do to help the REPL user when loading a file, is
that sometimes I add a (print '(in-package :this-package)) at the end of
the file. So that when it's loaded, the user can just copy-and-paste the
in-package form in the REPL, if he wants to switch to this package.
--
__Pascal J. Bourguignon__
http://www.informatimago.com/
More information about the slime-devel
mailing list