构建一个 RStudio 插件来调试管道链
Build a RStudio addin to debug pipe chains
我编写了一个函数来帮助逐步执行管道链。
要使用它,用户必须将指令复制到剪贴板,然后执行函数,然后移至控制台继续。
我想构建一个插件,允许我 select 使用 Ctrl + P
指令和 运行 函数,而无需笨拙的步骤。
理想情况下,插件将:
- 捕获select离子
- 运行函数
- 将光标移动到控制台
- 由Ctrl + P
触发
我相信它与 reprex 插件所做的非常相似,但我不知道从哪里开始,因为我是 100% 的插件新手。
我调查了 rstudioapi::getActiveDocumentContext()
,但没有我感兴趣的内容。
我怎样才能完成这项工作?
函数
debug_pipe <- function(.expr){
.pchain <-
if (missing(.expr)) readClipboard() # windows only , else try clipr::read_clip()
else deparse(substitute(.expr))
.lhs <- if (grepl("^\s*[[:alnum:]_.]*\s*<-",.pchain[1])) {
sub("^\s*([[:alnum:]_.]*)\s*<-.*","\1",.pchain[1])
} else NA
.pchain <- sub("[^%]*<-\s*","",.pchain) # remove lhs of assignment if exists
.pchain <- paste(.pchain,collapse = " ") # collapse
.pchain <- gsub("\s+"," ",.pchain) # multiple spaces to single
.pchain <- strsplit(.pchain,"\s*%>%\s*")[[1]] # split by pipe
.pchain <- as.list(.pchain)
for (i in rev(seq_along(.pchain))) {
# function to count matches
.f <- function(x) sum(gregexpr(x,.pchain[i],fixed = TRUE)[[1]] != -1)
# check if unbalanced operators
.balanced <-
all(c(.f("{"),.f("("),.f("[")) == c(.f("}"),.f(")"),.f("]"))) &
!.f("'") %% 2 &
!.f('"') %% 2
if (!.balanced) {
# if unbalanced, combine with previous
.pchain[[i - 1]] <- paste(.pchain[[i - 1]],"%>%", .pchain[[i]])
.pchain[[i]] <- NULL
}
}
.calls <- Reduce( # build calls to display
function(x,y) paste0(x," %>%\n ",y),
.pchain, accumulate = TRUE)
.xinit <- eval(parse(text = .pchain[1]))
.values <- Reduce(function(x,y){ # compute all values
if (inherits(x,"try-error")) NULL
else try(eval(parse(text = paste("x %>%", y))),silent = TRUE)},
.pchain[-1], .xinit, accumulate = TRUE)
message("press enter to show, 's' to skip, 'q' to quit, lhs can be accessed with `.`")
for (.i in (seq_along(.pchain))) {
cat("\n",.calls[.i])
.rdl_ <- readline()
. <- .values[[.i]]
# while environment is explored
while (!.rdl_ %in% c("q","s","")) {
# if not an assignment, should be printed
if (!grepl("^\s*[[:alnum:]_.]*\s*<-",.rdl_)) .rdl_ <- paste0("print(",.rdl_,")")
# wrap into `try` to safely fail
try(eval(parse(text = .rdl_)))
.rdl_ <- readline()
}
if (.rdl_ == "q") return(invisible(NULL))
if (.rdl_ != "s") {
if (inherits(.values[[.i]],"try-error")) {
# a trick to be able to use stop without showing that
# debug_pipe failed in the output
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
message(.values[[.i]])
stop()
} else
{
print(.)
}
}
}
if (!is.na(.lhs)) assign(.lhs,tail(.values,1),envir = parent.frame())
invisible(NULL)
}
示例代码:
library(dplyr)
# copy following 4 lines to clipboard, no need to execute
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
debug_pipe()
# or wrap expression
debug_pipe(
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
)
以下是我的步骤:
两个很好的资源是:
1。创建一个新包
新 Project/R package/Name 包为 pipedebug
2。构建 R 文件
将函数的代码放入 R
文件夹中的 .R
文件中。我们重命名函数 pdbg
因为我意识到 magrittr
已经有一个名为 debug_pipe
的函数,它做了一些不同的事情(它执行浏览器和 returns 输入)。
我们必须添加第二个函数,不带参数,插件将触发该函数,我们可以随意命名它:
pdbg_addin <- function(){
selection <- rstudioapi::primary_selection(
rstudioapi::getSourceEditorContext())[["text"]]
rstudioapi::sendToConsole("",execute = F)
eval(parse(text=paste0("pdbg(",selection,")")))
}
第一行捕获了 selection,改编自 reprex
的代码。
第二行是向控制台发送一个空字符串并且不执行它,这就是我找到的所有移动光标的方法,但可能有更好的方法。
第三行是运行以selection作为参数的主函数。
3。创建 dcf 文件
下一步是创建包含以下内容的文件 inst/rstudio/addins.dcf
:
Name: debug pipe
Description: debug pipes step by step
Binding: pdbg_addin
Interactive: false
usethis::use_addin("pdbg_addin")
将创建文件,用模板填充它并打开它以便您可以编辑它。
4。构建包
Ctrl+Shift+B
5。添加快捷方式
工具/插件/浏览插件/键盘快捷键/调试管道/Ctrl+P
6.测试一下
在文本编辑器中复制/select/Ctrl+P
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
找一个粗略的版本here:
devtools::install_github("moodymudskipper/pipedebug")
?pdbg
类似的努力:
@Alistaire did this and advertised this other effort 在他的页面上。
我编写了一个函数来帮助逐步执行管道链。
要使用它,用户必须将指令复制到剪贴板,然后执行函数,然后移至控制台继续。
我想构建一个插件,允许我 select 使用 Ctrl + P
指令和 运行 函数,而无需笨拙的步骤。
理想情况下,插件将:
- 捕获select离子
- 运行函数
- 将光标移动到控制台
- 由Ctrl + P 触发
我相信它与 reprex 插件所做的非常相似,但我不知道从哪里开始,因为我是 100% 的插件新手。
我调查了 rstudioapi::getActiveDocumentContext()
,但没有我感兴趣的内容。
我怎样才能完成这项工作?
函数
debug_pipe <- function(.expr){
.pchain <-
if (missing(.expr)) readClipboard() # windows only , else try clipr::read_clip()
else deparse(substitute(.expr))
.lhs <- if (grepl("^\s*[[:alnum:]_.]*\s*<-",.pchain[1])) {
sub("^\s*([[:alnum:]_.]*)\s*<-.*","\1",.pchain[1])
} else NA
.pchain <- sub("[^%]*<-\s*","",.pchain) # remove lhs of assignment if exists
.pchain <- paste(.pchain,collapse = " ") # collapse
.pchain <- gsub("\s+"," ",.pchain) # multiple spaces to single
.pchain <- strsplit(.pchain,"\s*%>%\s*")[[1]] # split by pipe
.pchain <- as.list(.pchain)
for (i in rev(seq_along(.pchain))) {
# function to count matches
.f <- function(x) sum(gregexpr(x,.pchain[i],fixed = TRUE)[[1]] != -1)
# check if unbalanced operators
.balanced <-
all(c(.f("{"),.f("("),.f("[")) == c(.f("}"),.f(")"),.f("]"))) &
!.f("'") %% 2 &
!.f('"') %% 2
if (!.balanced) {
# if unbalanced, combine with previous
.pchain[[i - 1]] <- paste(.pchain[[i - 1]],"%>%", .pchain[[i]])
.pchain[[i]] <- NULL
}
}
.calls <- Reduce( # build calls to display
function(x,y) paste0(x," %>%\n ",y),
.pchain, accumulate = TRUE)
.xinit <- eval(parse(text = .pchain[1]))
.values <- Reduce(function(x,y){ # compute all values
if (inherits(x,"try-error")) NULL
else try(eval(parse(text = paste("x %>%", y))),silent = TRUE)},
.pchain[-1], .xinit, accumulate = TRUE)
message("press enter to show, 's' to skip, 'q' to quit, lhs can be accessed with `.`")
for (.i in (seq_along(.pchain))) {
cat("\n",.calls[.i])
.rdl_ <- readline()
. <- .values[[.i]]
# while environment is explored
while (!.rdl_ %in% c("q","s","")) {
# if not an assignment, should be printed
if (!grepl("^\s*[[:alnum:]_.]*\s*<-",.rdl_)) .rdl_ <- paste0("print(",.rdl_,")")
# wrap into `try` to safely fail
try(eval(parse(text = .rdl_)))
.rdl_ <- readline()
}
if (.rdl_ == "q") return(invisible(NULL))
if (.rdl_ != "s") {
if (inherits(.values[[.i]],"try-error")) {
# a trick to be able to use stop without showing that
# debug_pipe failed in the output
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
message(.values[[.i]])
stop()
} else
{
print(.)
}
}
}
if (!is.na(.lhs)) assign(.lhs,tail(.values,1),envir = parent.frame())
invisible(NULL)
}
示例代码:
library(dplyr)
# copy following 4 lines to clipboard, no need to execute
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
debug_pipe()
# or wrap expression
debug_pipe(
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
)
以下是我的步骤:
两个很好的资源是:
1。创建一个新包
新 Project/R package/Name 包为 pipedebug
2。构建 R 文件
将函数的代码放入 R
文件夹中的 .R
文件中。我们重命名函数 pdbg
因为我意识到 magrittr
已经有一个名为 debug_pipe
的函数,它做了一些不同的事情(它执行浏览器和 returns 输入)。
我们必须添加第二个函数,不带参数,插件将触发该函数,我们可以随意命名它:
pdbg_addin <- function(){
selection <- rstudioapi::primary_selection(
rstudioapi::getSourceEditorContext())[["text"]]
rstudioapi::sendToConsole("",execute = F)
eval(parse(text=paste0("pdbg(",selection,")")))
}
第一行捕获了 selection,改编自 reprex
的代码。
第二行是向控制台发送一个空字符串并且不执行它,这就是我找到的所有移动光标的方法,但可能有更好的方法。
第三行是运行以selection作为参数的主函数。
3。创建 dcf 文件
下一步是创建包含以下内容的文件 inst/rstudio/addins.dcf
:
Name: debug pipe
Description: debug pipes step by step
Binding: pdbg_addin
Interactive: false
usethis::use_addin("pdbg_addin")
将创建文件,用模板填充它并打开它以便您可以编辑它。
4。构建包
Ctrl+Shift+B
5。添加快捷方式
工具/插件/浏览插件/键盘快捷键/调试管道/Ctrl+P
6.测试一下
在文本编辑器中复制/select/Ctrl+P
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
找一个粗略的版本here:
devtools::install_github("moodymudskipper/pipedebug")
?pdbg
类似的努力:
@Alistaire did this and advertised this other effort 在他的页面上。