R/utils-cli-tree.R

Defines functions cli_is_latex_output cli_is_utf8_output cli_box_chars trace_deemph cli_tree

# Extract from cli::tree
# Modifications:
# * Remove assertions (requires assertthat)
# * Remove width triming (requires ansistrings or similar)
# * Use map_chr() instead of vcapply()
# Additional functions inlined:
# * is_utf8_output()
# * is_latex_output()

cli_tree <- function(data,
                     root = data$id[[1]],
                     style = NULL) {
  style <- style %||% cli_box_chars()
  full_emphasis <- every(data$node_type, is_string, "main")

  labels <- data$call_text
  src_locs <- chr(map_if(data$src_loc, nzchar, ~ paste0(" at ", .x)))
  labels <- paste0(labels, style_locs(src_locs))

  res <- character()

  pt <- function(root, n = integer(), mx = integer(), deemphasise = FALSE) {
    num_root <- match(root, data$id)
    main_sibling <- is_string(data$node_type[[num_root]], "main_sibling")
    marked_deemph <- FALSE

    prefix <- map_chr(seq_along(n), function(i) {
      mark_deemph <- function(x) {
        if (!marked_deemph) {
          marked_deemph <<- TRUE
          paste0("<DEEMPH>", x)
        } else {
          x
        }
      }

      if (n[i] < mx[i]) {
        if (i == length(n)) {
          if (deemphasise) {
            mark_deemph(paste0(style$j, style$h))
          } else if (!main_sibling && !full_emphasis) {
            paste0(style$j, mark_deemph(style$h))
          } else {
            paste0(style$j, style$h)
          }
        } else {
          # Detect first "|" branch displayed by taking into account
          # the empty " " spaces
          past <- seq_len(i)
          n_spaces <- sum(n[past] >= mx[past] & past != length(n))

          if (!deemphasise || i == 1 + n_spaces) {
            paste0(style$v, " ")
          } else {
            mark_deemph(paste0(style$v, " "))
          }
        }
      } else if (n[i] == mx[i] && i == length(n)) {
        if (deemphasise) {
          mark_deemph(paste0(style$l, style$h))
        } else {
          paste0(style$l, style$h)
        }
      } else {
        "  "
      }
    })

    line <- paste0(paste(prefix, collapse = ""), labels[[num_root]])

    if (marked_deemph) {
      parts <- strsplit(line, "<DEEMPH>")[[1]]
      line <- paste0(parts[[1]], trace_deemph(parts[[2]]))
    }

    res <<- c(res, line)

    children <- data$children[[num_root]]
    deemphasise <- deemphasise || !is_string(data$node_type[[num_root]], "main")

    for (d in seq_along(children)) {
      pt(children[[d]], c(n, d), c(mx, length(children)), deemphasise = deemphasise)
    }
  }

  if (nrow(data)) {
    pt(root)
  }

  indices <- data$id[-1]

  if (length(indices)) {
    indices <- pad_spaces(indices)
    indices <- paste0(" ", indices, ". ")

    # The root isn't numbered
    root_padding <- spaces(nchar(indices[[1]]))
    indices <- c(root_padding, indices)

    res <- paste0(col_silver(indices), res)
  }

  res
}

trace_deemph <- function(x) {
  deemph <- peek_option("rlang:::trace_deemph") %||% style_dim_soft
  deemph(x)
}

cli_box_chars <- function() {
  if (cli_is_utf8_output()) {
    list(
      "h" = "\u2500",                   # horizontal
      "v" = "\u2502",                   # vertical
      "l" = "\u2514",                   # leaf
      "j" = "\u251C"                    # junction
    )
  } else {
    list(
      "h" = "-",                        # horizontal
      "v" = "|",                        # vertical
      "l" = "\\",                       # leaf
      "j" = "+"                         # junction
    )
  }
}

cli_is_utf8_output <- function() {
  opt <- getOption("cli.unicode", NULL)
  if (!is.null(opt)) {
    isTRUE(opt)
  } else {
    l10n_info()$`UTF-8` && !cli_is_latex_output()
  }
}

cli_is_latex_output <- function() {
  if (!("knitr" %in% loadedNamespaces())) return(FALSE)
  get("is_latex_output", asNamespace("knitr"))()
}
tidyverse/rlang documentation built on Oct. 31, 2024, 5:35 p.m.