X-Git-Url: http://christophe.rhodes.io/gitweb/?p=swankr.git;a=blobdiff_plain;f=swank.R;h=67fdbbcfc9cdb1d63e72130a3e169176312f9584;hp=cf64daaaa81b8ad7341284a67dd67423058c922f;hb=fdb4f4d744580b9dc6db92489610f32e42cec15e;hpb=4da3085437b358a5c9b21a91ad01732c85568416 diff --git a/swank.R b/swank.R index cf64daa..67fdbbc 100644 --- a/swank.R +++ b/swank.R @@ -33,9 +33,10 @@ acceptConnections <- function(port, portFile) { 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) { @@ -62,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) } @@ -145,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") } @@ -287,6 +293,8 @@ printToString <- function(val) { `swank:connection-info` <- function (slimeConnection, sldbState) { list(quote(`:pid`), Sys.getpid(), quote(`:package`), list(quote(`:name`), "R", quote(`:prompt`), "R> "), + quote(`:version`), "2012-04-23", + 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="."))) @@ -324,22 +332,49 @@ 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)) - tmp <- withVisible(eval(lookedup, envir = globalenv())) - if(tmp$visible) { - sendReplResultFunction(slimeConnection, tmp$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() } `swank:autodoc` <- function(slimeConnection, sldbState, rawForm, ...) { - "No Arglist Information" + list("No Arglist Information", TRUE) } `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:describe-function` <- function(slimeConnection, sldbState, op, package) { + ## FIXME: maybe not the best match? + `swank:operator-arglist`(slimeConnection, sldbState, op, package) +} + +helpFilesWithTopicString <- function(value) { + output <- capture.output(tools:::Rd2txt(utils:::.getHelpFile(value), + options=list(underline_titles=FALSE))) + paste(output, collapse="\n") +} + +`swank:describe-symbol` <- function(slimeConnection, sldbState, op, package) { + value <- help(op) + helpFilesWithTopicString(value) } `swank:throw-to-toplevel` <- function(slimeConnection, sldbState) { @@ -421,24 +456,59 @@ 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) + symbolFieldsCompletion <- function(object, rest) { + ## FIXME: this is hacky, ignoring several syntax issues (use of + ## and/or necessity for backquoting identifiers: e.g. fields + ## containing hyphens) + if((dollar <- regexpr("$", rest, fixed=TRUE)) == -1) { + matches <- grep(sprintf("^%s", literal2rx(rest)), names(object), value=TRUE) + matches <- sprintf("%s$%s", gsub("\\$[^$]*$", "", prefix), matches) + returnMatches(matches) + } else { + if(exists(substr(rest, 1, dollar-1), object)) { + symbolFieldsCompletion(get(substr(rest, 1, dollar-1), object), substr(rest, dollar+1, nchar(rest))) + } else { + returnMatches(character(0)) + } + } + } + returnMatches <- function(matches) { + nmatches <- length(matches) + if(nmatches == 0) { + list(list(), "") + } else { + longest <- matches[order(nchar(matches))][1] + while(length(grep(sprintf("^%s", literal2rx(longest)), matches)) < nmatches) { + longest <- substr(longest, 1, nchar(longest)-1) + } + list(as.list(matches), longest) + } + } + 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(), "") + if((nmatches == 0) && ((dollar <- regexpr("$", prefix, fixed=TRUE)) > -1)) { + symbolFieldsCompletion(globalenv(), prefix) } else { - longest <- matches[order(nchar(matches))][1] - while(length(grep(sprintf("^%s", longest), matches)) < nmatches) { - longest <- substr(longest, 1, nchar(longest)-1) - } - list(as.list(matches), longest) + returnMatches(matches) } } @@ -460,6 +530,23 @@ computeRestartsForEmacs <- function (sldbState) { ifelse(x[3]==1, x[6]+colOffset-1, x[6])))) } transformSrcrefs <- function(s) { + ## horrendous KLUDGE: we need to short-circuit here for "name" + ## objects, rather than having a nice uniform behaviour, because + ## for expressions of the form x[y,] there is an empty "name" + ## which ends up becoming a `missing' object when passed through + ## the switch; why, I do not know, but it is then impossible to + ## return it, because returning it attempts to evaluate it and + ## evaluating it is an error. Fortunately it appears that names + ## don't have srcrefs attached. + if(mode(s) == "name") { + return(s) + } + if(is(s, "srcref")) { + ## more monumental KLUDGE: parsing (in 2.14, at least) appears + ## to put srcrefs directly in `length 2' objects, which we need + ## to frob directly. + return(frob(list(s))[[1]]) + } srcrefs <- attr(s, "srcref") attribs <- attributes(s) new <- @@ -471,6 +558,9 @@ computeRestartsForEmacs <- function (sldbState) { if(!is.null(attr(s, "srcref"))) { attr(new, "srcref") <- frob(srcrefs) } + if(!is.null(attr(s, "wholeSrcref"))) { + attr(new, "wholeSrcref") <- frob(list(attr(s, "wholeSrcref")))[[1]] + } new } withRestarts({ @@ -509,7 +599,7 @@ withRetryRestart <- function(description, expr) { envir=globalenv()))) }) output <- paste(output, sep="", collapse="\n") if(tmp$visible) { - list(output, prin1ToString(value)) + list(output, prin1ToString(tmp$value)) } else { list(output, "# invisible value") } @@ -519,7 +609,7 @@ withRetryRestart <- function(description, expr) { withRetryRestart("retry SLIME interactive evaluation request", tmp <- withVisible(eval(parse(text=string), envir=globalenv()))) if(tmp$visible) { - prin1ToString(value) + prin1ToString(tmp$value) } else { "# invisible value" } @@ -581,12 +671,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)) { @@ -597,7 +694,7 @@ inspectObject <- function(slimeConnection, object) { valuePart <- function(istate, object, string) { list(quote(`:value`), - if(is.null(string)) printToString(object) else string, + if(is.null(string)) prin1ToString(object) else string, assignIndexInParts(object, istate)) } @@ -608,7 +705,7 @@ preparePart <- function(istate, part) { switch(as.character(part[[1]]), `:newline` = list("\n"), `:value` = valuePart(istate, part[[2]], part[[3]]), - `:line` = list(printToString(part[[2]]), ": ", + `:line` = list(prin1ToString(part[[2]]), ": ", valuePart(istate, part[[3]], NULL), "\n")) } }