Build a RStudio addin to debug pipe chains
Build a RStudio addin to debug pipe chains
I wrote a function that helps executing pipe chains step by step.
To use it the users has to copy the instruction to clipboard, then execute the function, and move to the console to proceed.
I would like to build an addin that would allow me to select the instructions and run the function with Ctrl + P without the awkward steps.
Ctrl + P
Ideally, the addin would :
I believe it's extremely similar to what the reprex addin is doing but I don't know where to start as I'm 100% new to addins.
I looked into rstudioapi::getActiveDocumentContext() but there was nothing there of interest to me.
rstudioapi::getActiveDocumentContext()
How can I make this work ?
The function
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)
}
Example code:
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)
)
This question has not received enough attention.
No need to dive into the function's code, it's only there for reproducibility. I just need to implement the steps laid out at the top.
debug(`%>%`)
it's not the same at all, here you're just entering the
%>% function, where you can indeed explore or print the lhs object but it's a pain. With my function by hitting just enter for each step you see all your data transformation happening, this was my main desire, and then I added a quick debugging feature because it wasn't too hard to do.– Moody_Mudskipper
Jul 2 at 0:42
%>%
lhs
Right, I see. Even in a long chain with many expressions separated by
%>%, the function is only called once, so debug(`%>%`) would not be very helpful.– user2554330
Jul 2 at 11:22
%>%
debug(`%>%`)
magrittr's approach with their own
debug_pipe function is to do ` foo %>% {browser();x}` (`browser();x}``is the actual code), so you have access to the standard debugging tools, but you need to manually type it after each step and then print manually the result, that's not great for my main use cases.– Moody_Mudskipper
Jul 2 at 11:56
debug_pipe
Not to waste my bounty, I'll give it to an answer that would give a good criticism of my code and suggestions or alternatives, maybe I use
eval(parse(text too much and there's a way around it, maybe I can leverage the debug function instead of imitating it. Because it removes double spaces naively at the time it also can mess with some strings, I'm not sure how I can leave those alone. Of course I'll welcome as well an alternative answer not related to mine or to my code, anything that would add value to the topic.– Moody_Mudskipper
4 hours ago
eval(parse(text
debug
1 Answer
1
Here are the steps I came with :
Two good ressources were :
1. create a new package
New Project/R package/Name package as pipedebug
pipedebug
2. build R file
Put the function's code into a .R file in the R folder. We rename the function pdbg as I realised that magrittr already has a function called debug_pipe that does something different (it executes browser and returns input).
.R
R
pdbg
magrittr
debug_pipe
We must add a second function, without parameter, that the addin will trigger, we can name it however we want:
pdbg_addin <- function(){
selection <- rstudioapi::primary_selection(
rstudioapi::getSourceEditorContext())[["text"]]
rstudioapi::sendToConsole("",execute = F)
eval(parse(text=paste0("pdbg(",selection,")")))
}
The first line captures the selection, adapted from reprex's code.
reprex
The second line is sending an empty string to the console and not executing it, that's all I found to move the cursor, but there might be a better way.
The third line is running the main function with the selection as an argument. I used eval(parse(text( because I don't understand how to do it otherwise, but I suppose it's evil.
eval(parse(text(
3. Create dcf file
Next step is to create file inst/rstudio/addins.df with following content:
inst/rstudio/addins.df
Name: debug pipe
Description: debug pipes step by step
Binding: pdbg_addin
Interactive: false
4. build package
Ctrl+Shift+B
5. Add shortcut
Tools / addins / browse addins / keyboard shortcuts / debug pipe / Ctrl+P
6. Test it
Copy in text editor / select / Ctrl+P
test <- iris %>%
slice(1:2) %>%
select(1:3) %>%
mutate(x=3)
find a rough version here:
devtools::install_github("moodymudskipper/pipedebug")
?pdbg
similar efforts:
@Alistaire did this and advertised this other effort on his page.
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Why not just
debug(`%>%`)?– user2554330
Jul 2 at 0:12