Skip to contents

highlight() creates a vector with a conditional format() and print() method. The function takes an input vector .x, a test function .t, and a formatter function .f. When the result of highlight(.x, .t, .f) is printed, elements of .x for which .t returns TRUE are transformed by .f before they are printed.

.t and .f may be equal length lists of functions. Elements of .x for which .t[[i]] returns true are transformed using .f[[i]]. Conditional formats are applied to the highlighted vector in the order that they are applied to .t and .f.

highlight_mult() and highlight_case() allow these pairs of functions to be supplied as two-sided formulas .t ~ .f using dplyr::case_when() style syntax.

hl() and highlight() are synonyms, as are hl_mult() and highlight_mult(), hl_case() and highlight_case().

Usage

highlight(
  .x = logical(),
  .t = getOption("vlightr.default_test"),
  .f = getOption("vlightr.default_formatter")
)

hl(
  .x = logical(),
  .t = getOption("vlightr.default_test"),
  .f = getOption("vlightr.default_formatter")
)

highlight_mult(.x, ...)

hl_mult(.x, ...)

highlight_case(.x, ...)

hl_case(.x, ...)

Arguments

.x

[vector]

A vector to highlight. Conceptually, a vector is a collection of objects of size 1.

.x is considered a vector if:

Atomic vector types "logical", "integer", "double", "complex", "character", and "raw" meet these criteria. As do many common vector classes such as POSIXct, lubridate::interval, or ivs::iv.

By default .x is an empty logical vector.

.t

[function / list]

Vectorized test functions that indicate which elements of .x to conditionally format. .t may be:

  • A named function, e.g. is.na

  • An anonymous function, e.g. \(x) 0 <= x & x <= 1

  • A purrr-style lambda, e.g. ~ nchar(.x) > 0, ~ .h == 1, ~ TRUE

  • A list of functions or lambdas, e.g. list(~ .x < mean(.x), is.finite)

Each function in .t will receive .x as it's input and must return a logical vector the same length as .x or of length 1 (in which case the result will be recycled to the length of .x).

By default .t is the function false() which returns FALSE for any input. You can modify this default by setting the vlightr.default_test in options().

.f

[function / list]

Vectorized character manipulation functions used to format .x. .f may be:

  • A named function, e.g. cli::style_bold

  • An anonymous function, e.g. \(words) gsub("hi", "hey", words)

  • A purrr-style lambda, e.g. ~ paste0(.h, "!"), ~ "fizz"

  • A list of functions or lambdas, e.g. list(~ cli::col_red(.x), toupper)

Each formatter function in .f will receive a character vector (of variable length) as it's only argument. A formatter must return a character vector the same length as it's input or of length 1 (in which case the result is recycled to the length of the input character).

ANSI string vectors (class cli_ansi_string) are also supported (see cli::ansi-styles for details).

By default .f is the function [cli::bg_br_yellow()] which changes the background color of it's input text to bright yellow. You can modify this default by setting the vlightr.default_formatter in options().

...

[formula / vlightr_highlighter]

For highlighter_mult() and highlighter_case(), a two sided formula with a test on the left-hand-side and a formatter on the right-hand-side. This argument replaces the .t and .f arguments of highlight(). The ith dot ..i is roughly equivalent to .t[[i]] ~ .f[[i]].

The left-hand-side and right-hand-side of the formula may be:

  • A named function, e.g. is.numeric, cli::style_bold

  • An anonymous function, e.g. \(x) is.nan(x), \(x) ifelse(x == "", "empty", x)

  • A purrr-style lambda expression, e.g. paste0(.h, "?"), "fizzbuzz"

Additionally, the left-hand-side of the formula may be a scalar atomic object, supplied by name e.g. 1, NaN, "Hello". This is a shorthand for a test of equality. For example:

  • 10 corresponds to .x == 10, "Word" to .x == "Word"

  • NaN corresponds to is.nan(.x)

  • NA corresponds to is.na(.x), NA_int_ to is.na(.x) & is.integer(.x)

A one-sided formula (e.g. ~ tolower) may also be supplied, in which case every element of .x is formatted using the right-hand-side function (e.g. tolower).

Finally, a highlighter, e.g. highlighter(is.na, color("red")), may be supplied instead of a formula. Every test (e.g. .t) and formatter (e.g. .f) associated with the highlighter is inserted as a test and formatter of the returned vector.

Examples of arguments to ... include:

  • Color NA values red: is.na ~ color("red")

  • Add an exclamation mark: toupper(.x) == .x ~ paste0(.x, "!")

  • Label the number 1 as "Yes": 1 ~ "Yes"

  • Color the background yellow by default: ~ cli::cli_bg_yellow(.x)

In the case of highlight_case(), elements of .x can be conditionally formatted at most once. Each element of .x is formatted using the formatter corresponding to the first test which returns TRUE for that element.

Value

A vector of class vlightr_highlight. For highlight_case(), the a vector of class vlightr_highlight/vlightr_highlight_case.

Details

The highlighter_mult() and highlight_case() formula syntax can conflict with dplyr::across() in-lined formulas and other purrr-style-lambdas. In particular, when the following is executed:

dplyr::across(col, ~highlight_mult(.x, is.na(.x) ~ color("red")))

