Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: tinyplot
Type: Package
Title: Lightweight Extension of the Base R Graphics System
Version: 0.4.1
Version: 0.4.1.99
Date: 2025-06-02
Authors@R:
c(
Expand Down
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ _If you are viewing this file on CRAN, please check the
[latest NEWS](https://grantmcdermott.com/tinyplot/NEWS.html) on our website
where the formatting is also better._

## Development

### Bug fixes

- Safer handling of pre-plot hooks. Resolves an issue affecting how `tinyplot`
behaves inside loops, particularly for themed plots where only the final plot
was being drawn in Quarto/RMarkdown contexts. Special thanks to @hadley and @cderv
for helping us to debug. (@vincentarelbundock #425)

## 0.4.1

### Bug fixes
Expand Down
14 changes: 14 additions & 0 deletions R/environment.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
init_environment = function() {
tnypltptns = parent.env(environment())
assign(".tinyplot_env", new.env(), envir = tnypltptns)
.tpar = new.env()
assign(".tpar", .tpar, envir = tnypltptns)
}

get_environment_variable = function(name) {
get(name, envir = get(".tinyplot_env", envir = parent.env(environment())))
}

set_environment_variable = function(name, value) {
assign(name, value, envir = get(".tinyplot_env", envir = parent.env(environment())))
}
36 changes: 36 additions & 0 deletions R/hooks.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copied from https://raw.githubusercontent.com/r-lib/evaluate/refs/heads/main/R/hooks.R
# license: MIT + file LICENSE



#' Set and remove hooks
#'
#' This interface wraps the base [setHook()] function to provide a return
#' value that makes it easy to undo.
#'
#' @param hooks a named list of hooks - each hook can either be a function or
#' a list of functions.
#' @param action `"replace"`, `"append"` or `"prepend"`
#' @keywords internal
set_hooks <- function(hooks, action = "append") {
old <- list()
for (hook_name in names(hooks)) {
old[[hook_name]] <- getHook(hook_name)
setHook(hook_name, hooks[[hook_name]], action = action)
}
invisible(old)
}

#' @rdname set_hooks
#' @keywords internal
remove_hooks <- function(hooks) {
for (hook_name in names(hooks)) {
hook <- getHook(hook_name)
if (length(hook) > 0) {
for (fun in unlist(hooks[hook_name])) {
hook[sapply(hook, identical, fun)] <- NULL
}
}
setHook(hook_name, hook, "replace")
}
}
3 changes: 2 additions & 1 deletion R/tinytheme.R
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ tinytheme = function(
# for default theme, we want to revert the original pars and turn off the
# before.new.plot hook (otherwise manual par(x = y) changes won't work)
tpar(settings, hook = FALSE)
setHook("before.new.plot", NULL, "replace")
old_hooks = get_environment_variable(".tpar_hooks")
remove_hooks(old_hooks)
} else {
tpar(settings, hook = TRUE)
}
Expand Down
16 changes: 10 additions & 6 deletions R/tpar.R
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ tpar = function(..., hook = FALSE) {
base_par = opts[base_par]
if (length(base_par) > 0) {
if (isTRUE(hook)) {
setHook("before.plot.new", function() par(base_par), action = "replace")
# append new hook to existing ones
new_hooks = list("before.plot.new" = function() par(base_par))
set_hooks(new_hooks, action = "append")
# save new hook to tinyplot environment for later removal
old_hooks = get_environment_variable(".tpar_hooks")
set_environment_variable(".tpar_hooks", c(old_hooks, new_hooks))
} else {
par_names = names(par(no.readonly = TRUE))
base_par = base_par[names(base_par) %in% par_names]
Expand Down Expand Up @@ -310,11 +315,10 @@ init_tpar = function(rm_hook = FALSE) {
rm(list = names(.tpar), envir = .tpar)

if (isTRUE(rm_hook)) {
hook = getHook("before.plot.new")
if (length(hook) > 0) {
# need weird function because of Quarto evaluate::evaluate failure
# setHook("before.plot.new", NULL, action = "replace")
setHook("before.plot.new", function() NULL, action = "replace")
old_hooks = get_environment_variable(".tpar_hooks")
if (length(old_hooks) > 0) {
remove_hooks(old_hooks)
set_environment_variable(".tpar_hooks", NULL)
}
}

Expand Down
17 changes: 6 additions & 11 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,13 @@
.onLoad = function(libname, pkgname) {
# https://stackoverflow.com/questions/12598242/global-variables-in-packages-in-r
# https://stackoverflow.com/questions/49056642/r-how-to-make-variable-available-to-namespace-at-loading-time?noredirect=1&lq=1
tnypltptns = parent.env(environment())
assign(".tinyplot_env", new.env(), envir = tnypltptns)
.tpar = new.env()

assign(".tpar", .tpar, envir = tnypltptns)

init_environment()
init_tpar()

assign(".saved_par_before", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))
assign(".saved_par_after", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))
assign(".saved_par_first", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))
assign(".last_call", NULL, envir = get(".tinyplot_env", envir = parent.env(environment())))
set_environment_variable(".saved_par_before", NULL)
set_environment_variable(".saved_par_after", NULL)
set_environment_variable(".saved_par_first", NULL)
set_environment_variable(".last_call", NULL)
set_environment_variable(".tpar_hooks", NULL)

globalVariables(c(
"add",
Expand Down
22 changes: 22 additions & 0 deletions man/set_hooks.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.