| # File src/library/utils/R/completion.R |
| # Part of the R package, https://www.R-project.org |
| # |
| # Copyright 2006 Deepayan Sarkar |
| # (C) 2006-2017 The R Core Team |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # A copy of the GNU General Public License is available at |
| # https://www.R-project.org/Licenses/ |
| |
| |
| |
| ### Note: By default, we try not to do things that might be slow due |
| ### to network latency (think NFS). For example, retrieving a list of |
| ### available packages is potentially slow, and is thus disabled |
| ### initially. |
| |
| |
| ### Status: I'm mostly happy with things. The only obvious |
| ### improvement I can think of is figuring out when we are in |
| ### continuation mode (R prompt == "+") and make use of previous lines |
| ### in that case. I haven't found a way to do that. |
| |
| |
| |
| ### Note: sprintf seems faster than paste based on naive benchmarking: |
| |
| ## > system.time(for (i in 1L:100000L) sprintf("foo%sbar%d", letters, 1L:26L) ) |
| ## user system total user.children system.children |
| ## 4.796 0.088 4.887 0.000 0.000 |
| ## > system.time(for (i in 1L:100000L) paste("foo", letters, "bar", 1L:26L) ) |
| ## user system total user.children system.children |
| ## 8.300 0.028 8.336 0.000 0.000 |
| |
| ### so will change all pastes to sprintf. However, we need to be |
| ### careful because 0 length components in sprintf will cause errors. |
| |
| |
| ## [July 2013] First attempt to support fuzzy matching, if |
| ## rc.settings(fuzzy=TRUE), based on suggestion from Rasmus Baath. |
| ## Initially, this replaces use of grep() to find matches by |
| ## findMatches(), which behaves differently depending on the 'fuzzy' |
| ## setting. This does not affect basic object name completion, which |
| ## is done using apropos(). For that, we need to write a fuzzy |
| ## version of apropos (which is not that difficult; just loop through |
| ## everything in the search path). |
| |
| findExactMatches <- function(pattern, values) |
| { |
| grep(pattern, values, value = TRUE) |
| } |
| |
| ### agrep() version |
| ## |
| ## findFuzzyMatches <- function(pattern, values) |
| ## { |
| ## ## Try exact matches first, and return them if found |
| ## ans <- findExactMatches(pattern, values) |
| ## if (length(ans) == 0) { |
| ## fuzzies <- |
| ## agrep(pattern, values, max.distance = 2, |
| ## ignore.case = TRUE, fixed = FALSE, value = TRUE) |
| ## ## Multiple inconsistent matches will lead to more deletion |
| ## ## than reasonable. To avoid this, we find distances, and |
| ## ## return the one with minimum distance. However, if minimum |
| ## ## distance is not unique, this will still delete. |
| ## ## E.g., a = list(.foobar = 1, foo.bar = 2) ; a$foob<TAB> |
| ## if (length(fuzzies) == 0) character(0) |
| ## else { |
| ## fdist <- adist(pattern, fuzzies, ignore.case=TRUE, partial = TRUE, fixed = FALSE) |
| ## fmin <- which(fdist == min(fdist)) |
| ## fuzzies[fmin] |
| ## } |
| ## } |
| ## else |
| ## ans |
| ## } |
| |
| ### normalizing version (from Rasmus Baath) |
| ## |
| |
| findFuzzyMatches <- function(pattern, values) { |
| ## FIXME: option to allow experimentation, remove eventually |
| if (!is.null(ffun <- getOption("fuzzy.match.fun"))) { |
| return (ffun(pattern, values)) |
| } |
| ## Try exact matches first, and return them if found |
| exact.matches <- findExactMatches(pattern, values) |
| if (length(exact.matches) == 0) { |
| ## Removes "\\." and "_" in the pattern excluding the anchor |
| ## (^) and the first character but does not removes "\\." and |
| ## "_" if it is the last character. |
| normalizedPattern <- gsub("(?<!\\^)(?<=.)(\\\\\\.|_)(?!$)", "", pattern, perl = TRUE) |
| ## Replaces "\\." and "_" last in the pattern with ".", that |
| ## is the pattern is "seq\\." we transform it into "seq." in |
| ## order to match "seq_along" and "seq.int" but not "seq". |
| normalizedPattern <- gsub("(\\\\\\.|_)$", ".", normalizedPattern) |
| normalizedValues <- gsub("(?<=.)[._]", "", values, perl = TRUE) |
| values[ grep(normalizedPattern, normalizedValues, ignore.case = TRUE) ] |
| } |
| else exact.matches |
| } |
| |
| |
| findMatches <- function(pattern, values) |
| { |
| if (.CompletionEnv$settings[["fuzzy"]]) |
| findFuzzyMatches(pattern, values) |
| else |
| findExactMatches(pattern, values) |
| } |
| |
| fuzzyApropos <- function(what) |
| { |
| stopifnot(is.character(what)) |
| x <- character(0L) |
| for (i in seq_along(search())) { |
| li <- findFuzzyMatches(what, ls(pos = i, all.names = TRUE)) |
| if (length(li)) { x <- c(x, li) } |
| } |
| findFuzzyMatches(what, x) |
| } |
| |
| ## generic and built-in methods to generate completion after $ |
| |
| .DollarNames <- function(x, pattern) |
| UseMethod(".DollarNames") |
| |
| .DollarNames.default <- function(x, pattern = "") { |
| if (is.atomic(x) || is.symbol(x)) character() |
| else findMatches(pattern, names(x)) |
| } |
| |
| .DollarNames.list <- function(x, pattern = "") { |
| findMatches(pattern, names(x)) |
| } |
| |
| .DollarNames.environment <- function(x, pattern = "") { |
| if (!.CompletionEnv$settings[["fuzzy"]]) |
| ls(x, all.names = TRUE, pattern = pattern) # more efficient |
| else |
| findMatches(pattern, ls(x, all.names = TRUE)) |
| } |
| |
| ## if (is.environment(object)) |
| ## { |
| ## ls(object, |
| ## all.names = TRUE, |
| ## pattern = sprintf("^%s", makeRegexpSafe(suffix))) |
| ## } |
| ## else |
| ## { |
| ## grep(sprintf("^%s", makeRegexpSafe(suffix)), |
| ## names(object), value = TRUE) |
| ## } |
| |
| |
| |
| |
| ## modifies settings: |
| |
| rc.settings <- function(ops, ns, args, dots, func, ipck, S3, data, help, argdb, fuzzy, quotes, files) |
| { |
| if (length(match.call()) == 1) return(unlist(.CompletionEnv[["settings"]])) |
| checkAndChange <- function(what, value) |
| { |
| if ((length(value) == 1L) && |
| is.logical(value) && |
| !is.na(value)) |
| .CompletionEnv$settings[[what]] <- value |
| } |
| if (!missing(ops)) checkAndChange( "ops", ops) |
| if (!missing(ns)) checkAndChange( "ns", ns) |
| if (!missing(args)) checkAndChange( "args", args) |
| if (!missing(dots)) checkAndChange( "dots", dots) |
| if (!missing(func)) checkAndChange( "func", func) |
| if (!missing(ipck)) checkAndChange( "ipck", ipck) |
| if (!missing(S3)) checkAndChange( "S3", S3) |
| if (!missing(data)) checkAndChange( "data", data) |
| if (!missing(help)) checkAndChange( "help", help) |
| if (!missing(argdb)) checkAndChange("argdb", argdb) |
| if (!missing(files)) checkAndChange("files", files) |
| if (!missing(quotes))checkAndChange("quotes", quotes) |
| if (!missing(fuzzy)) checkAndChange("fuzzy", fuzzy) |
| invisible() |
| } |
| |
| |
| |
| ## modifies options (adapted from similar functions in lattice): |
| |
| rc.getOption <- function(name) |
| { |
| get("options", envir = .CompletionEnv)[[name]] |
| } |
| |
| rc.options <- function(...) |
| { |
| new <- list(...) |
| if (is.null(names(new)) && length(new) == 1L && is.list(new[[1L]])) |
| new <- new[[1L]] |
| old <- .CompletionEnv$options |
| |
| ## if no args supplied, returns full options list |
| if (length(new) == 0L) return(old) |
| ## typically getting options |
| nm <- names(new) |
| if (is.null(nm)) return(old[unlist(new)]) |
| |
| isNamed <- nzchar(nm) |
| if (any(!isNamed)) nm[!isNamed] <- unlist(new[!isNamed]) |
| |
| ## so now everything has non-"" names, but only the isNamed ones |
| ## should be set. Everything should be returned though. |
| |
| retVal <- old[nm] |
| names(retVal) <- nm |
| nm <- nm[isNamed] |
| .CompletionEnv$options <- modifyList(old, new[nm]) |
| invisible(retVal) |
| } |
| |
| |
| |
| ## summarizes results of last completion attempt: |
| |
| rc.status <- function() |
| { |
| ## eapply(.CompletionEnv, function(x) x, all.names = TRUE) |
| as.list(.CompletionEnv) |
| } |
| |
| |
| ### Everything below is unexported |
| |
| |
| ## accessors called from C (also .completeToken below): |
| |
| .assignToken <- function(text) assign("token", text, envir = .CompletionEnv) |
| .assignLinebuffer <- function(line) assign("linebuffer", line, envir = .CompletionEnv) |
| .assignStart <- function(start) assign("start", start, envir = .CompletionEnv) |
| .assignEnd <- function(end) assign("end", end, envir = .CompletionEnv) |
| .setFileComp <- function(state) assign("fileName", state, envir = .CompletionEnv) |
| |
| .retrieveCompletions <- function() unique(get("comps", envir = .CompletionEnv)) |
| .getFileComp <- function() get("fileName", envir = .CompletionEnv) |
| |
| |
| |
| |
| ## The following function is not required for GNU readline, but can be |
| ## useful if one needs to break a line into tokens. It requires |
| ## linebuffer and end to be already set, and itself sets token and |
| ## start. It returns the token. |
| |
| ## FIXME: should this use getOption("rl_word_breaks")? |
| |
| .guessTokenFromLine <- |
| function(linebuffer = .CompletionEnv[["linebuffer"]], |
| end = .CompletionEnv[["end"]], |
| update = TRUE) |
| ## update=TRUE changes 'start' and 'token', otherwise they are just returned |
| { |
| linebuffer <- substr(linebuffer, 1L, end) # end is cursor, not necessarily line-end |
| ## special rules apply when we are inside quotes (see fileCompletionPreferred() below) |
| insideQuotes <- { |
| lbss <- head.default(unlist(strsplit(linebuffer, "")), .CompletionEnv[["end"]]) |
| ((sum(lbss == "'") %% 2 == 1) || |
| (sum(lbss == '"') %% 2 == 1)) |
| } |
| start <- |
| if (insideQuotes) |
| ## set 'start' to the location of the last quote |
| suppressWarnings(gregexpr("['\"]", linebuffer, |
| perl = TRUE))[[1L]] |
| else |
| ## things that should not cause breaks |
| ## _____.^._____ |
| ## / \ |
| suppressWarnings(gregexpr("[^\\.\\w:?$@[\\]]+", |
| linebuffer, |
| perl = TRUE))[[1L]] |
| start <- ## 0-indexed |
| if (all(start < 0L)) 0L |
| else tail.default(start + attr(start, "match.length"), 1L) - 1L |
| token <- substr(linebuffer, start + 1L, end) |
| if (update) { |
| .CompletionEnv[["start"]] <- start |
| .CompletionEnv[["token"]] <- token |
| .CompletionEnv[["token"]] |
| } |
| else list(start = start, token = token) |
| } |
| |
| |
| |
| |
| ## convert a string to something that escapes special regexp |
| ## characters. Doesn't have to be perfect, especially for characters |
| ## that would cause breaks or be handled elsewhere. All we really |
| ## need is to handle ".", so that e.g. "heat." doesn't match |
| ## "heatmap". |
| |
| makeRegexpSafe <- function(s) |
| { |
| ## the following can cause errors otherwise |
| s <- gsub("\\", "\\\\", s, fixed = TRUE) ## has to be the first |
| s <- gsub("(", "\\(", s, fixed = TRUE) |
| s <- gsub("*", "\\*", s, fixed = TRUE) |
| s <- gsub("+", "\\+", s, fixed = TRUE) |
| s <- gsub("?", "\\?", s, fixed = TRUE) |
| s <- gsub("[", "\\[", s, fixed = TRUE) |
| s <- gsub("{", "\\{", s, fixed = TRUE) |
| ## s <- gsub("]", "\\]", s, fixed = TRUE) # necessary? |
| ## these are wildcards that we want to interpret literally |
| s <- gsub(".", "\\.", s, fixed = TRUE) |
| s <- gsub("^", "\\^", s, fixed = TRUE) |
| ## what else? |
| s |
| } |
| |
| |
| |
| ## Operators that are handled specially. Order is important, ::: must |
| ## come before :: (because :: will match :::) |
| |
| specialOps <- c("$", "@", ":::", "::", "?", "[", "[[") |
| |
| |
| specialOpCompletionsHelper <- function(op, suffix, prefix) |
| { |
| tryToEval <- function(s) |
| { |
| tryCatch(eval(parse(text = s), envir = .GlobalEnv), error = function(e)e) |
| } |
| switch(op, |
| "$" = { |
| if (.CompletionEnv$settings[["ops"]]) |
| { |
| object <- tryToEval(prefix) |
| if (inherits(object, "error")) ## nothing else to do |
| suffix |
| else |
| { |
| ## ## suffix must match names(object) (or ls(object) for environments) |
| .DollarNames(object, pattern = sprintf("^%s", makeRegexpSafe(suffix))) |
| } |
| } else suffix |
| }, |
| "@" = { |
| if (.CompletionEnv$settings[["ops"]]) |
| { |
| object <- tryToEval(prefix) |
| if (inherits(object, "error")) ## nothing else to do |
| suffix |
| else |
| { |
| findMatches(sprintf("^%s", makeRegexpSafe(suffix)), |
| methods::slotNames(object)) |
| } |
| } else suffix |
| }, |
| "::" = { |
| if (.CompletionEnv$settings[["ns"]]) |
| { |
| nse <- tryCatch(unique(c(getNamespaceExports(prefix), |
| if(prefix != "base") |
| names(getNamespaceInfo(prefix, |
| "lazydata")) |
| )), |
| error = identity) |
| if (inherits(nse, "error")) ## nothing else to do |
| suffix |
| else |
| { |
| findMatches(sprintf("^%s", makeRegexpSafe(suffix)), |
| nse) |
| } |
| } else suffix |
| }, |
| ":::" = { |
| if (.CompletionEnv$settings[["ns"]]) |
| { |
| ns <- tryCatch(getNamespace(prefix), error = function(e)e) |
| if (inherits(ns, "error")) ## nothing else to do |
| suffix |
| else |
| { |
| if (!.CompletionEnv$settings[["fuzzy"]]) |
| ls(ns, |
| all.names = TRUE, |
| pattern = sprintf("^%s", makeRegexpSafe(suffix))) |
| else |
| findMatches(sprintf("^%s", makeRegexpSafe(suffix)), |
| ls(ns, all.names = TRUE)) |
| } |
| } else suffix |
| }, |
| "[" = , # can't think of anything else to do |
| "[[" = { |
| comps <- normalCompletions(suffix) |
| if (length(comps)) comps |
| else suffix |
| }) |
| } |
| |
| |
| |
| |
| specialOpLocs <- function(text) |
| { |
| ## does text contain a special operator? There may be multiple |
| ## occurrences, and we want the last one (whereas regexpr gives |
| ## the first). So... |
| |
| ge <- |
| sapply(specialOps, |
| function(s) gregexpr(s, text, fixed = TRUE)[[1L]], |
| simplify = FALSE) |
| ## this gets the last ones |
| ge <- sapply(ge, tail.default, 1) |
| ge <- ge[ge > 0] |
| } |
| |
| |
| |
| ## Accessing the help system: should allow anything with an index entry. |
| ## This just looks at packages on the search path. |
| |
| matchAvailableTopics <- function(prefix, text) |
| { |
| .readAliases <- function(path) { |
| if(file.exists(f <- file.path(path, "help", "aliases.rds"))) |
| names(readRDS(f)) |
| else if(file.exists(f <- file.path(path, "help", "AnIndex"))) |
| ## aliases.rds was introduced before 2.10.0, as can phase this out |
| scan(f, what = list("", ""), sep = "\t", quote = "", |
| na.strings = "", quiet = TRUE)[[1L]] |
| else character() |
| } |
| if (length(text) != 1L || text == "") return (character()) |
| ## Update list of help topics if necessary |
| pkgpaths <- searchpaths()[substr(search(), 1L, 8L) == "package:"] |
| if (!identical(basename(pkgpaths), .CompletionEnv[["attached_packages"]])) { |
| assign("attached_packages", |
| basename(pkgpaths), |
| envir = .CompletionEnv) |
| assign("help_topics", |
| unique(unlist(lapply(pkgpaths, .readAliases))), |
| envir = .CompletionEnv) |
| } |
| aliases <- .CompletionEnv[["help_topics"]] |
| ans <- findMatches(sprintf("^%s", makeRegexpSafe(text)), aliases) |
| if (nzchar(prefix)) { |
| ## FIXME: This is a little unsafe. We are not protecting |
| ## prefix to make sure that we do not get any special |
| ## characters (like ? or + or *). However, these are unlikely |
| ## in practice. |
| tmp <- grep(sprintf("-%s$", prefix), ans, value = TRUE) |
| if (length(tmp)) substring(tmp, 1, nchar(tmp) - nchar(prefix) - 1L) |
| else character(0) |
| } |
| else ans |
| } |
| |
| |
| |
| ## this is for requests of the form ?suffix[TAB] or prefix?suffix[TAB] |
| |
| |
| helpCompletions <- function(prefix = "", suffix) |
| { |
| ## Do not attempt to complete ??<foo> (help.search) or ???<foo> (invalid) |
| if (prefix %in% c("?", "??")) return (character(0)) |
| nc <- |
| if (.CompletionEnv$settings[["help"]]) |
| matchAvailableTopics(prefix, suffix) |
| else |
| normalCompletions(suffix, check.mode = FALSE) |
| if (length(nc)) sprintf("%s?%s", prefix, nc) |
| else character() |
| } |
| |
| |
| specialCompletions <- function(text, spl) |
| { |
| |
| ## we'll only try to complete after the last special operator, and |
| ## assume that everything before is meaningfully complete. A more |
| ## sophisticated version of this function may decide to do things |
| ## differently. |
| |
| ## Note that this will involve evaluations, which may have side |
| ## effects. This (side-effects) would not happen normally (except |
| ## of lazy loaded symbols, which most likely would have been |
| ## evaluated shortly anyway), because explicit function calls |
| ## (with parentheses) are not evaluated. In any case, these |
| ## evaluations won't happen if settings$ops==FALSE |
| |
| ## spl (locations of matches) is guaranteed to be non-empty |
| |
| wm <- which.max(spl) |
| op <- names(spl)[wm] |
| opStart <- spl[wm] |
| opEnd <- opStart + nchar(op) |
| |
| if (opStart < 1) return(character()) # shouldn't happen |
| prefix <- substr(text, 1L, opStart - 1L) |
| suffix <- substr(text, opEnd, 1000000L) |
| |
| if (op == "?") return(helpCompletions(prefix, suffix)) |
| |
| if (opStart <= 1) return(character()) # not meaningful |
| |
| ## ( breaks words, so prefix should not involve function calls, |
| ## and thus, hopefully no side-effects. |
| |
| comps <- specialOpCompletionsHelper(op, suffix, prefix) |
| if (length(comps) == 0L) comps <- "" |
| sprintf("%s%s%s", prefix, op, comps) |
| } |
| |
| |
| |
| ## completions on special keywords (subset of those in gram.c). Some |
| ## issues with parentheses: e.g. mode(get("repeat")) is "function", so |
| ## it is normally completed with a left-paren appended, but that is |
| ## not normal usage. Putting it here means that both 'repeat' and |
| ## 'repeat(' will be valid completions (as they should be) |
| |
| |
| keywordCompletions <- function(text) |
| { |
| ## FIXME: Will not allow fuzzy completions, as this adds too much |
| ## noise in normalCompletions. Should revisit later once we |
| ## figure out a way to suppress fuzzy matching if there is at |
| ## least one exact match. |
| findExactMatches(sprintf("^%s", makeRegexpSafe(text)), |
| c("NULL", "NA", "TRUE", "FALSE", "Inf", "NaN", |
| "NA_integer_", "NA_real_", "NA_character_", "NA_complex_", |
| "repeat ", "in ", "next ", "break ", "else ")) |
| } |
| |
| |
| |
| |
| ## 'package' environments in the search path. These will be completed |
| ## with a :: (Use of this is function is replaced by |
| ## loadedPackageCompletions below, which also completes packages |
| ## loaded, but not necessarily attached). |
| |
| |
| attachedPackageCompletions <- function(text, add = rc.getOption("package.suffix")) |
| { |
| ## FIXME: Will not allow fuzzy completions. See comment in keywordCompletions() above |
| if (.CompletionEnv$settings[["ns"]]) |
| { |
| s <- grep("^package", search(), value = TRUE) |
| comps <- |
| findExactMatches(sprintf("^%s", makeRegexpSafe(text)), |
| substr(s, 9L, 1000000L)) |
| if (length(comps) && !is.null(add)) |
| sprintf("%s%s", comps, add) |
| else |
| comps |
| } |
| else character() |
| } |
| |
| loadedPackageCompletions <- function(text, add = rc.getOption("package.suffix")) |
| { |
| ## FIXME: Will not allow fuzzy completions. See comment in keywordCompletions() above |
| if (.CompletionEnv$settings[["ns"]]) |
| { |
| s <- loadedNamespaces() |
| comps <- findExactMatches(sprintf("^%s", makeRegexpSafe(text)), s) |
| if (length(comps) && !is.null(add)) |
| sprintf("%s%s", comps, add) |
| else |
| comps |
| } |
| else character() |
| } |
| |
| |
| |
| ## this provides the most basic completion, looking for completions in |
| ## the search path using apropos, plus keywords. Plus completion on |
| ## attached/loaded packages if settings$ns == TRUE |
| |
| |
| normalCompletions <- |
| function(text, check.mode = TRUE, |
| add.fun = rc.getOption("function.suffix")) |
| { |
| ## use apropos or equivalent |
| if (text == "") character() ## too many otherwise |
| else |
| { |
| comps <- |
| if (.CompletionEnv$settings[["fuzzy"]]) |
| fuzzyApropos(sprintf("^%s", makeRegexpSafe(text))) |
| else |
| apropos(sprintf("^%s", makeRegexpSafe(text)), ignore.case = FALSE) |
| if (.CompletionEnv$settings[["func"]] && check.mode && !is.null(add.fun)) |
| { |
| which.function <- sapply(comps, function(s) exists(s, mode = "function")) |
| if (any(which.function)) |
| comps[which.function] <- |
| sprintf("%s%s", comps[which.function], add.fun) |
| ##sprintf("\033[31m%s\033[0m%s", comps[which.function], add.fun) |
| } |
| c(comps, keywordCompletions(text), loadedPackageCompletions(text)) |
| } |
| } |
| |
| |
| ## completion on function arguments. This involves the most work (as |
| ## we need to look back in the line buffer to figure out which |
| ## function we are inside, if any), and is also potentially intensive |
| ## when many functions match the function that we are supposedly in |
| ## (which will happen for common generic functions like print (we are |
| ## very optimistic here, erring on the side of |
| ## whatever-the-opposite-of-caution-is (our justification being that |
| ## erring on the side of caution is practically useless and not erring |
| ## at all is expensive to the point of being impossible (we really |
| ## don't want to evaluate the dotplot() call in "print(dotplot(x), |
| ## positi[TAB] )" )))) |
| |
| |
| ## this defines potential function name boundaries |
| |
| breakRE <- "[^\\.\\w]" |
| ## breakRE <- "[ \t\n \\\" '`><=-%;,&}\\\?\\\+\\\{\\\|\\\(\\\)\\\*]" |
| |
| |
| |
| |
| ## for some special functions like library, data, etc, normal |
| ## completion is rarely meaningful, especially for the first argument. |
| ## Unfortunately, knowing whether the token being completed is the |
| ## first arg of such a function involves more work than we would |
| ## normally want to do. On the other hand, inFunction() below already |
| ## does most of this work, so we will add a piece of code (mostly |
| ## irrelevant to its primary purpose) to indicate this. The following |
| ## two functions are just wrappers to access and modify this |
| ## information. |
| |
| |
| setIsFirstArg <- function(v) |
| .CompletionEnv[["isFirstArg"]] <- v |
| |
| getIsFirstArg <- function() |
| .CompletionEnv[["isFirstArg"]] |
| |
| |
| |
| inFunction <- |
| function(line = .CompletionEnv[["linebuffer"]], |
| cursor = .CompletionEnv[["start"]]) |
| { |
| ## are we inside a function? Yes if the number of ( encountered |
| ## going backwards exceeds number of ). In that case, we would |
| ## also like to know what function we are currently inside |
| ## (ideally, also what arguments to it have already been supplied, |
| ## but let's not dream that far ahead). |
| |
| parens <- |
| sapply(c("(", ")"), |
| function(s) gregexpr(s, substr(line, 1L, cursor), fixed = TRUE)[[1L]], |
| simplify = FALSE) |
| ## remove -1's |
| parens <- lapply(parens, function(x) x[x > 0]) |
| |
| ## The naive algo is as follows: set counter = 0; go backwards |
| ## from cursor, set counter-- when a ) is encountered, and |
| ## counter++ when a ( is encountered. We are inside a function |
| ## that starts at the first ( with counter > 0. |
| |
| temp <- |
| data.frame(i = c(parens[["("]], parens[[")"]]), |
| c = rep.int(c(1, -1), lengths(parens))) |
| if (nrow(temp) == 0) return(character()) |
| temp <- temp[order(-temp$i), , drop = FALSE] ## order backwards |
| wp <- which(cumsum(temp$c) > 0) |
| if (length(wp)) # inside a function |
| { |
| ## return guessed name of function, letting someone else |
| ## decide what to do with that name |
| |
| index <- temp$i[wp[1L]] |
| prefix <- substr(line, 1L, index - 1L) |
| suffix <- substr(line, index + 1L, cursor + 1L) |
| |
| ## note in passing whether we are the first argument (no '=' |
| ## and no ',' in suffix) |
| |
| if ((length(grep("=", suffix, fixed = TRUE)) == 0L) && |
| (length(grep(",", suffix, fixed = TRUE)) == 0L)) |
| setIsFirstArg(TRUE) |
| |
| |
| if ((length(grep("=", suffix, fixed = TRUE))) && |
| (length(grep(",", substr(suffix, |
| tail.default(gregexpr("=", suffix, fixed = TRUE)[[1L]], 1L), |
| 1000000L), fixed = TRUE)) == 0L)) |
| { |
| ## we are on the wrong side of a = to be an argument, so |
| ## we don't care even if we are inside a function |
| return(character()) |
| } |
| else ## guess function name |
| { |
| possible <- suppressWarnings(strsplit(prefix, breakRE, perl = TRUE))[[1L]] |
| possible <- possible[nzchar(possible)] |
| if (length(possible)) return(tail.default(possible, 1)) |
| else return(character()) |
| } |
| } |
| else # not inside function |
| { |
| return(character()) |
| } |
| } |
| |
| |
| argNames <- |
| function(fname, use.arg.db = .CompletionEnv$settings[["argdb"]]) |
| { |
| if (use.arg.db) args <- .FunArgEnv[[fname]] |
| if (!is.null(args)) return(args) |
| ## else |
| args <- do.call(argsAnywhere, list(fname)) |
| if (is.null(args)) |
| character() |
| else if (is.list(args)) |
| unlist(lapply(args, function(f) names(formals(f)))) |
| else |
| names(formals(args)) |
| } |
| |
| |
| |
| specialFunctionArgs <- function(fun, text) |
| { |
| ## certain special functions have special possible arguments. |
| ## This is primarily applicable to library and require, for which |
| ## rownames(installed.packages()). This is disabled by default, |
| ## because the first call to installed.packages() can be time |
| ## consuming, e.g. on a network file system. However, the results |
| ## are cached, so subsequent calls are not that expensive. |
| |
| switch(EXPR = fun, |
| |
| library = , |
| require = { |
| if (.CompletionEnv$settings[["ipck"]]) |
| { |
| findMatches(sprintf("^%s", makeRegexpSafe(text)), |
| rownames(installed.packages())) |
| } |
| else character() |
| }, |
| |
| data = { |
| if (.CompletionEnv$settings[["data"]]) |
| { |
| findMatches(sprintf("^%s", makeRegexpSafe(text)), |
| data()$results[, "Item"]) |
| } |
| else character() |
| }, |
| |
| ## otherwise, |
| character()) |
| } |
| |
| |
| |
| functionArgs <- |
| function(fun, text, |
| S3methods = .CompletionEnv$settings[["S3"]], |
| S4methods = FALSE, |
| keep.dots = .CompletionEnv$settings[["dots"]], |
| add.args = rc.getOption("funarg.suffix")) |
| { |
| if (length(fun) < 1L || any(fun == "")) return(character()) |
| specialFunArgs <- specialFunctionArgs(fun, text) |
| if (S3methods && exists(fun, mode = "function")) |
| fun <- |
| c(fun, |
| tryCatch(methods(fun), |
| warning = function(w) {}, |
| error = function(e) {})) |
| if (S4methods) warning("cannot handle S4 methods yet") |
| allArgs <- unique(unlist(lapply(fun, argNames))) |
| ans <- findMatches(sprintf("^%s", makeRegexpSafe(text)), allArgs) |
| if (length(ans) && !keep.dots) |
| ans <- ans[ans != "..."] |
| if (length(ans) && !is.null(add.args)) |
| ans <- sprintf("%s%s", ans, add.args) |
| c(specialFunArgs, ans) |
| } |
| |
| |
| |
| ## Note: Inside the C code, we redefine |
| ## rl_attempted_completion_function rather than |
| ## rl_completion_entry_function, which means that if |
| ## retrieveCompletions() returns a length-0 result, by default the |
| ## fallback filename completion mechanism will be used. This is not |
| ## quite the best way to go, as in certain (most) situations filename |
| ## completion will definitely be inappropriate even if no valid R |
| ## completions are found. We could return "" as the only completion, |
| ## but that produces an irritating blank line on |
| ## list-possible-completions (or whatever the correct name is). |
| ## Instead (since we don't want to reinvent the wheel), we use the |
| ## following scheme: If the character just preceding our token is " or |
| ## ', we immediately go to file name completion. If not, we do our |
| ## stuff, and disable file name completion (using |
| ## .Call("RCSuppressFileCompletion")) even if we don't find any |
| ## matches. |
| |
| ## Note that under this scheme, filename completion will fail |
| ## (possibly in unexpected ways) if the partial name contains 'unusual |
| ## characters', namely ones that have been set (see C code) to cause a |
| ## word break because doing so is meaningful in R syntax (e.g. "+", |
| ## "-" ("/" is exempt (and treated specially below) because of its |
| ## ubiquitousness in UNIX file names (where this package is most |
| ## likely to be used)) |
| |
| |
| ## decide whether to fall back on filename completion. Yes if the |
| ## number of quotes between the cursor and the beginning of the line |
| ## is an odd number. |
| |
| ## FIXME: should include backtick (`)? May be useful, but needs more |
| ## thought; e.g., should imply not-filename, but rather variable |
| ## names. Must cooperate with the if (isInsideQuotes()) branch in |
| ## .completeToken(). |
| |
| isInsideQuotes <- |
| fileCompletionPreferred <- function() |
| { |
| (.CompletionEnv[["start"]] > 0 && { |
| |
| ## yes if the number of quote signs to the left is odd |
| linebuffer <- .CompletionEnv[["linebuffer"]] |
| lbss <- head.default(unlist(strsplit(linebuffer, "")), .CompletionEnv[["end"]]) |
| ((sum(lbss == "'") %% 2 == 1) || |
| (sum(lbss == '"') %% 2 == 1)) |
| |
| }) |
| } |
| |
| |
| ## File name completion, used if settings$quotes == TRUE. Front ends |
| ## that can do filename completion themselves should probably not use |
| ## this if they can do a better job. |
| |
| correctFilenameToken <- function() |
| { |
| ## Helper function |
| |
| ## If a file name contains spaces, the token will only have the |
| ## part after the last space. This function tries to recover the |
| ## complete initial part. |
| |
| ## Find part between last " or ' |
| linebuffer <- .CompletionEnv[["linebuffer"]] |
| lbss <- head.default(unlist(strsplit(linebuffer, "")), .CompletionEnv[["end"]]) |
| whichDoubleQuote <- lbss == '"' |
| whichSingleQuote <- lbss == "'" |
| insideDoubleQuote <- (sum(whichDoubleQuote) %% 2 == 1) |
| insideSingleQuote <- (sum(whichSingleQuote) %% 2 == 1) |
| loc.start <- |
| if (insideDoubleQuote && insideSingleQuote) |
| { |
| ## Should not happen, but if it does, should take whichever comes later |
| max(which(whichDoubleQuote), which(whichSingleQuote)) |
| } |
| else if (insideDoubleQuote) |
| max(which(whichDoubleQuote)) |
| else if (insideSingleQuote) |
| max(which(whichSingleQuote)) |
| else ## should not happen, abort non-intrusively |
| .CompletionEnv[["start"]] |
| substring(linebuffer, loc.start + 1L, .CompletionEnv[["end"]]) |
| } |
| |
| |
| |
| |
| fileCompletions <- function(token) |
| { |
| ## uses Sys.glob (conveniently introduced in 2.5.0) |
| |
| ## token may not start just after the begin quote, e.g., if spaces |
| ## are included. Get 'correct' partial file name by looking back |
| ## to begin quote |
| pfilename <- correctFilenameToken() |
| |
| ## This may come from an illegal string like "C:\Prog". Try to parse it: |
| pfilename <- try(eval(parse(text = paste0('"', token, '"'))), silent = TRUE) |
| if (inherits(pfilename, "try-error")) |
| return(character()) |
| |
| ## Sys.glob doesn't work without expansion. Is that intended? |
| pfilename.expanded <- path.expand(pfilename) |
| comps <- Sys.glob(sprintf("%s*", pfilename.expanded), dirmark = TRUE) |
| |
| ## If there is only one completion (and it's a directory), also |
| ## include files inside in list of completions. This is not |
| ## particularly useful, but without this, readline tends to add an |
| ## end-quote (if sole completion) which is irritating if one is |
| ## actually looking for something inside the directory. Note that |
| ## we don't actually test to see if it's a directory, because if |
| ## it is not, list.files() will simply return character(0). |
| if (length(comps) == 1 && substring(comps, nchar(comps), nchar(comps)) == "/") { |
| filesInside <- list.files(comps, all.files = TRUE, full.names = FALSE, no.. = TRUE) |
| if (length(filesInside)) comps <- c(comps, file.path(comps, filesInside)) |
| } |
| |
| ## for things that only extend beyond the cursor, need to |
| ## 'unexpand' path |
| if (pfilename.expanded != pfilename) |
| comps <- sub(path.expand("~"), "~", comps, fixed = TRUE) |
| |
| ## for tokens that were non-trivially corrected by adding prefix, |
| ## need to delete extra part |
| if (pfilename != token) |
| comps <- substring(comps, nchar(pfilename) - nchar(token) + 1L, 1000L) |
| |
| ## In Win32, we often have backslashes in names. Also possible on |
| ## Unix, though unlikely. Add escapes for those and for quotes. |
| comps <- gsub("([\\\\'\"])", "\\\\\\1", comps) |
| |
| comps |
| } |
| |
| |
| |
| |
| |
| ## .completeToken() is the primary interface, and does the actual |
| ## completion when called from C code. |
| |
| |
| .completeToken <- function() |
| { |
| ## Allow override by user-specified function |
| custom.completer <- rc.getOption("custom.completer") |
| if (is.function(custom.completer)) |
| return (custom.completer(.CompletionEnv)) |
| text <- .CompletionEnv[["token"]] |
| if (isInsideQuotes()) |
| { |
| |
| ## If we're in here, that means we think the cursor is inside |
| ## quotes. In most cases, this means that standard filename |
| ## completion is more appropriate, but probably not if we're |
| ## trying to access things of the form x["foo... or x$"foo... |
| ## The following tries to figure this out, but it won't work |
| ## in all cases (e.g. x[, "foo<TAB>"]) |
| |
| ## We assume that whoever determines our token boundaries |
| ## considers quote signs as a breaking symbol. |
| |
| |
| ## If the 'quotes' setting is FALSE, we will make no attempt to |
| ## do filename completion (this is likely to happen with |
| ## front-ends that are capable of doing their own file name |
| ## completion; such front-ends can fall back to their native |
| ## file completion when rc.status("fileName") is TRUE. |
| |
| if (.CompletionEnv$settings[["quotes"]]) |
| { |
| |
| ## ## This was used to make a guess whether we are in |
| ## ## special situations like ::, ?, [, etc. But from R |
| ## ## 3.0.0 we re-evaluate the token based from the |
| ## ## begin-quote, so this is postponed. This part can be |
| ## ## deleted once this is stable enough. |
| ## st <- .CompletionEnv[["start"]] |
| ## probablyNotFilename <- |
| ## ((st > 2L && |
| ## ((prequote <- substr(.CompletionEnv[["linebuffer"]], st-1L, st-1L)) %in% c("?", "[", ":", "$"))) || |
| ## (st == 2L && |
| ## ((prequote <- substr(.CompletionEnv[["linebuffer"]], st-1L, st-1L)) %in% c("?"))) |
| ## ) |
| |
| ## FIXME|TODO: readline (and maybe other backends) will |
| ## usually use a fixed set of breakpoints to detect |
| ## tokens. If we are handling quotes ourselves, the more |
| ## likely correct token is everything from the last |
| ## unclosed quote onwards (which may include spaces, |
| ## punctuations, etc. that would normally cause breaks). |
| ## We already do this when we guess the token ourselves |
| ## (e.g., for Windows) (and also in the fileCompletions() |
| ## call below using correctFilenameToken()), and can |
| ## re-use that here. The problem is that for other |
| ## backends a token may already have been determined, and |
| ## that's what we will need to use. We can still fake it |
| ## by using the correct token but substracting the extra |
| ## part when providing completions, but that will need |
| ## some work. |
| |
| ## Related to that: if we implement that, should also |
| ## check before for '<type>?' and move to help completion |
| ## if so. |
| |
| ### str(correctFilenameToken()) |
| ### str(.guessTokenFromLine(update = FALSE)) |
| |
| ## TODO: For extra credit, we could also allow for |
| ## spaces like in 'package ? grid', but will leave |
| ## that for the future (maybe some regexp magic will |
| ## make this simple) |
| |
| fullToken <- .guessTokenFromLine(update = FALSE) |
| probablyHelp <- (fullToken$start >= 2L && |
| ((substr(.CompletionEnv[["linebuffer"]], |
| fullToken$start-1L, |
| fullToken$start-1L)) == "?")) |
| if (probablyHelp) { |
| fullToken$prefix <- .guessTokenFromLine(end = fullToken$start - 2, update = FALSE)$token |
| } |
| probablyName <- ((fullToken$start > 2L && |
| ((substr(.CompletionEnv[["linebuffer"]], |
| fullToken$start-1L, |
| fullToken$start-1L)) == "$")) |
| || |
| (fullToken$start > 3L && |
| ((substr(.CompletionEnv[["linebuffer"]], |
| fullToken$start-2L, |
| fullToken$start-1L)) == "[["))) |
| probablyNamespace <- (fullToken$start > 3L && |
| ((substr(.CompletionEnv[["linebuffer"]], |
| fullToken$start-2L, |
| fullToken$start-1L)) %in% c("::"))) |
| ## in anticipation that we will handle this eventually: |
| ## probablyBacktick <- (fullToken$start >= 1L && |
| ## ((substr(.CompletionEnv[["linebuffer"]], |
| ## fullToken$start, |
| ## fullToken$start)) %in% c("`"))) |
| |
| probablySpecial <- probablyHelp || probablyName || probablyNamespace |
| |
| ## str(list(probablyHelp = probablyHelp, |
| ## probablyName = probablyName, |
| ## probablyNamespace = probablyNamespace, |
| ## probablyBacktick = probablyBacktick, |
| ## probablySpecial = probablySpecial)) |
| |
| ## For now, we only handle probablyHelp, and just decline |
| ## to do filename completion if any of the other special |
| ## situations are detected (but don't try to complete). |
| |
| tentativeCompletions <- |
| if (probablyHelp) { |
| substring(helpCompletions(fullToken$prefix, fullToken$token), |
| 2L + nchar(fullToken$prefix), 1000L) # drop initial "prefix + ?" |
| } |
| else if (!probablySpecial) |
| fileCompletions(fullToken$token) # FIXME: but not if probablyBacktick |
| .setFileComp(FALSE) |
| ## str(c(fullToken, list(comps = tentativeCompletions))) |
| ## Adjust for self-computed token |
| .CompletionEnv[["comps"]] <- |
| substring(tentativeCompletions, |
| 1L + nchar(fullToken$token) - nchar(text), |
| 1000L) |
| } |
| else |
| { |
| .CompletionEnv[["comps"]] <- character() |
| .setFileComp(TRUE) |
| } |
| } |
| else |
| { |
| .setFileComp(FALSE) |
| setIsFirstArg(FALSE) # might be changed by inFunction() call |
| ## make a guess at what function we are inside |
| guessedFunction <- |
| if (.CompletionEnv$settings[["args"]]) |
| inFunction(.CompletionEnv[["linebuffer"]], |
| .CompletionEnv[["start"]]) |
| else "" |
| .CompletionEnv[["fguess"]] <- guessedFunction |
| |
| ## if this is not "", then we want to add possible arguments |
| ## of that function(s) (methods etc). Should be character() |
| ## if nothing matches |
| |
| fargComps <- functionArgs(guessedFunction, text) |
| |
| if (getIsFirstArg() && length(guessedFunction) && |
| guessedFunction %in% |
| c("library", "require", "data")) |
| { |
| .CompletionEnv[["comps"]] <- fargComps |
| ## don't try anything else |
| return() |
| } |
| |
| ## Is there an arithmetic operator in there in there? If so, |
| ## work on the part after that and append to prefix before |
| ## returning. It would have been easier if these were |
| ## word-break characters, but that potentially interferes with |
| ## filename completion. |
| |
| ## lastArithOp <- tail(gregexpr("/", text, fixed = TRUE)[[1L]], 1) |
| lastArithOp <- tail.default(gregexpr("[\"'^/*+-]", text)[[1L]], 1) |
| if (haveArithOp <- (lastArithOp > 0)) |
| { |
| prefix <- substr(text, 1L, lastArithOp) |
| text <- substr(text, lastArithOp + 1L, 1000000L) |
| } |
| |
| spl <- specialOpLocs(text) |
| comps <- |
| if (length(spl)) |
| specialCompletions(text, spl) |
| else |
| { |
| ## should we append a left-paren for functions? |
| ## Usually yes, but not when inside certain special |
| ## functions which often take other functions as |
| ## arguments |
| |
| appendFunctionSuffix <- |
| !any(guessedFunction %in% |
| |
| c("help", "args", "formals", "example", |
| "do.call", "environment", "page", "apply", |
| "sapply", "lapply", "tapply", "mapply", |
| "methods", "fix", "edit")) |
| |
| normalCompletions(text, check.mode = appendFunctionSuffix) |
| } |
| if (haveArithOp && length(comps)) |
| { |
| comps <- paste0(prefix, comps) |
| } |
| comps <- c(fargComps, comps) |
| .CompletionEnv[["comps"]] <- comps |
| } |
| } |
| |
| |
| |
| ## support functions that attempt to provide tools useful specifically |
| ## for the Windows Rgui. |
| |
| |
| ## Note: even though these are unexported functions, changes in the |
| ## API should be noted in man/rcompgen.Rd |
| |
| |
| .win32consoleCompletion <- |
| function(linebuffer, cursorPosition, |
| check.repeat = TRUE, |
| minlength = -1) |
| { |
| isRepeat <- ## is TAB being pressed repeatedly with this combination? |
| if (check.repeat) |
| (linebuffer == .CompletionEnv[["linebuffer"]] && |
| cursorPosition == .CompletionEnv[["end"]]) |
| else TRUE |
| |
| .assignLinebuffer(linebuffer) |
| .assignEnd(cursorPosition) |
| .guessTokenFromLine() |
| token <- .CompletionEnv[["token"]] |
| comps <- |
| if (nchar(token, type = "chars") < minlength) character() |
| else |
| { |
| .completeToken() |
| .retrieveCompletions() |
| } |
| |
| ## FIXME: no idea how much of this is MBCS-safe |
| |
| if (length(comps) == 0L) |
| { |
| ## no completions |
| addition <- "" |
| possible <- character() |
| } |
| else if (length(comps) == 1L) |
| { |
| ## FIXME (maybe): in certain cases the completion may be |
| ## shorter than the token (e.g. when trying to complete on an |
| ## impossible name inside a list). It's debatable what the |
| ## behaviour should be in this case, but readline and Emacs |
| ## actually delete part of the token (at least currently). To |
| ## achieve this in Rgui one would need to do somewhat more |
| ## work than I'm ready to do right now (especially since it's |
| ## not clear that this is the right thing to do to begin |
| ## with). So, in this case, I'll just pretend that no |
| ## completion was found. |
| |
| addition <- substr(comps, nchar(token, type = "chars") + 1L, 100000L) |
| possible <- character() |
| } |
| else if (length(comps) > 1L) |
| { |
| ## more than one completion. The right thing to is to extend |
| ## the line by the unique part if any, and list the multiple |
| ## possibilities otherwise. |
| |
| additions <- substr(comps, nchar(token, type = "chars") + 1L, 100000L) |
| if (length(table(substr(additions, 1L, 1L))) > 1L) |
| { |
| ## no unique substring |
| addition <- "" |
| possible <- |
| if (isRepeat) capture.output(cat(format(comps, justify = "left"), fill = TRUE)) |
| else character() |
| } |
| else |
| { |
| ## need to figure out maximal unique substr |
| keepUpto <- 1 |
| while (length(table(substr(additions, 1L, keepUpto))) == 1L) |
| keepUpto <- keepUpto + 1L |
| addition <- substr(additions[1L], 1L, keepUpto - 1L) |
| possible <- character() |
| } |
| } |
| list(addition = addition, |
| possible = possible, |
| comps = paste(comps, collapse = " ")) |
| } |
| |
| |
| |
| |
| ## usage: |
| |
| ## .addFunctionInfo(foo = c("arg1", "arg2"), bar = c("a", "b")) |
| |
| .addFunctionInfo <- function(...) |
| { |
| dots <- list(...) |
| for (nm in names(dots)) |
| .FunArgEnv[[nm]] <- dots[[nm]] |
| } |
| |
| .initialize.argdb <- |
| function() |
| { |
| ## lattice |
| |
| lattice.common <- |
| c("data", "allow.multiple", "outer", "auto.key", "aspect", |
| "panel", "prepanel", "scales", "strip", "groups", "xlab", |
| "xlim", "ylab", "ylim", "drop.unused.levels", "...", |
| "default.scales", "subscripts", "subset", "formula", "cond", |
| "aspect", "as.table", "between", "key", "legend", "page", |
| "main", "sub", "par.strip.text", "layout", "skip", "strip", |
| "strip.left", "xlab.default", "ylab.default", "xlab", |
| "ylab", "panel", "xscale.components", "yscale.components", |
| "axis", "index.cond", "perm.cond", "...", "par.settings", |
| "plot.args", "lattice.options") |
| |
| densityplot <- |
| c("plot.points", "ref", "groups", "jitter.amount", |
| "bw", "adjust", "kernel", "weights", "window", "width", |
| "give.Rkern", "n", "from", "to", "cut", "na.rm") |
| |
| panel.xyplot <- |
| c("type", "groups", "pch", "col", "col.line", |
| "col.symbol", "font", "fontfamily", "fontface", "lty", |
| "cex", "fill", "lwd", "horizontal") |
| |
| .addFunctionInfo(xyplot.formula = c(lattice.common, panel.xyplot), |
| densityplot.formula = c(lattice.common, densityplot)) |
| |
| ## grid |
| |
| grid.clip <- |
| c("x", "y", "width", "height", "just", "hjust", "vjust", |
| "default.units", "name", "vp") |
| grid.curve <- |
| c("x1", "y1", "x2", "y2", "default.units", "curvature", |
| "angle", "ncp", "shape", "square", "squareShape", "inflect", |
| "arrow", "open", "debug", "name", "gp", "vp") |
| grid.polyline <- |
| c("x", "y", "id", "id.lengths", "default.units", "arrow", |
| "name", "gp", "vp") |
| grid.xspline <- |
| c("x", "y", "id", "id.lengths", "default.units", "shape", |
| "open", "arrow", "repEnds", "name", "gp", "vp") |
| |
| .addFunctionInfo(grid.clip = grid.clip, |
| grid.curve = grid.curve, |
| grid.polyline = grid.polyline, |
| grid.xspline = grid.xspline) |
| |
| ## par, options |
| |
| par <- |
| c("xlog", "ylog", "adj", "ann", "ask", "bg", "bty", "cex", |
| "cex.axis", "cex.lab", "cex.main", "cex.sub", "cin", "col", |
| "col.axis", "col.lab", "col.main", "col.sub", "cra", "crt", |
| "csi", "cxy", "din", "err", "family", "fg", "fig", "fin", |
| "font", "font.axis", "font.lab", "font.main", "font.sub", |
| "gamma", "lab", "las", "lend", "lheight", "ljoin", "lmitre", |
| "lty", "lwd", "mai", "mar", "mex", "mfcol", "mfg", "mfrow", |
| "mgp", "mkh", "new", "oma", "omd", "omi", "pch", "pin", |
| "plt", "ps", "pty", "smo", "srt", "tck", "tcl", "usr", |
| "xaxp", "xaxs", "xaxt", "xpd", "yaxp", "yaxs", "yaxt", |
| "page", "ylbias") |
| |
| options <- |
| c(names(options()), ## + some that are NULL by default |
| "mc.cores", "dvipscmd", "warn.FPU", "aspell_program", |
| "deparse.max.lines", "digits.secs", "error", "help.ports", |
| "help_type", "save.defaults", "save.image.defaults", |
| "SweaveHooks", "SweaveSyntax", "topLevelEnvironment") |
| |
| .addFunctionInfo(par = par, options = options) |
| |
| ## read.csv etc (... passed to read.table) |
| readTable <- |
| c("file", "header", "sep", "quote", "dec", "numerals", |
| "row.names", "col.names", "as.is", "na.strings", |
| "colClasses", "nrows", "skip", "check.names", "fill", |
| "strip.white", "blank.lines.skip", "comment.char", |
| "allowEscapes", "flush", "stringsAsFactors", "fileEncoding", |
| "encoding", "text", "skipNul") |
| |
| .addFunctionInfo(read.csv = readTable, read.csv2 = readTable, |
| read.delim = readTable, read.delim2 = readTable) |
| |
| ## c() because it is primitive c(...) and there is no proper |
| ## c.default even though docs say it is |
| ## c.default(..., recursive = FALSE, use.names = TRUE) |
| |
| .addFunctionInfo(c = c("recursive", "use.names")) |
| } |
| |
| |
| |
| .CompletionEnv <- new.env(hash = FALSE) |
| |
| ## needed to save some overhead in .win32consoleCompletion |
| assign("linebuffer", "", env = .CompletionEnv) |
| assign("end", 1, env = .CompletionEnv) |
| |
| assign("settings", |
| list(ops = TRUE, ns = TRUE, |
| args = TRUE, dots = TRUE, func = FALSE, |
| ipck = FALSE, S3 = TRUE, data = TRUE, |
| help = TRUE, argdb = TRUE, fuzzy = FALSE, |
| files = TRUE, # FIXME: deprecate in favour of quotes |
| quotes = TRUE), |
| env = .CompletionEnv) |
| |
| assign("options", |
| list(package.suffix = "::", |
| funarg.suffix = "=", |
| function.suffix = "("), |
| env = .CompletionEnv) |
| |
| ## These keeps track of attached packages and available help topics. |
| ## Needs updating only when packages are attached. |
| assign("attached_packages", character(0), env = .CompletionEnv) |
| assign("help_topics", character(0), env = .CompletionEnv) |
| |
| |
| .FunArgEnv <- new.env(hash = TRUE, parent = emptyenv()) |
| |
| .initialize.argdb() |
| |