Nothing
#' Get textual help on function or function arguments, or get a call tip
#'
#' @description Textual help on functions or their arguments is extracted for
#' text online help for a given function. By default, all arguments from the
#' online help are returned for `describe_args()`. If the file contains help for
#' several functions, one probably gets also some irrelevant information. Use of
#' 'args' to limit result is strongly encouraged. `args_tip()` provides a
#' human-readable textual description of function arguments in a better way than
#' `args()` does. It is primarily intended for code tips in GUIs. `call_tip()`
#' has a similar purpose to show how some code could be completed.
#'
#' @param fun A character string with the name of a function (several functions
#' accepted too for `describe_function()`.
#' @param args Either `NULL` (by default) to return the description of all
#' arguments from the corresponding man page, or a character vector with names
#' of the arguments to search for.
#' @param package A character string with the name of the package that contains
#' `fun`, or `NULL` for searching in all loaded packages.
#' @param lib.loc A character vector of directory names of \R libraries, or
#' `NULL`. The default value of `NULL` corresponds to all libraries currently
#' known. If the default is used, the loaded packages are searched before the
#' libraries.
#' @param name A string with the name of a function.
#' @param code A fraction of R code ending with the name of a function,
#' eventually followed by '('.
#' @param only.args Do we return only arguments of the function
#' (`arg1, arg2 = TRUE, ...`), or the full call, like
#' (`myfun(arg1, arg2 = TRUE, ...)`).
#' @param width Reformat the tip to fit to fit in that width, except if
#' `width = NULL`.
#' @param location If `TRUE` then the location (in which package the function
#' resides) is appended to the calltip between square brackets.
#' @param description If `TRUE` then a short description of the function is
#' added to the call_tip (in fact, the title of the corresponding help page, if
#' it exists).
#' @param methods If `TRUE` then a short message indicating if this is a generic
#' function and that lists, in this case, available methods.
#' @return A string with the description of the function or of its arguments, or
#' the calling syntax of the function, plus additional information depending on
#' the flags used. If the man page is not found, a vector of empty strings is
#' returned. Empty strings are also returned for arguments that are not found in
#' the man page.
#' @note `args_tip()` is supposed to display S3 and S4 methods, and primitives
#' adequately,... but this is not implemented yet in the current version! For
#' `call_tip()`, the use of `methods = TRUE` slows down the execution of the
#' function, especially for generic functions that have many methods like
#' `print()` or `summary()`.
#' @export
#' @seealso [completion()], [args()], [argsAnywhere()]
#' @keywords utilities
#' @concept graphical user interface (GUI) control
#' @examples
#' describe_function("ls", "base")
#' describe_function("library", "base")
#' describe_function("descFun", "svMisc")
#' describe_function("descArgs")
#'
#' describe_args("ls")
#' describe_args("library", args = c("package", "pos"))
#'
#' args_tip("ls")
#'
#' call_tip("myvar <- lm(")
describe_function <- function(fun, package, lib.loc = NULL) {
if (!length(fun))
return("")
fun <- as.character(fun)
l <- length(fun)
if (missing(package) || is.null(package)) package <- ""
package <- rep(package, length.out = l)
# Create a vector of results
res <- rep("", l)
# Collect help for each function
for (i in 1:l) {
# Get location of the help file
# We cannot just call help normally because otherwise it thinks
# we are looking for package "package" so we create a call and eval it
help_call <- call("help", fun[i], lib.loc = lib.loc, help_type = "text")
if (package[i] != "") help_call[["package"]] <- package[i]
file <- as.character(eval(help_call))
if (length(file) > 0) {
# Read the Rd file and get the title section out of it
Rdoc <- getNamespace("utils")$.getHelpFile(file[1L])
# Look for the \title tag
j <- 0
for (j in seq_along(Rdoc))
if (attr(Rdoc[[j]], "Rd_tag") == "\\title") break
if (j > 0) {
desc <- as.character(Rdoc[[j]][[1]])
desc <- sub("^[ \t]+", "", desc)
desc <- sub("[ \t]+$", "", desc)
res[i] <- desc
}
}
}
res
}
#' @export
#' @rdname describe_function
describe_args <- function(fun, args = NULL, package = NULL, lib.loc = NULL) {
# We cannot just call help normally because otherwise it thinks
# we are looking for package "package" so we create a call and eval it
help_call <- call("help", fun, lib.loc = lib.loc, help_type = "text")
if (!is.null(package)) help_call[["package"]] <- package
file <- eval(help_call)
# This is borrowed from utils::print.help_files_with_topic
path <- dirname(file)
dirpath <- dirname(path)
pkgname <- basename(dirpath)
RdDB <- file.path(path, pkgname)
if (!file.exists(paste(RdDB, "rdx", sep = ".")))
return(character(length(args)))
rd <- getNamespace("tools")$fetchRdDB(RdDB, basename(file))
# This is not exported from tools
rd_tags <- function(Rd) {
res <- sapply(Rd, attr, "Rd_tag")
if (!length(res)) res <- character(0)
return(res)
}
tags <- gsub("\\", "", rd_tags(rd), fixed = TRUE)
if (!any(tags == "arguments"))
return(character(length(args)))
arguments <- rd[[which(tags == "arguments")[1]]]
items <- arguments[rd_tags(arguments) == "\\item"]
descriptions <- do.call(rbind, lapply(items, function(item) {
names <- try(strsplit(item[[1]][[1]], "\\s*,\\s*", perl = TRUE)[[1]],
silent = TRUE)
if (inherits(names, "try-error")) {
# This happens with the "..." argument
names <- "..."
}
content <- paste(rapply(item[-1], as.character), collapse = "")
cbind(names, rep.int(content, length(names)))
}))
if (is.null(args)) {
structure(descriptions[, 2], names = descriptions[, 1])
} else {
sapply(args, function(a) {
if (a %in% descriptions[, 1]) {
descriptions[which(descriptions[, 1] == a)[1] , 2]
} else ""
})
}
}
#' @export
#' @rdname describe_function
args_tip <- function(name, only.args = FALSE, width = getOption("width")) {
# TODO: handle primitives and S3/S4 methods for generic functions
ret <- try(res <- eval(parse(text = paste0("argsAnywhere(", name, ")"))),
silent = TRUE)
if (inherits(ret, "try-error") || is.null(res))
return("") # Function 'name' not found
res <- deparse(res)
res <- paste(res[-length(res)], collapse = "\n")
if (isTRUE(only.args)) {
res <- sub("^function *[(]", "", res)
res <- sub(" *[)] *$", "", res)
} else {
res <- sub("^function *", name, res)
res <- sub(" *$", "", res)
}
# Reflow the tip
if (!is.null(width))
res <- paste(strwrap(res, width = width, exdent = 4), collapse = "\n")
res
}
#' @export
#' @rdname describe_function
call_tip <- function(code, only.args = FALSE, location = FALSE,
description = FALSE, methods = FALSE, width = getOption("width")) {
code <- attr(completion(code, types = NA, description = FALSE), "fguess")
if (is.null(code) || !length(code) || code == "")
return("")
# Get the corresponding call_tip
ctip <- args_tip(code, only.args = only.args, width = NULL) # Reflow later!
if (is.null(ctip))
return("")
# Do we need to append an indication about where this function is located?
if (isTRUE(location)) {
where <- res <-
eval(parse(text = paste0("getAnywhere(", code, ")")))$where[1]
if (!is.na(where) && where != ".GlobalEnv")
ctip <- paste0(ctip, " [", sub("^package:", "", where), "]")
}
# Reflow the tip now
if (!is.null(width))
ctip <- paste(strwrap(ctip, width = width, exdent = 4), collapse = "\n")
# Do we add the description of this function?
if (isTRUE(description)) {
desc <- describe_function(code)
if (!is.null(desc) && length(desc) && desc != "") {
if (!is.null(width))
desc <- paste(strwrap(desc, width = width), collapse = "\n")
ctip <- paste0(ctip, "\n\n", desc)
}
}
# Do we add a short mention of available methods if the function is generic?
if (isTRUE(methods)) {
mets <- list_methods(code)
if (length(mets)) {
# How many 25 char strings can we put on width and 5 lines max?
# Note: we use two space each time as separator, except for last
# line => take this into account in the calculation
if (is.null(width)) nitems <- 3 else nitems <- (width + 2) %/% 27
if (nitems < 1) nitems <- 1
# Make sure the list is not too long: restrict to nitems * 5 entries
if (length(mets) > nitems * 5) mets <- c(mets[1:(nitems * 5)], "...")
# Make sure each method description is not longer than 25 characters
n <- nchar(mets)
# Cut entries that are too long
tooLong <- n > 25
mets[tooLong] <- paste0(substr(mets[tooLong], 1, 22), "...")
# Paste strings together
mets <- paste0(format(mets, width = 25), c(rep(" ", nitems - 1), "\n"),
collapse = "")
# Add this info to the call_tip
ctip <- paste0(ctip,
"\n\nGeneric function with methods for the following classes:\n", mets)
}
}
ctip
}
# Backward compatibility
#' @export
#' @rdname describe_function
descFun <- describe_function
#' @export
#' @rdname describe_function
descArgs <- describe_args
#' @export
#' @rdname describe_function
argsTip <- args_tip
#' @export
#' @rdname describe_function
callTip <- call_tip
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.