Christophe Weblog Wiki Code Publications Music
implement `swank:operator-arglist` properly
[swankr.git] / swank.R
diff --git a/swank.R b/swank.R
index b5c79c78204695667f421bbceb3b3e83664971c1..49f73fff2b1dd3e61e4294140a03f31f727252a0 100644 (file)
--- 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,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=".")))
@@ -341,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) {
@@ -423,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)
@@ -511,7 +538,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")
   }
@@ -521,7 +548,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"
   }
@@ -583,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)) {