我想从我的R源脚本中提取注释(与模式匹配),保持它们发生的函数。
目标是使用经典的标记复选框- [ ]
或- [x]
在函数体代码中编写文档注释,并提取这些注释作为字符向量列表进行进一步处理-我可以轻松地将其写入新的.md
文件。
可复制的示例和预期输出如下。
# preview the 'data'
script_body = c('# some init comment - not matching pattern','g = function(){','# - [x] comment_g1','# - [ ] comment_g2','1','}','f = function(){','# - [ ] comment_f1','# another non match to pattern','g()+1','}')
cat(script_body, sep = "n")
# # some init comment - not matching pattern
# g = function(){
# # - [x] comment_g1
# # - [ ] comment_g2
# 1
# }
# f = function(){
# # - [ ] comment_f1
# # another non match to pattern
# g()+1
# }
# populate R souce file
writeLines(script_body, "test.R")
# test it
source("test.R")
f()
# [1] 2
# expected output
r = magic_function_get_comments("test.R", starts.with = c(" - [x] "," - [ ] "))
# r = list("g" = c(" - [x] comment_g1"," - [ ] comment_g2"), "f" = " - [ ] comment_f1")
str(r)
# List of 2
# $ g: chr [1:2] " - [x] comment_g1" " - [ ] comment_g2"
# $ f: chr " - [ ] comment_f1"
这是hrbmstr所做的一个简化的、未求值的变体:
get_comments = function (filename) {
is_assign = function (expr)
as.character(expr) %in% c('<-', '<<-', '=', 'assign')
is_function = function (expr)
is.call(expr) && is_assign(expr[[1]]) && is.call(expr[[3]]) && expr[[3]][[1]] == quote(`function`)
source = parse(filename, keep.source = TRUE)
functions = Filter(is_function, source)
fun_names = as.character(lapply(functions, `[[`, 2))
setNames(lapply(attr(functions, 'srcref'), grep,
pattern = '^\s*#', value = TRUE), fun_names)
}
这有一个警告:因为我们不评估源代码,我们可能会错过函数定义(例如,我们不会找到f = local(function (x) x)
)。上面的函数使用一个简单的启发式方法来查找函数定义(它查看function
表达式对变量的所有简单赋值)。
这只能使用eval
(或source
)来修复,它有自己的警告-例如,执行来自未知来源的文件是一种安全风险。
不太可能有人会为您编写grep
/stringr::str_match
部分(这不是一个grunt代码编写服务)。但是,迭代解析函数源的习惯用法可能对更广泛的受众足够有用,因此值得将其包含在内。
注意此source()
是.R
文件,这意味着它对进行求值。
#' Extract whole comment lines from an R source file
#'
#' code{source()} an R source file into a temporary environment then
#' iterate over the source of code{function}s in that environment and
#' code{grep} out the whole line comments which can then be further
#' processed.
#'
#' @param source_file path name to source file that code{source()} will accept
extract_comments <- function(source_file) {
tmp_env <- new.env(parent=sys.frame())
source(source_file, tmp_env, echo=FALSE, print.eval=FALSE, verbose=FALSE, keep.source=TRUE)
funs <- Filter(is.function, sapply(ls(tmp_env), get, tmp_env))
lapply(funs, function(f) {
# get function source
function_source <- capture.output(f)
# only get whole line comment lines
comments <- grep("^[[:blank:]]*#", function_source, value=TRUE)
# INCANT YOUR GREP/REGEX MAGIC HERE
# instead of just returning the comments
# since this isn't a free code-writing service
comments
})
}
str(extract_comments("test.R"))
## List of 2
## $ f: chr [1:2] "# - [ ] comment_f1" "# another non match to pattern"
## $ g: chr [1:2] "# - [x] comment_g1" "# - [ ] comment_g2"