X-Git-Url: http://christophe.rhodes.io/gitweb/?p=swankr.git;a=blobdiff_plain;f=swank.R;h=49f73fff2b1dd3e61e4294140a03f31f727252a0;hp=00967c558d916290739138a0c4177267fe697e57;hb=f26811f35b41594dbbc6c6c53b0d09fd819c152d;hpb=f0e61c84f770a997753de7c6928eacbb89756a1d diff --git a/swank.R b/swank.R index 00967c5..49f73ff 100644 --- a/swank.R +++ b/swank.R @@ -13,18 +13,30 @@ ### latest version of the GNU General Public Licence is available at ### . +### KLUDGE: this assumes that we're being sourced with chdir=TRUE. +### (If not, `swank:swank-require` will work under the circumstances +### that it used to work anyway -- i.e. the working directory is the +### swankr directory) +swankrPath <- getwd() + swank <- function(port=4005) { acceptConnections(port, FALSE) } startSwank <- function(portFile) { - acceptConnections(FALSE, portFile) + acceptConnections(4005, portFile) } acceptConnections <- function(port, portFile) { + if(portFile != FALSE) { + f <- file(portFile, open="w+") + cat(port, file=f) + close(f) + } + ## FIXME: maybe we should support dontClose here? s <- socketConnection(host="localhost", server=TRUE, port=port, open="r+b") on.exit(close(s)) - serve(s) + tryCatch(serve(s), endOfFile=function(c) NULL) } serve <- function(io) { @@ -51,7 +63,7 @@ dispatch <- function(slimeConnection, event, sldbState=NULL) { sendToEmacs <- function(slimeConnection, obj) { io <- slimeConnection$io payload <- writeSexpToString(obj) - writeChar(sprintf("%06x", nchar(payload)), io, eos=NULL) + writeChar(sprintf("%06x", nchar(payload, type="bytes")), io, eos=NULL) writeChar(payload, io, eos=NULL) flush(io) } @@ -134,6 +146,11 @@ readPacket <- function(io) { readChunk <- function(io, len) { buffer <- readChar(io, len) + if(length(buffer) == 0) { + condition <- simpleCondition("End of file on io") + class(condition) <- c("endOfFile", class(condition)) + signalCondition(condition) + } if(nchar(buffer) != len) { stop("short read in readChunk") } @@ -210,7 +227,14 @@ readSexpFromString <- function(string) { } else if(grepl("^[0-9]+\\.[0-9]+$", token)) { as.double(token) } else { - as.name(token) + name <- as.name(token) + if(name == quote(t)) { + TRUE + } else if(name == quote(nil)) { + FALSE + } else { + name + } } } readToken <- function() { @@ -269,6 +293,7 @@ printToString <- function(val) { `swank:connection-info` <- function (slimeConnection, sldbState) { list(quote(`:pid`), Sys.getpid(), quote(`:package`), list(quote(`:name`), "R", quote(`:prompt`), "R> "), + quote(`:encoding`), list(quote(`:coding-systems`), list("utf-8-unix")), quote(`:lisp-implementation`), list(quote(`:type`), "R", quote(`:name`), "R", quote(`:version`), paste(R.version$major, R.version$minor, sep="."))) @@ -276,7 +301,7 @@ printToString <- function(val) { `swank:swank-require` <- function (slimeConnection, sldbState, contribs) { for(contrib in contribs) { - filename <- sprintf("%s.R", as.character(contrib)) + filename <- sprintf("%s/%s.R", swankrPath, as.character(contrib)) if(file.exists(filename)) { source(filename) } @@ -306,11 +331,15 @@ sendReplResultFunction <- sendReplResult `swank:listener-eval` <- function(slimeConnection, sldbState, string) { ## O how ugly string <- gsub("#\\.\\(swank:lookup-presented-object-or-lose([^)]*)\\)", ".(`swank:lookup-presented-object-or-lose`(slimeConnection, sldbState,\\1))", string) - expr <- parse(text=string)[[1]] - ## O maybe this is even uglier - lookedup <- do.call("bquote", list(expr)) - value <- eval(lookedup, envir = globalenv()) - sendReplResultFunction(slimeConnection, value) + for(expr in parse(text=string)) { + expr <- expr + ## O maybe this is even uglier + lookedup <- do.call("bquote", list(expr)) + tmp <- withVisible(eval(lookedup, envir = globalenv())) + if(tmp$visible) { + sendReplResultFunction(slimeConnection, tmp$value) + } + } list() } @@ -319,7 +348,16 @@ sendReplResultFunction <- sendReplResult } `swank:operator-arglist` <- function(slimeConnection, sldbState, op, package) { - list() + if(!exists(op, envir = globalenv())) { + return(list()) + } + funoid <- get(op, envir = globalenv()) + if(is.function(funoid)) { + args <- formals(funoid) + paste(sprintf("%s=%s", names(args), args), collapse=", ") + } else { + list() + } } `swank:throw-to-toplevel` <- function(slimeConnection, sldbState) { @@ -401,21 +439,32 @@ computeRestartsForEmacs <- function (sldbState) { `swank:frame-locals-and-catch-tags` <- function(slimeConnection, sldbState, index) { frame <- sldbState$frames[[1+index]] objs <- ls(envir=frame) + if(identical(frame, globalenv())) { + objs <- c() + } list(lapply(objs, function(name) { list(quote(`:name`), name, quote(`:id`), 0, - quote(`:value`), printToString(eval(parse(text=name), envir=frame))) }), + quote(`:value`), + tryCatch({ + printToString(eval(parse(text=name), envir=frame)) + }, error=function(c) { + sprintf("error printing object") + }))}), list()) } `swank:simple-completions` <- function(slimeConnection, sldbState, prefix, package) { - ## fails multiply if prefix contains regexp metacharacters - matches <- apropos(sprintf("^%s", prefix), ignore.case=FALSE) + literal2rx <- function(string) { + ## list of ERE metacharacters from ?regexp + gsub("([.\\|()[{^$*+?])", "\\\\\\1", string) + } + matches <- apropos(sprintf("^%s", literal2rx(prefix)), ignore.case=FALSE) nmatches <- length(matches) if(nmatches == 0) { list(list(), "") } else { longest <- matches[order(nchar(matches))][1] - while(length(grep(sprintf("^%s", longest), matches)) < nmatches) { + while(length(grep(sprintf("^%s", literal2rx(longest)), matches)) < nmatches) { longest <- substr(longest, 1, nchar(longest)-1) } list(as.list(matches), longest) @@ -474,17 +523,35 @@ withRetryRestart <- function(description, expr) { `swank:interactive-eval` <- function(slimeConnection, sldbState, string) { withRetryRestart("retry SLIME interactive evaluation request", - value <- eval(parse(text=string), envir=globalenv())) - prin1ToString(value) + tmp <- withVisible(eval(parse(text=string), envir=globalenv()))) + if(tmp$visible) { + prin1ToString(tmp$value) + } else { + "# invisible value" + } } `swank:eval-and-grab-output` <- function(slimeConnection, sldbState, string) { withRetryRestart("retry SLIME interactive evaluation request", { output <- - capture.output(value <- eval(parse(text=string), - envir=globalenv())) }) + capture.output(tmp <- withVisible(eval(parse(text=string), + envir=globalenv()))) }) output <- paste(output, sep="", collapse="\n") - list(output, prin1ToString(value)) + if(tmp$visible) { + list(output, prin1ToString(tmp$value)) + } else { + list(output, "# invisible value") + } +} + +`swank:interactive-eval-region` <- function(slimeConnection, sldbState, string) { + withRetryRestart("retry SLIME interactive evaluation request", + tmp <- withVisible(eval(parse(text=string), envir=globalenv()))) + if(tmp$visible) { + prin1ToString(tmp$value) + } else { + "# invisible value" + } } `swank:find-definitions-for-emacs` <- function(slimeConnection, sldbState, string) { @@ -543,12 +610,19 @@ resetInspector <- function(slimeConnection) { } inspectObject <- function(slimeConnection, object) { + vectorify <- function(x) { + if(is.vector(x)) { + x + } else { + list(x) + } + } previous <- slimeConnection$istate slimeConnection$istate <- new.env() slimeConnection$istate$object <- object slimeConnection$istate$previous <- previous slimeConnection$istate$content <- emacsInspect(object) - if(!(object %in% slimeConnection$inspectorHistory)) { + if(!(vectorify(object) %in% slimeConnection$inspectorHistory)) { slimeConnection$inspectorHistory <- c(slimeConnection$inspectorHistory, object) } if(!is.null(slimeConnection$istate$previous)) { @@ -683,12 +757,18 @@ emacsInspect.numeric <- function(numeric) { } `swank:load-file` <- function(slimeConnection, sldbState, filename) { - source(filename, local=FALSE) + source(filename, local=FALSE, keep.source=TRUE) TRUE } `swank:compile-file-for-emacs` <- function(slimeConnection, sldbState, filename, loadp, ...) { - times <- system.time(parse(filename)) + times <- system.time(parse(filename, srcfile=srcfile(filename))) + if(loadp) { + ## KLUDGE: inelegant, but works. It might be more in the spirit + ## of things to keep the result of the parse above around to + ## evaluate. + `swank:load-file`(slimeConnection, sldbState, filename) + } list(quote(`:compilation-result`), list(), TRUE, times[3], substitute(loadp), filename) }