有几个代码片段对我的工作流程非常宝贵,并且与我自定义的R包中的函数很好地配合使用。我可以把这些代码片段包含在我的R包中,这样当用户安装我的包时,它们就会被添加到用户的代码片段中(当然需要获得允许)。
创建一个SQL块的Rmd代码片段示例:
snippet sql
```{sql, connection = conn, output.var = "${1:df}"}
${2}
```
有几个代码片段对我的工作流程非常宝贵,并且与我自定义的R包中的函数很好地配合使用。我可以把这些代码片段包含在我的R包中,这样当用户安装我的包时,它们就会被添加到用户的代码片段中(当然需要获得允许)。
创建一个SQL块的Rmd代码片段示例:
snippet sql
```{sql, connection = conn, output.var = "${1:df}"}
${2}
```
简短回答:是的
实现你想要的方式(适用于我的软件包)之一是:
在软件包的inst/
目录中的某个地方存储包裹片段定义的两个文本文件。重要的是,片段要完全遵循格式规则(例如,行首使用制表符而不是空格)。我有一个用于R代码片段的文件和一个用于Markdown的文件。
定义一个函数,它读取这些文件并将它们的内容复制到RStudio的用户片段文件中。这些文件在第一次尝试编辑片段时生成(Tools -> Global Options -> Code -> Edit Snippets)(我认为RStudio在尝试编辑之前使用其他文件,而非用户公开的文件。)。在ubuntu上,RStudio的文件名为'r.snippets'和'markdown.snippets',位于'~/.R/snippets/'中。我还会检查片段定义是否已存在,并在使用cat(..., append=TRUE)
添加软件包片段定义之前再次检查行首的制表符。
我最初使用了一个包含配置等所有内容的复杂.onLoad
函数,但现在我只导出一个addPackageSnippets
函数;)
一些代码:
检查已经存在的片段定义的部分:
我只是读取rstudio文件并提取以“snippet”开头的行。我对软件包片段定义文件执行相同的操作,并使用setdiff
(人们可能还想对列表使用trimws
,以防存在一些尾随的空格)。
# load package snippets definitions
#
pckgSnippetsFileContent <- readLines(pckgSnippetsFilesPath)
# Extract names of package snippets
#
pckgSnippetsFileDefinitions <- pckgSnippetsFileContent[grepl("^snippet (.*)", pckgSnippetsFileContent)]
# Extract 'names' of already existing snitppets
#
rstudioSnippetsFileContent <- readLines(rstudioSnippetsFilePath)
rstudioSnippetDefinitions <- rstudioSnippetsFileContent[grepl("^snippet (.*)", rstudioSnippetsFileContent)]
# find definitions appearing in packageSnippets but not in rstudioSnippets
# if no snippets are missing go to next file
#
snippetsToCopy <- setdiff(pckgSnippetsFileDefinitions, rstudioSnippetDefinitions)
为了提供背景信息,这是整个“addPackageSnippets”函数。该函数仅使用基本软件包,除了getOS
之外,它返回“linux”,“windows”或“mac”中的一个(即Sys.info()
的包装器)。
#' @title Export snippets
#'
#' @description \code{addPackageSnippets} copies all (missing) snippet definitions
#' in 'inst/rstudio/Rsnippets.txt' and 'Rmdsnippets.txt' to the RStudios user snippet location.
#'
#' @return boolean invisible(FALSE) if nothing was added, invisible(TRUE) if snipped definitions were added
#' @export
#'
#' @examples \dontrun{addPackageSnippets()}
addPackageSnippets <- function() {
added <- FALSE
# if not on RStudio or RStudioServer exit
#
if (!nzchar(Sys.getenv("RSTUDIO_USER_IDENTITY"))) {
return(NULL)
}
# Name of files containing snippet code to copy
#
pckgSnippetsFiles <- c("Rsnippets.txt", "Rmdsnippets.txt")
# Name of files to copy into. Order has to be the same
# as in 'pckgSnippetsFiles'
#
rstudioSnippetsFiles <- c("r.snippets", "markdown.snippets")
# Path to directory for RStudios user files depends on OS
#
if (getOS() == "linux") {
rstudioSnippetsPathBase <- "~/.R/snippets"
} else if (getOS() == "windows") {
rstudioSnippetsPathBase <- file.path(path.expand('~'), ".R", "snippets")
} else {
warning(paste0("goSnippets() is only implemented on linux and windows"))
return(NULL)
}
# Read each file in pckgSnippetsFiles and add its contents
#
for (i in seq_along(pckgSnippetsFiles)) {
# Try to get template, if template is not found skip it
#
pckgSnippetsFilesPath <- system.file("rstudio", pckgSnippetsFiles[i], package = "myFunc")
if (pckgSnippetsFilesPath == "") {
next()
}
# load package snippets definitions
#
pckgSnippetsFileContent <- readLines(pckgSnippetsFilesPath)
# Extract names of package snippets
#
pckgSnippetsFileDefinitions <- pckgSnippetsFileContent[grepl("^snippet (.*)", pckgSnippetsFileContent)]
# Construct path for destination file
#
rstudioSnippetsFilePath <- file.path(rstudioSnippetsPathBase, rstudioSnippetsFiles[i])
# If targeted RStudios user file does not exist, raise error (otherwise we would 'remove')
# the default snippets from the 'user file'
#
if (!file.exists(rstudioSnippetsFilePath)) {
stop(paste0( "'", rstudioSnippetsFilePath, "' does not exist yet\n.",
"Use RStudio -> Tools -> Global Options -> Code -> Edit Snippets\n",
"To initalize user defined snippets file by adding dummy snippet\n"))
}
# Extract 'names' of already existing snitppets
#
rstudioSnippetsFileContent <- readLines(rstudioSnippetsFilePath)
rstudioSnippetDefinitions <- rstudioSnippetsFileContent[grepl("^snippet (.*)", rstudioSnippetsFileContent)]
# replace two spaces with tab, ONLY at beginning of string
#
pckgSnippetsFileContentSanitized <- gsub("(?:^ {2})|\\G {2}|\\G\t", "\t", pckgSnippetsFileContent, perl = TRUE)
# find defintions appearing in packageSnippets but not in rstudioSnippets
# if no snippets are missing go to next file
#
snippetsToCopy <- setdiff(trimws(pckgSnippetsFileDefinitions), trimws(rstudioSnippetDefinitions))
snippetsNotToCopy <- intersect(trimws(pckgSnippetsFileDefinitions), trimws(rstudioSnippetDefinitions))
if (length(snippetsToCopy) == 0) {
# cat(paste0("(\nFollowing snippets will NOT be added because there is already a snippet with that name: ",
# paste0(snippetsNotToCopy, collapse=", ") ,")"))
next()
}
# Inform user about changes, ask to confirm action
#
if (interactive()) {
cat(paste0("You are about to add the following ", length(snippetsToCopy),
" snippets to '", rstudioSnippetsFilePath, "':\n",
paste0(paste0("-", snippetsToCopy), collapse="\n")))
if (length(snippetsNotToCopy) > 0) {
cat(paste0("\n(The following snippets will NOT be added because there is already a snippet with that name:\n",
paste0(snippetsNotToCopy, collapse=", ") ,")"))
}
answer <- readline(prompt="Do you want to procedd (y/n): ")
if (substr(answer, 1, 1) == "n") {
next()
}
}
# Create list of line numbers where snippet definitons start
# This list is used to determine the end of each definition block
#
allPckgSnippetDefinitonStarts <- grep("^snippet .*", pckgSnippetsFileContentSanitized)
for (s in snippetsToCopy) {
startLine <- grep(paste0("^", s, ".*"), pckgSnippetsFileContentSanitized)
# Find last line of snippet definition:
# First find start of next defintion and return
# previous line number or lastline if already in last definiton
#
endLine <- allPckgSnippetDefinitonStarts[allPckgSnippetDefinitonStarts > startLine][1] -1
if (is.na(endLine)) {
endLine <- length(pckgSnippetsFileContentSanitized)
}
snippetText <- paste0(pckgSnippetsFileContentSanitized[startLine:endLine], collapse = "\n")
# Make sure there is at least one empty line between entries
#
if (tail(readLines(rstudioSnippetsFilePath), n=1) != "") {
snippetText <- paste0("\n", snippetText)
}
# Append snippet block, print message
#
cat(paste0(snippetText, "\n"), file = rstudioSnippetsFilePath, append = TRUE)
cat(paste0("* Added '", s, "' to '", rstudioSnippetsFilePath, "'\n"))
added <- TRUE
}
}
if (added) {
cat("Restart RStudio to use new snippets")
}
return(invisible(added))
}
readLines()
内使用了warn=FALSE
,这样就不会收到不完整的最后一行警告信息了。 - DeanAttali对于任何看到这个帖子的人,为了补充Dario的很好的回答:从RStudio v1.3开始,文件路径已经更改。因此,在他的函数中,用于设置rstudioSnippetsPathBase
的部分需要更改为以下内容:
if (rstudioapi::versionInfo()$version < "1.3") {
rstudioSnippetsPathBase <- file.path(path.expand('~'),".R", "snippets")
} else {
if (.Platform$OS.type == "windows") {
rstudioSnippetsPathBase <- file.path(Sys.getenv("APPDATA"), "RStudio", "snippets")
} else {
rstudioSnippetsPathBase <- file.path(path.expand('~'), ".config/rstudio", "snippets")
}
}