Skip to content
Open
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
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ Collate:
'utilities-break.R'
'utilities-grid.R'
'utilities-help.R'
'utilities-lifecycle.R'
'utilities-patterns.R'
'utilities-performance.R'
'utilities-resolution.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ export(scale_y_reverse)
export(scale_y_sqrt)
export(scale_y_time)
export(sec_axis)
export(set_edition)
export(set_last_plot)
export(set_theme)
export(sf_transform_xy)
Expand Down
2 changes: 1 addition & 1 deletion R/annotation-logticks.R
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ annotation_logticks <- function(base = 10, sides = "bl", outside = FALSE, scaled
if (!is.null(color))
colour <- color

lifecycle::signal_stage("superseded", "annotation_logticks()", "guide_axis_logticks()")
supersede("2026", "annotation_logticks()", "guide_axis_logticks()")

if (lifecycle::is_present(size)) {
deprecate("3.5.0", I("Using the `size` aesthetic in this geom"), I("`linewidth`"))
Expand Down
2 changes: 1 addition & 1 deletion R/coord-flip.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
#' geom_area() +
#' coord_flip()
coord_flip <- function(xlim = NULL, ylim = NULL, expand = TRUE, clip = "on") {
lifecycle::signal_stage("superseded", "coord_flip()")
supersede("2026", "coord_flip()", with = I("swapping x and y aesthetics"))
check_coord_limits(xlim)
check_coord_limits(ylim)
ggproto(NULL, CoordFlip,
Expand Down
2 changes: 1 addition & 1 deletion R/coord-map.R
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ coord_map <- function(projection="mercator", ..., parameters = NULL, orientation
} else {
params <- parameters
}
lifecycle::signal_stage("superseded", "coord_map()", "coord_sf()")
supersede("2026", "coord_map()", "coord_sf()")
check_coord_limits(xlim)
check_coord_limits(ylim)

Expand Down
2 changes: 1 addition & 1 deletion R/coord-polar.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
coord_polar <- function(theta = "x", start = 0, direction = 1, clip = "on") {
theta <- arg_match0(theta, c("x", "y"))
r <- if (theta == "x") "y" else "x"
lifecycle::signal_stage("superseded", "coord_polar()", "coord_radial()")
supersede("2026", "coord_polar()", "coord_radial()")

ggproto(NULL, CoordPolar,
theta = theta,
Expand Down
2 changes: 2 additions & 0 deletions R/ggplot-global.R
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ ggplot_global$x_aes <- c("x", "xmin", "xmax", "xend", "xintercept",

ggplot_global$y_aes <- c("y", "ymin", "ymax", "yend", "yintercept",
"ymin_final", "ymax_final", "lower", "middle", "upper", "y0")

ggplot_global$edition <- NULL
6 changes: 3 additions & 3 deletions R/labels.R
Original file line number Diff line number Diff line change
Expand Up @@ -239,21 +239,21 @@ labs <- function(..., title = waiver(), subtitle = waiver(),
#' superseded. It is recommended to use the `labs(x, y, title, subtitle)`
#' arguments instead.
xlab <- function(label) {
lifecycle::signal_stage("superseded", "xlab()", "labs(x)")
supersede("2026", "xlab()", "labs(x)")
labs(x = label)
}

#' @rdname labs
#' @export
ylab <- function(label) {
lifecycle::signal_stage("superseded", "ylab()", "labs(y)")
supersede("2026", "ylab()", "labs(y)")
labs(y = label)
}

#' @rdname labs
#' @export
ggtitle <- function(label, subtitle = waiver()) {
lifecycle::signal_stage("superseded", "ggtitle()", I("labs(title, subtitle)"))
supersede("2026", "ggtitle()", I("labs(title, subtitle)"))
labs(title = label, subtitle = subtitle)
}

Expand Down
3 changes: 2 additions & 1 deletion R/limits.R
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ limits.POSIXlt <- function(lims, var, call = caller_env()) {
expand_limits <- function(...) {
data <- list2(...)

lifecycle::signal_stage("superseded", "expand_limits()")

supersede("2026", "expand_limits()")

# unpack data frame columns
data_dfs <- vapply(data, is.data.frame, logical(1))
Expand Down
108 changes: 108 additions & 0 deletions R/utilities-lifecycle.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#' Set ggplot2 edition
#'
#' ggplot2 uses the 'edition' concept to manage the lifecycles of functions and
#' arguments. Setting a recent edition opens up the latest features but also
#' closes down deprecated and superseded functionality.
#'
#' @param edition An edition. Possible values currently include `"2026"` only.
#' Can be `NULL` (default) to unset an edition.
#'
#' @returns The previous `edition` value. This function is called for the side
#' effect of setting the edition though.
#' @export
#' @keywords internal
#'
#' @examples
#' set_edition(2026)
set_edition <- function(edition = NULL) {
old <- ggplot_global$edition
ggplot_global$edition <- validate_edition(edition)
invisible(old)
}

get_edition <- function() {
ggplot_global$edition[[1]]
}

# Any new editions should be appended here and anchored to a version
edition_versions <- c(
"2025" = "4.0.0",
"2026" = "4.1.0"
)

validate_edition <- function(edition, allow_null = TRUE, call = caller_env()) {
if (is.null(edition) && allow_null) {
return(NULL)
}
edition <- as.character(edition)
check_string(edition, allow_empty = FALSE, call = call)
arg_match0(edition, names(edition_versions), error_call = call)
}

edition_require <- function(edition = NULL, what, call = caller_env()) {
edition <- validate_edition(edition)
current_edition <- get_edition()
if (
!is.null(current_edition) &&
as.numeric(current_edition) >= as.numeric(edition)
) {
return(invisible())
}
cli::cli_abort(
"{what} requires the {edition} edition of {.pkg ggplot2}.",
call = call
)
}

deprecate <- function(when, ..., id = NULL, always = FALSE, user_env = NULL,
escalate = NULL) {

defunct <- "3.0.0"
full <- "3.4.0"
soft <- utils::packageVersion("ggplot2")

if (identical(escalate, "delay")) {
soft <- full
full <- defunct
defunct <- "0.0.0"
}

edition <- get_edition()
if (!is.null(edition) && edition %in% names(edition_versions)) {
soft <- full <- defunct <- edition_versions[[edition]]
}

version <- as.package_version(when)
if (version <= defunct || identical(escalate, "abort")) {
lifecycle::deprecate_stop(when, ...)
}
user_env <- user_env %||% getOption("ggplot2_plot_env") %||% caller_env(2)
if (version <= full || identical(escalate, "warn")) {
lifecycle::deprecate_warn(when, ..., id = id, always = always, user_env = user_env)
} else if (version <= soft) {
lifecycle::deprecate_soft(when, ..., id = id, user_env = user_env)
}
invisible()
}

supersede <- function(edition, what, with = NULL, ..., env = caller_env()) {
current_edition <- get_edition()
if (
!is.null(current_edition) &&
current_edition %in% names(edition_versions) &&
as.numeric(current_edition) >= as.numeric(edition)
) {
lifecycle::deprecate_stop(
when = paste0("edition ", edition),
what = what,
with = with,
env = env
)
}
lifecycle::signal_stage(
stage = "superseded",
what = what,
with = with,
env = env
)
}
26 changes: 0 additions & 26 deletions R/utilities.R
Original file line number Diff line number Diff line change
Expand Up @@ -789,32 +789,6 @@ as_cli <- function(..., env = caller_env()) {
cli::cli_fmt(cli::cli_text(..., .envir = env))
}

deprecate <- function(when, ..., id = NULL, always = FALSE, user_env = NULL,
escalate = NULL) {

defunct <- "3.0.0"
full <- "3.4.0"
soft <- utils::packageVersion("ggplot2")

if (identical(escalate, "delay")) {
soft <- full
full <- defunct
defunct <- "0.0.0"
}

version <- as.package_version(when)
if (version < defunct || identical(escalate, "abort")) {
lifecycle::deprecate_stop(when, ...)
}
user_env <- user_env %||% getOption("ggplot2_plot_env") %||% caller_env(2)
if (version <= full || identical(escalate, "warn")) {
lifecycle::deprecate_warn(when, ..., id = id, always = always, user_env = user_env)
} else if (version <= soft) {
lifecycle::deprecate_soft(when, ..., id = id, user_env = user_env)
}
invisible()
}

as_unordered_factor <- function(x) {
x <- as.factor(x)
class(x) <- setdiff(class(x), "ordered")
Expand Down
25 changes: 25 additions & 0 deletions man/set_edition.Rd

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

39 changes: 39 additions & 0 deletions tests/testthat/_snaps/utilities-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# editions can be set and unset

Code
set_edition("nonsense")
Condition
Error in `set_edition()`:
! `edition` must be one of "2025" or "2026", not "nonsense".

# edition deprecation works

`foo()` was deprecated in ggplot2 4.0.0.
i Please use `bar()` instead.

---

Code
foo()
Condition
Error:
! `foo()` was deprecated in ggplot2 4.0.0 and is now defunct.
i Please use `bar()` instead.

# edition supersession works

Code
foo()
Condition
Error:
! `foo()` was deprecated in <NA> edition 2025 and is now defunct.
i Please use `bar()` instead.

# edition requirements work

Code
foo()
Condition
Error in `foo()`:
! foo() requires the 2025 edition of ggplot2.

56 changes: 56 additions & 0 deletions tests/testthat/test-utilities-lifecycle.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
test_that("editions can be set and unset", {

x <- set_edition(2026)
expect_null(x) # Set edition returns old value
expect_equal(get_edition(), "2026")

x <- set_edition(NULL)
expect_equal(x, "2026")
expect_equal(get_edition(), NULL)

# Test invalid values
expect_snapshot(
set_edition("nonsense"),
error = TRUE
)
})

test_that("edition deprecation works", {
foo <- function() {
deprecate("4.0.0", what = "foo()", with = "bar()")
}
expect_snapshot_warning(foo())

set_edition(2025)
withr::defer(set_edition())

expect_snapshot(foo(), error = TRUE)
})

test_that("edition supersession works", {
foo <- function() {
supersede("2025", what = "foo()", with = "bar()")
NULL
}
expect_silent(foo())

set_edition(2025)
withr::defer(set_edition())

expect_snapshot(foo(), error = TRUE)
})

test_that("edition requirements work", {

foo <- function() {
edition_require("2025", what = "foo()")
NULL
}

expect_snapshot(foo(), error = TRUE)

set_edition(2025)
withr::defer(set_edition())

expect_silent(foo())
})
Loading