1# nocov start
2keep_empty <- function(fun) {
3  function(x) {
4    ret <- rep_along(x, "")
5    update <- which(is.na(x) | x != "")
6    ret[update] <- fun(x[update])
7    ret
8  }
9}
10# nocov end
11
12#' Styling helpers
13#'
14#' Functions that allow implementers of formatters for custom data types to
15#' maintain a consistent style with the default data types.
16#'
17#' `style_subtle()` is affected by the `subtle` [option][pillar_options].
18#'
19#' @param x The character vector to style.
20#' @export
21#' @seealso [pillar_options] for a list of options
22#' @examples
23#' style_subtle("text")
24style_subtle <- keep_empty(function(x) {
25  force(x)
26  if (isTRUE(get_pillar_option_subtle())) {
27    crayon_grey_0.6(x)
28  } else {
29    x
30  }
31})
32
33#' @rdname style_subtle
34#' @details
35#' `style_subtle_num()` is affected by the
36#' `subtle_num` [option][pillar_options],
37#' which is `FALSE` by default.
38#'
39#' @export
40#' @examples
41#' style_subtle_num(0.01 * 1:3, c(TRUE, FALSE, TRUE))
42style_subtle_num <- function(x, negative) {
43  if (isTRUE(get_pillar_option_subtle_num())) {
44    style_subtle(x)
45  } else {
46    ifelse(negative, style_neg(x), x)
47  }
48}
49
50style_hint <- keep_empty(function(x) {
51  force(x)
52  if (isTRUE(get_pillar_option_subtle())) {
53    crayon_grey_0.8(x)
54  } else {
55    x
56  }
57})
58
59style_spark_na <- function(x) {
60  crayon_yellow(x)
61}
62
63#' @details
64#' `style_bold()` is affected by the `bold` [option][pillar_options],
65#' which is `FALSE` by default.
66#'
67#' @rdname style_subtle
68#' @export
69#' @examples
70#' style_bold("Petal.Width")
71style_bold <- keep_empty(function(x) {
72  if (isTRUE(get_pillar_option_bold())) {
73    crayon_bold(x)
74  } else {
75    x
76  }
77})
78
79#' @rdname style_subtle
80#' @export
81#' @examples
82#' style_na("NA")
83style_na <- function(x) {
84  crayon_red(x)
85}
86
87#' @details
88#' `style_neg()` is affected by the `pillar.neg` [option][pillar_options].
89#'
90#' @rdname style_subtle
91#' @export
92#' @examples
93#' style_neg("123")
94style_neg <- keep_empty(function(x) {
95  if (isTRUE(get_pillar_option_neg())) {
96    crayon_red(x)
97  } else {
98    x
99  }
100})
101
102pillar_na <- function(use_brackets_if_no_color = FALSE) {
103  if (use_brackets_if_no_color && !has_color()) {
104    "<NA>"
105  } else {
106    style_na("NA")
107  }
108}
109
110style_list <- function(x) {
111  style_subtle(x)
112}
113
114# Only check if we have color support once per session
115num_colors <- local({
116  num_colors <- NULL
117  function(forget = FALSE) {
118    if (is.null(num_colors) || forget) {
119      num_colors <<- crayon::num_colors(forget = forget)
120    }
121    num_colors
122  }
123})
124
125has_color <- function() {
126  num_colors() > 1
127}
128
129# nocov start
130# Crayon functions call crayon::num_colors() every call
131make_style_fast <- function(...) {
132  # Force has_color to be true when making styles
133  # Use prefix to avoid problems with empty NAMESPACE
134  rlang::local_options(cli.num_colors = 16L)
135
136  style_16 <- crayon::make_style(..., colors = 16)
137  start_16 <- stats::start(style_16)
138  finish_16 <- crayon::finish(style_16)
139
140  style_256 <- crayon::make_style(..., colors = 256)
141  start_256 <- stats::start(style_256)
142  finish_256 <- crayon::finish(style_256)
143
144  function(...) {
145    if (has_color()) {
146      colors <- num_colors()
147      if (colors >= 256) {
148        paste0(start_256, ..., finish_256)
149      } else {
150        paste0(start_16, ..., finish_16)
151      }
152    } else {
153      paste0(...)
154    }
155  }
156}
157
158# Placeholders, assigned in .onLoad()
159crayon_underline <- function(...) {}
160crayon_italic <- function(...) {}
161crayon_red <- function(...) {}
162crayon_yellow <- function(...) {}
163crayon_bold <- function(...) {}
164crayon_grey_0.6 <- function(...) {}
165crayon_grey_0.8 <- function(...) {}
166
167assign_crayon_styles <- function() {
168  crayon_underline <<- make_style_fast("underline")
169  crayon_italic <<- make_style_fast("italic")
170  crayon_red <<- make_style_fast("red")
171  crayon_yellow <<- make_style_fast("yellow")
172  crayon_bold <<- make_style_fast("bold")
173  crayon_grey_0.6 <<- make_style_fast(grDevices::grey(0.6), grey = TRUE)
174  crayon_grey_0.8 <<- make_style_fast(grDevices::grey(0.8), grey = TRUE)
175}
176# nocov end
177