The formula is.na(.x) ~ color("red") will be replaced with is.na(y) ~ color("red") by dplyr::across(). When the expression is.na(col) is converted to a test function by highlight_mult(), the object col will not exist in the environment of the test function, causing an error (or worse, a difficult to diagnose bug) when the test function is called.

To avoid this behavior, vlightr allows the use of .h in it's purrr-style-lambdas. Replacing is.na(.x) with is.na(.h), as in the following snippet, will work as expected:

 dplyr::across(y, ~highlight_mult(.x, is.na(.h) ~ color("red")))

All purrr-style-lambdas used by vlightr accept the symbols .x, .h, and . as aliases for their first argument.

See also

templight() for conditionally formatting elements of a vector .x by location. Replaces the test .t with a vector of positions .at.

is_highlightable() for testing whether an object can be highlighted.

un_highlight() for converting a vector highlight(.x) back to .x.

tests(), formatters(), highlight_functions() for setting and getting the values of .t (i.e. tests) and .f (i.e. formatters) of a highlighted vector.

color() and friends for generating formatter functions for use in .f.

Examples

# Emphasize NA values when `x_hl` is printed
x <- c(1, 0, NA, 1, 0)
x_hl <- highlight(x, is.na, ~paste("[", .x, "]"))
print(x)
#> [1]  1  0 NA  1  0
print(x_hl)
#> <highlight<double>[5]>
#> [1] 1      0      [ NA ] 1      0     

# Track the maximum of `values`
values <- highlight(c(1, 5, 7, 3), ~ .x == max(.x), wrap("[", "]"))
print(values)
#> <highlight<double>[4]>
#> [1] 1   5   [7] 3  
print(sort(values))
#> <highlight<double>[4]>
#> [1] 1   3   5   [7]
print(hl(-1) * values)
#> <highlight<double>[4]>
#> [1] [-1] -5   -7   -3  

# Add labels to an indicator variable
indicator <- highlight_mult(
  c(0, 1, NA, 5),
  0 ~ label("No"),
  1 ~ label("Yes"),
  is.na ~ color("red"),
  !(.x %in% c(0, 1, NA)) ~ label("?")
)
print(indicator)
#> <highlight<double>[4]>
#> [1] 0 [No]  1 [Yes] NA      5 [?]  

# Simplify using `dplyr::case_when()` style case matching.
# Elements are conditionally formatted using the first case
# where the left-hand-side returns `TRUE`.
indicator <- highlight_case(
  c(0, 1, NA, 5),
  0 ~ label("No"),
  1 ~ label("Yes"),
  is.na ~ color("red"),
  true ~ label("?") # `true()` is a function which returns `TRUE`
)
print(indicator)
#> <highlight_case<double>[4]>
#> [1] 0 [No]  1 [Yes] NA      5 [?]  

# Make a `highlighter()` to add the formatting of `indicator`
# to other vectors.
indicator_highlighter <- as_highlighter(indicator)
indicator_highlighter(c(1, 0, 1, NA, -9))
#> <highlight_case<double>[5]>
#> [1] 1 [Yes] 0 [No]  1 [Yes] NA      -9 [?] 

# A highlighter can be supplied to `highlight_mult()` or
# `highlight_case()`. This is an easy way to append new
# options to an existing highlighter.
highlight_case(
  c(1, 2, 0, NA, -9),
  2 ~ label("Maybe"),
  indicator_highlighter
)
#> <highlight_case<double>[5]>
#> [1] 1 [Yes]   2 [Maybe] 0 [No]    NA        -9 [?]   

# Apply multiple formats to the same element
highlight_mult(
  1:6,
  .x %% 2 == 0 ~ wrap("<", ">"),
  .x >= 3 ~ wrap("[", "]")
)
#> <highlight<integer>[6]>
#> [1] 1     <2>   [3]   [<4>] [5]   [<6>]

# Apply a formatter to every element of `.x` by
# supplying a one-sided formula.
upper_letters <- highlight_mult(letters[1:10], ~ toupper)
print(upper_letters)
#> <highlight<character>[10]>
#>  [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J"

# Note that highlighting does not alter the underlying data
un_highlight(upper_letters)
#>  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

# A one-sided formula supplied to `highlight_case()` will
# format all elements, even if they have already been formatted,
# over-riding the default matching behavior.
highlight_case(
  c(1, 1, 0),
  0 ~ "No",
  1 ~ "Yes",
  ~ toupper # `true ~ toupper` wouldn't format any elements
)
#> <highlight_case<double>[3]>
#> [1] YES YES NO 

# By default no formatting is applied to a highlighted vector.
highlight(1:5) # No conditional formatting
#> <highlight<integer>[5]>
#> [1] 1 2 3 4 5

# The default formatter `.f` colors the background of the
# formatted vector yellow. If you are reading this is in
# an environment which doesn't support ANSI coloring, you
# may not see the yellow background.
highlight(1:5, ~ .x > 3) # Yellow background
#> <highlight<integer>[5]>
#> [1] 1 2 3 4 5

# Change the default test or formatter using `options()`
opts <- options() # Save previous options
options(vlightr.default_formatter = \(x) paste("{", x, "}"))
highlight(-2:2, ~ .x < 0)
#> <highlight<integer>[5]>
#> [1] { -2 } { -1 } 0      1      2     

options(vlightr.default_test = \(x) x > 0)
highlight(-2:2)
#> <highlight<integer>[5]>
#> [1] -2    -1    0     { 1 } { 2 }
options(opts) # Reset previous options