# Given a line buffer, return a list of possible auto completions.
# If there is a valid label, then attach the server env to allow for local overrides of functions
auto_complete_r <- function(line, label = NULL, server_env = NULL) {
# If the last line includes comments then we don't return any completions.
# It's okay to consider only the last line for comments: Comment detection
# takes into account quotes on the same line, but `quotes = FALSE` in the
# completion settings below ensures completions aren't returned if the last
# line is part of a multi-line quote.
last_line <- utils::tail(strsplit(line, "\n")[[1]], 1)
if (length(last_line) == 0 || detect_comment(last_line)) {
# If a comment is found, return `list()` to signify no completions are found
# (Similar to the output of Map(list, list()))
return(list())
}
# set completion settings
options <- utils::rc.options()
utils::rc.options(package.suffix = "::",
funarg.suffix = " = ",
function.suffix = "(")
on.exit(do.call(utils::rc.options, as.list(options)), add = TRUE)
# If and when exercises gain access to files, then we should evaluate this
# code in the exercise dir with `quotes = TRUE` (and sanitize to keep
# filename lookup local to exercise dir)
settings <- utils::rc.settings()
utils::rc.settings(ops = TRUE, ns = TRUE, args = TRUE, func = FALSE,
ipck = TRUE, S3 = TRUE, data = TRUE, help = TRUE,
argdb = TRUE, fuzzy = FALSE, files = FALSE, quotes = FALSE)
on.exit(do.call(utils::rc.settings, as.list(settings)), add = TRUE)
# temporarily attach global setup to search path
# for R completion engine
do.call("attach", list(server_env, name = "tutorial:server_env"))
on.exit(detach("tutorial:server_env"), add = TRUE)
# temporarily attach env to search path
# for R completion engine
if (isTRUE(nzchar(label)) && is.environment(server_env[[label]])) {
do.call("attach", list(server_env[[label]], name = "tutorial:question_env"))
on.exit(detach("tutorial:question_env"), add = TRUE)
}
completions <- character()
try(silent = TRUE, {
utils <- asNamespace("utils")
utils$.assignLinebuffer(line)
utils$.assignEnd(nchar(line))
utils$.guessTokenFromLine()
utils$.completeToken()
completions <- as.character(utils$.retrieveCompletions())
})
# detect functions
splat <- strsplit(completions, ":{2,3}")
fn <- vapply(splat, function(el) {
n <- length(el)
envir <- if (n == 1) .GlobalEnv else asNamespace(el[[1]])
symbol <- if (n == 2) el[[2]] else el[[1]]
tryCatch(
is.function(get(symbol, envir = envir)),
error = function(e) FALSE
)
}, logical(1))
# remove a leading '::', ':::' from autocompletion results, as
# those won't be inserted as expected in Ace
completions <- gsub("[^:]+:{2,3}(.)", "\\1", completions)
completions <- completions[nzchar(completions)]
# zip together
result <- Map(list, completions, fn, USE.NAMES = FALSE)
# return completions
as.list(result)
}
detect_comment <- function(line = "") {
line <- strsplit(line, "")[[1]]
quote_str <- ""
in_quote <- FALSE
in_escape <- FALSE
for (char in line) {
if (identical(char, "\\")) {
in_escape <- TRUE
next
}
if (char %in% c("'", '"')) {
if (in_escape) {
in_escape <- FALSE
} else if (identical(quote_str, "")) {
in_quote <- TRUE
quote_str <- char
} else if (identical(char, quote_str)) {
in_quote <- FALSE
quote_str <- ""
} else {
# ignore a quote within a quote
}
next
}
in_escape <- FALSE
if (!identical(char, "#")) next
if (in_quote) next
return(TRUE)
}
FALSE
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.