%in% needs a `vector' first argument, so make it so, listifying
anything that isn't already a vector. (Note: there seem to be plenty
of non-vector first arguments that work, such as
as.Date("2012-01-01"), which returns FALSE to is.vector() -- but the new
code seems to get that right anyway, based on very limited testing.
pass an :encoding stanza in the connection information
Allows slime-repl to start again. I've said "utf-8-unix" but that is
almost certainly a lie; I have no real idea how R handles encodings of
text. Simply passing in "ë" to the R slime repl breaks things without
too much effort.
don't print out "locals" from the global environment
This is important because e.g. the repl evaluation happens in the
global environment, so errors on code called from the repl will pull
up a backtrace with that evaluation frame, which can be inspected for
locals. But printing out all the locals is a hugely expensive and not
helpful thing to do.
There ought to be some way of catching this mistake, which is passing
a non-length-1 vector to the swank functions, which then vectorise and
confuse the event stream.
enough support for emacs/slim to start swank for itself
There's a race condition, I think, in between writing the socket port
and running the socket server; it might be as simple as reordering the
writing and the socket opening, but strange things happened once when
I did that.
Because of our vaguely dodgy heuristic for finding the "defun" to
evaluate, C-c C-c is not terribly useful. This allows at least C-c
C-r to be used without thinking.
This fixes bug #1. It is somewhat on the risky side given that there
are current known protocol problems in the presence of non-ASCII
encodings, but it does make working in the slime repl much more
pleasant.
Wow, this was hard. parse() constructs a vector with mode
"expression", consisting of multiple, possibly nested calls.
Scattered throughout this structure are srcrefs with absolute
locations. This means that we need to walk the parsed data structure
and adjust every srcref that we can find, giving it an offset based on
the location information passed to compile-string-for-emacs (which
requires bleeding-edge 2010-10-08 slime, because earlier versions
don't pass line/column information in the position argument).
But we can't simply adjust the "srcref" attribute on our parsed data
structure, because R tries very hard to be pure. Instead we need to
return a copy with the right modifications (but preserving everything
else of importance). It's straightforward once you know how, but
there were many painful missteps to get to this point. Still, now
M-. works on function names assigned with C-c C-c in source buffers.
Firstly, only parse, don't source, as that seems to be the
protocol (the emacs side is responsible for requesting a load if
desired). Secondly, the protocol is also to return a
compilation-result structure rather than just TRUE, so do that too.
implement swank:default-directory and swank:set-default-directory
It was surprisingly hard to intuit getwd() and setwd(), what with
apropos("dir") and apropos("cwd") failing. Nevertheless, they exist
and function, so use them for the interface (but not slimefun)
functions.
R has capture.output as equivalent to with-output-to-string, so use it.
printToString, used by the slime-repl, now reverts back to calling
print. New function prin1ToString, used by swank:interactive-eval and
swank:eval-and-grab-output, uses deparse.
Implement enough that C-c I begins to work, at least for values like
list(1,2,3). The resulting inspector on the emacs side apparently has
no features, and an error message results on quitting the inspector
because I've only implemented `swank:init-inspector`, and not
`swank:quit-inspector`. Still, good enough to checkpoint.
instead of using print(), use str(). Also, be even more cautious
about cleaning up in printToString; previously, errors in printing
would lead to a sink to a closed fifo...
Printing is still not really right, and I forsee that we will end up
writing our own printer to get something more lispy. But at least
this approach tends not to dump huge tables into the REPL.
in readPacket, call socketSelect() before actually trying to read on
the connection. This seems to allow R to update graphics windows and
other such niceties.
It would be nice to be able to preserve the (inferior) R REPL as well,
but I haven't yet discovered whether that's possible.
A simple implementation, only looking for a single function
definition (no methods, whether S3 or S4, or indeed anything else).
Enough to support M-., though.
initial implementation of support for REPL presentations
There's a lot here that's ugly, unfinished or just downright horrible.
Most notably, presentation support depends on swank-side read-time
evaluation (indicated with Common Lisp syntax, which hilariously is a
comment in R). We can't support that in general, but we can
special-case the presentation-specific operator.
But then the next difficulty comes along; actually performing that
read-time evaluation needs to happen in a different environment than
the evaluation of the REPL form. In order to achieve this, we abuse
bquote() the equivalent of Lisp's backquote facility, by calling what
in CL terms would be its macro-function on the parsed, preprocessed
expression; only after doing that (and hence resolving the `read-time'
evaluations) do we evaluate the call itself.
The implementation of presentation protocol messages is also slightly
ugly; having to implement cl:nth-value is particulraly horrible, but
the lack of weak references / weak tables in R (at least as far as I
can tell at the moment) is a cause of niggling concern.
use an environment holding the Slime I/O connection
Rather than having a bare io argument everywhere, encapsulate it in an
environment. (An environment is just about the only thing I can find
in R that isn't copy-on-write; this isn't helpful for the i/o
connection, but will be once we start implementing inspectors and
presentations...)
implement swank:interactive-eval and swank:eval-and-grab-output
This allows C-c : and C-u C-c : to work, modulo the printing of an
unnecessary [1]. The evaluation semantics of R are not what a Lisper might
expect; printToString(eval(parse(...))) does not necessarily perform the
evaluation before altering the output stream with sink() -- so capturing
all sorts of incidental output if something goes wrong.
Implement swank:frame-locals-and-catch-tags. There are problems:
1. it seems that some names can be present in an environment but not have
a value, causing eval to blow up;
2. the default printing routines aren't really adapted to the constraints
of rendering in sldb;
3. trying to display locals of some of the swank frames (perhaps anything
with a tryCatch or similar block?) causes R to complain about a promise
already in the process of evaluation;
4. (probably) other things I haven't yet found
Rather than do complicated stuff to keep track of where we are in the
stack, simply store the interesting frames and calls in the sldb state
structure.
R doesn't have much in the way of dynamic binding; you can fake it by
messing with environments, but that's not fun.
So instead, pass around the connection (`io') and an object
representing the SLDB state (`sldbState') to all functions.
Poor-man's explicit continuation-passing-style...
We need to call some of the mainLoop internal functions from elsewhere
now, so make them not-internal any more.
Fix ridiculous thinko in the logical branch of writeSexpToString
use simpleCondition rather than simpleError in swank:throw-to-toplevel
implement swank:debugger-info-for-emacs
Now `q' in sldb (sldb-quit) works. Things that don't work:
1. the backtrace is the wrong way up.
2. calling any restarts
3. frame locals
4. returning from frames (dunno if R actually supports this)
5. zoom to source
6. probably other things
We get far enough to start up a swank server in R and connect to it from
emacs; we get a REPL which formats the R-style results from parsing and
evaluating the input it receives. Debugging, stepping, documentation,
arglist and so on is basically entirely missing.