1
2#' A simple CLI theme
3#'
4#' To use this theme, you can set it as the `cli.theme` option.
5#' Note that this is in addition to the builtin theme, which is still in
6#' effect.
7#'
8#' ```r
9#' options(cli.theme = cli::simple_theme())
10#' ```
11#'
12#' and then CLI apps started after this will use it as the default theme.
13#' You can also use it temporarily, in a div element:
14#'
15#' ```r
16#' cli_div(theme = cli::simple_theme())
17#' ```
18#'
19#' # Showcase
20#'
21#' ```{asciicast simple-theme}
22#' show <- cli_div(theme = cli::simple_theme())
23#'
24#' cli_h1("Heading 1")
25#' cli_h2("Heading 2")
26#' cli_h3("Heading 3")
27#'
28#' cli_par()
29#' cli_alert_danger("Danger alert")
30#' cli_alert_warning("Warning alert")
31#' cli_alert_info("Info alert")
32#' cli_alert_success("Success alert")
33#' cli_alert("Alert for starting a process or computation",
34#'   class = "alert-start")
35#' cli_end()
36#'
37#' cli_text("Packages and versions: {.pkg cli} {.version 1.0.0}.")
38#' cli_text("Time intervals: {.timestamp 3.4s}")
39#'
40#' cli_text("{.emph Emphasis} and  {.strong strong emphasis}")
41#'
42#' cli_text("This is a piece of code: {.code sum(x) / length(x)}")
43#' cli_text("Function names: {.fn cli::simple_theme}")
44#'
45#' cli_text("Files: {.file /usr/bin/env}")
46#' cli_text("URLs: {.url https://r-project.org}")
47#'
48#' cli_h2("Longer code chunk")
49#' cli_par(class = "code R")
50#' cli_verbatim(
51#'   '# window functions are useful for grouped mutates',
52#'   'mtcars %>%',
53#'   '  group_by(cyl) %>%',
54#'   '  mutate(rank = min_rank(desc(mpg)))')
55#'
56#' cli_end(show)
57#' ```
58#'
59#' @param dark Whether the theme should be optimized for a dark
60#'   background. If `"auto"`, then cli will try to detect this.
61#'   Detection usually works in recent RStudio versions, and in iTerm
62#'   on macOS, but not on other platforms.
63#'
64#' @seealso [themes], [builtin_theme()].
65#' @export
66
67simple_theme <- function(dark = getOption("cli_theme_dark", "auto")) {
68
69  dark <- detect_dark_theme(dark)
70
71  list(
72    h1 = list(
73      "margin-top" = 1,
74      "margin-bottom" = 0,
75      color = "cyan",
76      fmt = function(x) cli::rule(x, line_col = "cyan")),
77
78    h2 = list(
79      "margin-top" = 1,
80      "margin-bottom" = 0,
81      color = "cyan",
82      fmt = function(x) paste0(symbol$line, " ", x, " ", symbol$line, symbol$line)),
83
84    h3 = list(
85      "margin-top" = 1,
86      "margin-bottom" = 0,
87      color = "cyan"),
88
89    par = list("margin-top" = 0, "margin-bottom" = 1),
90
91    ".alert-danger" = list(
92      "background-color" = "red",
93      color = "white",
94      before = function() paste0(symbol$cross, " ")
95    ),
96
97    ".alert-warning" = list(
98      color = "orange",
99      "font-weight" = "bold",
100      before = paste0("!", " ")
101    ),
102
103    ".alert-success" = list(
104      before = function() paste0(col_green(symbol$tick), " ")
105    ),
106    ".alert-info" = list(
107      before = function() paste0(col_cyan(symbol$info), " ")
108    ),
109
110    ".alert-start" = list(
111      before = function() paste0(symbol$arrow_right, " ")),
112
113    span.pkg = list(
114      color = "blue",
115      "font-weight" = "bold"),
116    span.version = list(color = "blue"),
117
118    span.emph = simple_theme_emph(),
119    span.strong = list("font-weight" = "bold", "font-style" = "italic"),
120
121    span.fun = utils::modifyList(simple_theme_code(dark), list(after = "()")),
122    span.fn = utils::modifyList(simple_theme_code(dark), list(after = "()")),
123    span.arg = simple_theme_code(dark),
124    span.kbd = utils::modifyList(simple_theme_code(dark),
125                          list(before = "<", after = ">")),
126    span.key = utils::modifyList(simple_theme_code(dark),
127                          list(before = "<", after = ">")),
128    span.file = simple_theme_file(),
129    span.path = simple_theme_file(),
130    span.email = simple_theme_url(),
131    span.url = utils::modifyList(simple_theme_url(),
132                          list(before = "<", after = ">")),
133    span.var = simple_theme_code(dark),
134    span.envvar = simple_theme_code(dark),
135
136    span.timestamp = list(before = "[", after = "]", color = "grey")
137  )
138}
139
140simple_theme_emph <- function() {
141  list("font-style" = "italic")
142}
143
144simple_theme_url <- function() {
145  list(color = "blue")
146}
147
148simple_theme_code <- function(dark) {
149  if (dark) {
150    list("background-color" = "#232323", color = "#f0f0f0")
151  } else{
152    list("background-color" = "#f8f8f8", color = "#202020")
153  }
154}
155
156simple_theme_file <- function() {
157  list(color = "blue")
158}
159
160simple_theme_r_code <- function(dark) {
161  dark <- dark
162  style <- if (dark) {
163    make_ansi_style("#f0f0f0")
164  } else {
165    make_ansi_style("#202020")
166  }
167  function(x) {
168    x <- ansi_strip(x)
169    lines <- strsplit(x, "\n", fixed = TRUE)[[1]]
170    fmd <- code_highlight(lines)
171    style(fmd)
172  }
173}
174
175is_iterm <- function() {
176  isatty(stdout()) && Sys.getenv("TERM_PROGRAM", "") == "iTerm.app"
177}
178
179is_iterm_dark <- function() {
180  if (is.null(clienv[["is_iterm_dark"]])) {
181    clienv$is_iterm_dark <-
182      tryCatch(
183        error = function(x) FALSE, {
184          osa <- '
185            tell application "iTerm2"
186              tell current session of current window
187                get background color
188              end tell
189            end tell
190          '
191          out <- system2("osascript", c("-e", shQuote(osa)), stdout = TRUE)
192          nums <- scan(text = gsub(",", "", out), quiet = TRUE)
193          mean(nums) < 20000
194        })
195  }
196  clienv[["is_iterm_dark"]]
197}
198