Full Code of r-lib/crayon for AI

main 846317afdb81 cached
78 files
136.5 KB
43.3k tokens
1 requests
Download .txt
Repository: r-lib/crayon
Branch: main
Commit: 846317afdb81
Files: 78
Total size: 136.5 KB

Directory structure:
gitextract_d59v6gvb/

├── .Rbuildignore
├── .github/
│   ├── .gitignore
│   ├── CODE_OF_CONDUCT.md
│   └── workflows/
│       ├── R-CMD-check.yaml
│       ├── pkgdown.yaml
│       ├── pr-commands.yaml
│       └── test-coverage.yaml
├── .gitignore
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── NAMESPACE
├── NEWS.md
├── R/
│   ├── aaa-rstudio-detect.R
│   ├── aaaa-rematch2.R
│   ├── aab-num-ansi-colors.R
│   ├── aac-num-ansi-colors.R
│   ├── ansi-256.R
│   ├── ansi-palette.R
│   ├── combine.R
│   ├── crayon-package.R
│   ├── disposable.R
│   ├── enc-utils.R
│   ├── has_ansi.R
│   ├── has_color.R
│   ├── link.R
│   ├── machinery.R
│   ├── parts.R
│   ├── print.R
│   ├── show.R
│   ├── string.R
│   ├── string_operations.R
│   ├── style-var.R
│   ├── styles.R
│   └── utils.R
├── README.md
├── _pkgdown.yml
├── air.toml
├── codecov.yml
├── crayon.Rproj
├── man/
│   ├── chr.Rd
│   ├── col_align.Rd
│   ├── col_nchar.Rd
│   ├── col_strsplit.Rd
│   ├── col_substr.Rd
│   ├── col_substring.Rd
│   ├── combine_styles.Rd
│   ├── concat.Rd
│   ├── crayon.Rd
│   ├── drop_style.Rd
│   ├── has_color.Rd
│   ├── has_style.Rd
│   ├── hyperlink.Rd
│   ├── make_style.Rd
│   ├── num_ansi_colors.Rd
│   ├── num_colors.Rd
│   ├── show_ansi_colors.Rd
│   ├── start.crayon.Rd
│   ├── strip_style.Rd
│   ├── style.Rd
│   └── styles.Rd
├── tests/
│   ├── testthat/
│   │   ├── helper.R
│   │   ├── test-ansi256.R
│   │   ├── test-color.R
│   │   ├── test-combine.R
│   │   ├── test-has-style.R
│   │   ├── test-hyperlink.R
│   │   ├── test-make-style.R
│   │   ├── test-operations.R
│   │   ├── test-style-var.R
│   │   ├── test-styles.R
│   │   ├── test-utils.R
│   │   └── test-vectors.R
│   └── testthat.R
└── tools/
    ├── ansi-iterm-palettes.txt
    └── ansi-palettes.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .Rbuildignore
================================================
^tags$
^\.github$
^crayon\.Rproj$
^\.Rproj\.user$
^revdep$
^codecov\.yml$
^_pkgdown\.yml$
^docs$
^pkgdown$
^LICENSE\.md$
^[\.]?air\.toml$
^\.vscode$


================================================
FILE: .github/.gitignore
================================================
*.html


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
  community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of
  any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
  without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at codeofconduct@posit.co. 
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of
actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the
community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
<https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.

Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion].

For answers to common questions about this code of conduct, see the FAQ at
<https://www.contributor-covenant.org/faq>. Translations are available at <https://www.contributor-covenant.org/translations>.

[homepage]: https://www.contributor-covenant.org


================================================
FILE: .github/workflows/R-CMD-check.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
#
# NOTE: This workflow is overkill for most R packages and
# check-standard.yaml is likely a better choice.
# usethis::use_github_action("check-standard") will install it.
on:
  push:
    branches: [main, master]
  pull_request:

name: R-CMD-check.yaml

permissions: read-all

jobs:
  R-CMD-check:
    runs-on: ${{ matrix.config.os }}

    name: ${{ matrix.config.os }} (${{ matrix.config.r }})

    strategy:
      fail-fast: false
      matrix:
        config:
          - {os: macos-latest,   r: 'release'}

          - {os: windows-latest, r: 'release'}
          # use 4.0 or 4.1 to check with rtools40's older compiler
          - {os: windows-latest, r: 'oldrel-4'}

          - {os: ubuntu-latest,  r: 'devel', http-user-agent: 'release'}
          - {os: ubuntu-latest,  r: 'release'}
          - {os: ubuntu-latest,  r: 'oldrel-1'}
          - {os: ubuntu-latest,  r: 'oldrel-2'}
          - {os: ubuntu-latest,  r: 'oldrel-3'}
          - {os: ubuntu-latest,  r: 'oldrel-4'}

    env:
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
      R_KEEP_PKG_SOURCE: yes

    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/setup-pandoc@v2

      - uses: r-lib/actions/setup-r@v2
        with:
          r-version: ${{ matrix.config.r }}
          http-user-agent: ${{ matrix.config.http-user-agent }}
          use-public-rspm: true

      - uses: r-lib/actions/setup-r-dependencies@v2
        with:
          extra-packages: any::rcmdcheck
          needs: check

      - uses: r-lib/actions/check-r-package@v2
        with:
          upload-snapshots: true
          build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'


================================================
FILE: .github/workflows/pkgdown.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
  push:
    branches: [main, master]
  pull_request:
  release:
    types: [published]
  workflow_dispatch:

name: pkgdown.yaml

permissions: read-all

jobs:
  pkgdown:
    runs-on: ubuntu-latest
    # Only restrict concurrency for non-PR jobs
    concurrency:
      group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
    env:
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/setup-pandoc@v2

      - uses: r-lib/actions/setup-r@v2
        with:
          use-public-rspm: true

      - uses: r-lib/actions/setup-r-dependencies@v2
        with:
          extra-packages: any::pkgdown, local::.
          needs: website

      - name: Build site
        run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
        shell: Rscript {0}

      - name: Deploy to GitHub pages 🚀
        if: github.event_name != 'pull_request'
        uses: JamesIves/github-pages-deploy-action@v4.5.0
        with:
          clean: false
          branch: gh-pages
          folder: docs


================================================
FILE: .github/workflows/pr-commands.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
  issue_comment:
    types: [created]

name: pr-commands.yaml

permissions: read-all

jobs:
  document:
    if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }}
    name: document
    runs-on: ubuntu-latest
    env:
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/pr-fetch@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

      - uses: r-lib/actions/setup-r@v2
        with:
          use-public-rspm: true

      - uses: r-lib/actions/setup-r-dependencies@v2
        with:
          extra-packages: any::roxygen2
          needs: pr-document

      - name: Document
        run: roxygen2::roxygenise()
        shell: Rscript {0}

      - name: commit
        run: |
          git config --local user.name "$GITHUB_ACTOR"
          git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
          git add man/\* NAMESPACE
          git commit -m 'Document'

      - uses: r-lib/actions/pr-push@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

  style:
    if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }}
    name: style
    runs-on: ubuntu-latest
    env:
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/pr-fetch@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

      - uses: r-lib/actions/setup-r@v2

      - name: Install dependencies
        run: install.packages("styler")
        shell: Rscript {0}

      - name: Style
        run: styler::style_pkg()
        shell: Rscript {0}

      - name: commit
        run: |
          git config --local user.name "$GITHUB_ACTOR"
          git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
          git add \*.R
          git commit -m 'Style'

      - uses: r-lib/actions/pr-push@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/test-coverage.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
  push:
    branches: [main, master]
  pull_request:

name: test-coverage.yaml

permissions: read-all

jobs:
  test-coverage:
    runs-on: ubuntu-latest
    env:
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/setup-r@v2
        with:
          use-public-rspm: true

      - uses: r-lib/actions/setup-r-dependencies@v2
        with:
          extra-packages: any::covr, any::xml2
          needs: coverage

      - name: Test coverage
        run: |
          cov <- covr::package_coverage(
            quiet = FALSE,
            clean = FALSE,
            install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
          )
          print(cov)
          covr::to_cobertura(cov)
        shell: Rscript {0}

      - uses: codecov/codecov-action@v5
        with:
          # Fail if error if not on PR, or if on PR and token is given
          fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }}
          files: ./cobertura.xml
          plugins: noop
          disable_search: true
          token: ${{ secrets.CODECOV_TOKEN }}

      - name: Show testthat output
        if: always()
        run: |
          ## --------------------------------------------------------------------
          find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
        shell: bash

      - name: Upload test results
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: coverage-test-failures
          path: ${{ runner.temp }}/package


================================================
FILE: .gitignore
================================================
/tags
.Rproj.user
docs


================================================
FILE: .vscode/extensions.json
================================================
{
    "recommendations": [
        "Posit.air-vscode"
    ]
}


================================================
FILE: .vscode/settings.json
================================================
{
    "[r]": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "Posit.air-vscode"
    }
}


================================================
FILE: DESCRIPTION
================================================
Package: crayon
Title: Colored Terminal Output
Version: 1.5.3.9000
Authors@R: c(
    person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = c("aut", "cre")),
    person("Brodie", "Gaslam", , "brodie.gaslam@yahoo.com", role = "ctb"),
    person("Posit Software, PBC", role = c("cph", "fnd"),
           comment = c(ROR = "03wc8by49"))
  )
Description: The crayon package is now superseded. Please use the 'cli'
    package for new projects.  Colored terminal output on terminals that
    support 'ANSI' color and highlight codes. It also works in 'Emacs'
    'ESS'. 'ANSI' color support is automatically detected. Colors and
    highlighting can be combined and nested. New styles can also be
    created easily.  This package was inspired by the 'chalk' 'JavaScript'
    project.
License: MIT + file LICENSE
URL: https://r-lib.github.io/crayon/, https://github.com/r-lib/crayon
BugReports: https://github.com/r-lib/crayon/issues
Imports:
    grDevices,
    methods,
    utils
Suggests:
    rstudioapi,
    testthat,
    withr
Config/Needs/website: tidyverse/tidytemplate
Config/usethis/last-upkeep: 2025-04-25
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.1
Collate:
    'aaa-rstudio-detect.R'
    'aaaa-rematch2.R'
    'aab-num-ansi-colors.R'
    'aac-num-ansi-colors.R'
    'ansi-256.R'
    'ansi-palette.R'
    'combine.R'
    'string.R'
    'utils.R'
    'crayon-package.R'
    'disposable.R'
    'enc-utils.R'
    'has_ansi.R'
    'has_color.R'
    'link.R'
    'styles.R'
    'machinery.R'
    'parts.R'
    'print.R'
    'style-var.R'
    'show.R'
    'string_operations.R'


================================================
FILE: LICENSE
================================================
YEAR: 2025
COPYRIGHT HOLDER: crayon authors


================================================
FILE: LICENSE.md
================================================
# MIT License

Copyright (c) 2025 crayon authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: NAMESPACE
================================================
# Generated by roxygen2: do not edit by hand

S3method("$",crayon)
S3method("-",crayon)
S3method(as.character,crayon)
S3method(finish,crayon)
S3method(print,crayon)
S3method(start,crayon)
export("%+%")
export(bgBlack)
export(bgBlue)
export(bgCyan)
export(bgGreen)
export(bgMagenta)
export(bgRed)
export(bgWhite)
export(bgYellow)
export(black)
export(blue)
export(blurred)
export(bold)
export(chr)
export(col_align)
export(col_nchar)
export(col_strsplit)
export(col_substr)
export(col_substring)
export(combine_styles)
export(cyan)
export(drop_style)
export(finish)
export(green)
export(has_color)
export(has_hyperlink)
export(has_style)
export(hidden)
export(hyperlink)
export(inverse)
export(italic)
export(magenta)
export(make_style)
export(num_ansi_colors)
export(num_colors)
export(red)
export(reset)
export(show_ansi_colors)
export(silver)
export(strikethrough)
export(strip_style)
export(style)
export(styles)
export(underline)
export(white)
export(yellow)
importFrom(grDevices,col2rgb)
importFrom(grDevices,colors)
importFrom(grDevices,grey)
importFrom(grDevices,rgb)
importFrom(methods,is)
importFrom(stats,start)
importFrom(utils,head)
importFrom(utils,install.packages)
importFrom(utils,package.skeleton)
importFrom(utils,tail)
importFrom(utils,tar)


================================================
FILE: NEWS.md
================================================
# crayon (development version)

# crayon 1.5.3

* The semantics of the `cli.default_num_colors` options is now different.
  It is now used as the default number of colors if the number of colors
  is not already set by the `cli.num_colors` option, the `R_CLI_NUM_COLORS`
  environment variable, the `crayon.enabled` and `crayon.colors` options,
  the `NO_COLOR` environment variable, the `knitr.in.progress` option,
  or a `sink()` call for the stream. This matches the behavior of the cli
  package.

# crayon 1.5.2

* crayon functions now mark their output with the right encoding,
  if their input was marked (#136, #138).

# 1.5.1

* crayon now does not fail to install from source if the utils package
  is not loaded (#133).

# 1.5.0

* You can use the new `cli.default_num_colors` option to set the default
  number of ANSI colors, only if ANSI support is otherwise detected.
  See the details in the manual of `num_ansi_colors()`. (From cli.)

* crayon now install cleanly on R 3.4.x and R 3.5.x on Windows (#131).

# 1.4.2

* Better ANSI support detection if there is an active `sink()`.

* Support ANSI markup in RStudio jobs.

# 1.4.1

* ANSI color support detection works correctly now in older RStudio (#112).

* ANSI color support detection works correctly now on older R versions
  (#109).

# 1.4.0

* Overhauled and much improved detection of ANSI color support and
  number of ANSI colors.

* `NO_COLOR` environment variable disables color (#64)

* Now colors are turned on if ANSICON (<https://github.com/adoxa/ansicon>)
  is active.

* New `hyperlink()` function to add hyperlinks to terminal output.
  The new `has_hyperlink()` function tests if the current `stdout()`
  supports this.

* `reset` style now restores previous style for text following it (@brodieG,
  #35).

* Warnings are no longer generated when the INSIDE_EMACS environment variable is
  set but does not include a version number.

# 1.3.4

* Style functions convert arguments to character now

* Autodetect RStudio ANSI support

* `col_align()` gains `type` argument, default `"width"` (#54).

# 1.3.2

* Removed dependency to `memoise` (@brodieG, #25)

* Fixed a test case that changed the `crayon.enabled`
  setting, potentially (@brodieG)

* Added `crayon.colors` option, to specify the number of
  colors explicitly

* `TERM=xterm` and `tput colors=8` will use 256 colors,
  as 256 colors are usually supported in this case (#17)

* Support colors in ConEmu and cmder, on Windows

* Fix color detection in Emacs tramp

* `col_strsplit` and `col_substr` corner cases:

    * handle empty chunks at beginning or end of strings
      like `base::strsplit` (@brodieG, #26)

    * explicitly deal with 'split' values that are not
      length 1 as that is not currently supported

    * handle zero length `x` argument in `col_substr`, and
      add more explicit error messages for corner cases

* Some performance improvements to `col_substr` (@brodieG)

* Change rgb to ANSI code mapping, based on the "paint" ruby gem
  (@richfitz, #33, #34)

# 1.3.1

* Fixed some `R CMD check` problems.

# 1.3.0

* Colors are turned on by default in Emacs ESS 23.x and above.

* Functions to turn on and off a style: `start`, `finish`.

* Really fix `tput` corner cases (@jimhester, #21)

# 1.2.1

* Fix detecting number of colors when `tput` exists, but
  fails with an error and/or does not return anything useful.
  (@jimhester, #18, #19)

# 1.2.0

* Fix detection of number of colors, it was cached from
  installation time (#17).

* Color aware string operations. They are slow and experimental
  currently.

# 1.1.0

* `show_ansi_colors()` prints all supported colors on the screen.

* 256 colors, on terminals that support it.

* Disable colors on Windows, they are not supported in the default setup.

# 1.0.0

* First released version.


================================================
FILE: R/aaa-rstudio-detect.R
================================================
rstudio <- local({
  standalone_env <- environment()
  parent.env(standalone_env) <- baseenv()

  # -- Collect data ------------------------------------------------------

  data <- NULL

  get_data <- function() {
    envs <- c(
      "R_BROWSER",
      "R_PDFVIEWER",
      "RSTUDIO",
      "RSTUDIO_TERM",
      "RSTUDIO_CONSOLE_COLOR",
      "RSTUDIOAPI_IPC_REQUESTS_FILE",
      "XPC_SERVICE_NAME",
      "ASCIICAST"
    )

    d <- list(
      pid = Sys.getpid(),
      envs = Sys.getenv(envs),
      api = tryCatch(
        asNamespace("rstudioapi")$isAvailable(),
        error = function(err) FALSE
      ),
      tty = isatty(stdin()),
      gui = .Platform$GUI,
      args = commandArgs(),
      search = search()
    )
    d$ver <- if (d$api) asNamespace("rstudioapi")$getVersion()
    d$desktop <- if (d$api) asNamespace("rstudioapi")$versionInfo()$mode

    d
  }

  # -- Auto-detect environment -------------------------------------------

  is_rstudio <- function() {
    Sys.getenv("RSTUDIO") == "1"
  }

  detect <- function(clear_cache = FALSE) {
    # Check this up front, in case we are in a testthat 3e test block.
    # We cannot cache this, because we might be in RStudio in reality.
    if (!is_rstudio()) {
      return(get_caps(type = "not_rstudio"))
    }

    # Cached?
    if (clear_cache) data <<- NULL
    if (!is.null(data)) return(get_caps(data))

    # Otherwise get data
    new <- get_data()

    # Cache unless told otherwise
    cache <- TRUE

    new$type <- if (new$envs[["RSTUDIO"]] != "1") {
      # 1. Not RStudio at all
      "not_rstudio"
    } else if (new$gui == "RStudio" && new$api) {
      # 2. RStudio console, properly initialized
      "rstudio_console"
    } else if (!new$api && basename(new$args[1]) == "RStudio") {
      # 3. RStudio console, initializing
      cache <- FALSE
      "rstudio_console_starting"
    } else if (new$gui == "Rgui") {
      # Still not RStudio, but Rgui that was started from RStudio
      "not_rstudio"
    } else if (new$tty && new$envs[["ASCIICAST"]] != "true") {
      # 4. R in the RStudio terminal
      # This could also be a subprocess of the console or build pane
      # with a pseudo-terminal. There isn't really a way to rule that
      # out, without inspecting some process data with ps::ps_*().
      # At least we rule out asciicast
      "rstudio_terminal"
    } else if (
      !new$tty &&
        new$envs[["RSTUDIO_TERM"]] == "" &&
        new$envs[["R_BROWSER"]] == "false" &&
        new$envs[["R_PDFVIEWER"]] == "false" &&
        is_build_pane_command(new$args)
    ) {
      # 5. R in the RStudio build pane
      # https://github.com/rstudio/rstudio/blob/master/src/cpp/session/
      # modules/build/SessionBuild.cpp#L231-L240
      "rstudio_build_pane"
    } else if (
      new$envs[["RSTUDIOAPI_IPC_REQUESTS_FILE"]] != "" &&
        grepl("rstudio", new$envs[["XPC_SERVICE_NAME"]])
    ) {
      # RStudio job, XPC_SERVICE_NAME=0 in the subprocess of a job
      # process. Hopefully this is reliable.
      "rstudio_job"
    } else if (
      new$envs[["RSTUDIOAPI_IPC_REQUESTS_FILE"]] != "" &&
        any(grepl("SourceWithProgress.R", new$args))
    ) {
      # Or we can check SourceWithProgress.R in the command line, see
      # https://github.com/r-lib/cli/issues/367
      "rstudio_job"
    } else {
      # Otherwise it is a subprocess of the console, terminal or
      # build pane, and it is hard to say which, so we do not try.
      "rstudio_subprocess"
    }

    installing <- Sys.getenv("R_PACKAGE_DIR", "")
    if (cache && installing == "") data <<- new

    get_caps(new)
  }

  is_build_pane_command <- function(args) {
    cmd <- gsub("[\"']", "", args[[length(args)]], useBytes = TRUE)
    rcmd <- sub("[(].*$", "", cmd)
    rcmd %in% c("devtools::build", "devtools::test", "devtools::check")
  }

  # -- Capabilities ------------------------------------------------------

  caps <- list()

  caps$not_rstudio <- function(data) {
    list(
      type = "not_rstudio",
      dynamic_tty = FALSE,
      ansi_tty = FALSE,
      ansi_color = FALSE,
      num_colors = 1L
    )
  }

  caps$rstudio_console <- function(data) {
    list(
      type = "rstudio_console",
      dynamic_tty = TRUE,
      ansi_tty = FALSE,
      ansi_color = data$envs[["RSTUDIO_CONSOLE_COLOR"]] != "",
      num_colors = as.integer(data$envs[["RSTUDIO_CONSOLE_COLOR"]])
    )
  }

  caps$rstudio_console_starting <- function(data) {
    res <- caps$rstudio_console(data)
    res$type <- "rstudio_console_starting"
    res
  }

  caps$rstudio_terminal <- function(data) {
    list(
      type = "rstudio_terminal",
      dynamic_tty = TRUE,
      ansi_tty = FALSE,
      ansi_color = FALSE,
      num_colors = 1L
    )
  }

  caps$rstudio_build_pane <- function(data) {
    list(
      type = "rstudio_build_pane",
      dynamic_tty = TRUE,
      ansi_tty = FALSE,
      ansi_color = data$envs[["RSTUDIO_CONSOLE_COLOR"]] != "",
      num_colors = as.integer(data$envs[["RSTUDIO_CONSOLE_COLOR"]])
    )
  }

  caps$rstudio_job <- function(data) {
    list(
      type = "rstudio_job",
      dynamic_tty = FALSE,
      ansi_tty = FALSE,
      ansi_color = data$envs[["RSTUDIO_CONSOLE_COLOR"]] != "",
      num_colors = as.integer(data$envs[["RSTUDIO_CONSOLE_COLOR"]])
    )
  }

  caps$rstudio_subprocess <- function(data) {
    list(
      type = "rstudio_subprocess",
      dynamic_tty = FALSE,
      ansi_tty = FALSE,
      ansi_color = FALSE,
      num_colors = 1L
    )
  }

  get_caps <- function(data, type = data$type) caps[[type]](data)

  structure(
    list(
      .internal = standalone_env,
      is_rstudio = is_rstudio,
      detect = detect
    ),
    class = c("standalone_rstudio_detect", "standalone")
  )
})


================================================
FILE: R/aaaa-rematch2.R
================================================
re_match <- function(text, pattern, perl = TRUE, ...) {
  stopifnot(is.character(pattern), length(pattern) == 1, !is.na(pattern))
  text <- as.character(text)

  match <- regexpr(pattern, text, perl = perl, ...)

  start <- as.vector(match)
  length <- attr(match, "match.length")
  end <- start + length - 1L

  matchstr <- substring(text, start, end)
  matchstr[start == -1] <- NA_character_

  empty <- data.frame(stringsAsFactors = FALSE, .text = text)[, numeric()]
  res <- list(match = !is.na(matchstr), groups = empty)

  if (!is.null(attr(match, "capture.start"))) {
    gstart <- attr(match, "capture.start")
    glength <- attr(match, "capture.length")
    gend <- gstart + glength - 1L

    groupstr <- substring(text, gstart, gend)
    groupstr[gstart == -1] <- NA_character_
    dim(groupstr) <- dim(gstart)

    res$groups <- cbind(groupstr, res$groups, stringsAsFactors = FALSE)
    names(res$groups) <- attr(match, "capture.names")
  }

  res$groups
}


================================================
FILE: R/aab-num-ansi-colors.R
================================================
#' Detect the number of ANSI colors to use
#'
#' @description
#' Certain Unix and Windows terminals, and also certain R GUIs, e.g.
#' RStudio, support styling terminal output using special control
#' sequences (ANSI sequences).
#'
#' `num_ansi_colors()` detects if the current R session supports ANSI
#' sequences, and if it does how many colors are supported.
#'
#' @param stream The stream that will be used for output, an R connection
#' object. It can also be a string, one of `"auto"`, `"message"`,
#' `"stdout"`, `"stderr"`. `"auto"` will select `stdout()` if the session is
#' interactive and there are no sinks, otherwise it will select `stderr()`.
#' @return Integer, the number of ANSI colors the current R session
#' supports for `stream`.
#'
#' @family ANSI styling
#' @export
#' @examples
#' num_ansi_colors()
#'
#' @details
#' The detection mechanism is quite involved and it is designed to work
#' out of the box on most systems. If it does not work on your system,
#' please report a bug. Setting options and environment variables to turn
#' on ANSI support is error prone, because they are inherited in other
#' environments, e.g. knitr, that might not have ANSI support.
#'
#' If you want to _turn off_ ANSI colors, set the `NO_COLOR` environment
#' variable to a non-empty value.
#'
#' The exact detection mechanism is as follows:

num_ansi_colors <- function(stream = "auto") {
  #' 1. If the `cli.num_colors` options is set, that is returned.

  opt <- getOption("cli.num_colors", NULL)
  if (!is.null(opt)) return(as.integer(opt))

  #' 1. If the `R_CLI_NUM_COLORS` environment variable is set to a
  #'    non-empty value, then it is used.

  if ((env <- Sys.getenv("R_CLI_NUM_COLORS", "")) != "") {
    return(as.integer(env))
  }

  #' 1. If the `crayon.enabled` option is set to `FALSE`, 1L is returned.
  #'    (This is for compatibility with code that uses the crayon package.)
  #' 1. If the `crayon.enabled` option is set to `TRUE` and the
  #'    `crayon.colors` option is not set, then the value of the
  #'    `cli.default_num_colors` option, or if it is unset, then 8L is
  #'    returned.
  #' 1. If the `crayon.enabled` option is set to `TRUE` and the
  #'    `crayon.colors` option is also set, then the latter is returned.
  #'    (This is for compatibility with code that uses the crayon package.)

  cray_opt_has <- getOption("crayon.enabled", NULL)
  cray_opt_num <- getOption("crayon.colors", NULL)
  if (!is.null(cray_opt_has) && !isTRUE(cray_opt_has)) return(1L)
  if (isTRUE(cray_opt_has) && !is.null(cray_opt_num)) {
    return(as.integer(cray_opt_num))
  }
  if (isTRUE(cray_opt_has) && is.null(cray_opt_num)) {
    default <- get_default_number_of_colors()
    return(default %||% 8L)
  }

  #' 1. If the `NO_COLOR` environment variable is set, then 1L is returned.

  if (!is.na(Sys.getenv("NO_COLOR", NA_character_))) return(1L)

  #' 1. If we are in knitr, then 1L is returned, to turn off colors in
  #'    `.Rmd` chunks.

  if (isTRUE(getOption("knitr.in.progress"))) return(1L)

  #' 1. If `stream` is `"auto"` (the default) and there is an active
  #'    sink (either for `"output"` or `"message"`), then we return 1L.
  #'    (In theory we would only need to check the stream that will be
  #'    be actually used, but there is no easy way to tell that.)
  if (stream == "auto" && !no_sink()) return(1L)

  # Defer computation on streams to speed up common case
  # when environment variables are set
  orig_stream <- stream
  stream <- get_real_output(stream)

  is_stdout <- is_stderr <- is_std <- FALSE
  std <- "nope"
  if (identical(stream, stdout())) {
    is_stdout <- is_std <- TRUE
    std <- "stdout"
  } else if (identical(stream, stderr())) {
    is_stderr <- is_std <- TRUE
    std <- "stderr"
  }

  #' 1. If `stream` is not `"auto"`, but it is `stderr()` and there is an
  #'    active sink for it, then 1L is returned.
  #'    (If a sink is active for "output", then R changes the `stdout()`
  #'    stream, so this check is not needed.)

  # If a sink is active for "message" (ie. stderr), then R does not update
  # the `stderr()` stream, so we need to catch this case.
  if (is_stderr && sink.number("message") != 2) return(1L)

  #' 1. If the `cli.default_num_colors` option is set, then we use that.

  dopt <- get_default_number_of_colors()
  if (!is.null(dopt)) return(as.integer(dopt))

  #' 1. If R is running inside RGui on Windows, or R.app on macOS, then we
  #'    return 1L.

  # RStudio sets GUI to RGui initially, so we'll handle that after RStudio.
  if (.Platform$GUI == "AQUA") return(1L)

  #' 1. If R is running inside RStudio, with color support, then the
  #'    appropriate number of colors is returned, usually 256L.

  rstudio <- rstudio$detect()
  rstudio_colors <- c(
    "rstudio_console",
    "rstudio_console_starting",
    "rstudio_build_pane",
    "rstudio_job"
  )
  if (is.na(rstudio$num_colors)) rstudio$num_colors <- 1L
  if (rstudio$type %in% rstudio_colors && is_std) {
    return(rstudio$num_colors)
  }

  # RGui? We need to do this after RStudio, because .Platform$GUI is
  # "Rgui" in RStudio when we are starting up
  if (.Platform$GUI == "Rgui") return(1L)

  #' 1. If R is running on Windows, inside an Emacs version that is recent
  #'    enough to support ANSI colors, then the value of the
  #'    `cli.default_num_colors` option, or if unset 8L is returned.
  #'    (On Windows, Emacs has `isatty(stdout()) == FALSE`, so we need to
  #'    check for this here before dealing with terminals.)

  # Windows Emacs? The top R process will have `--ess` in ESS, but the
  # subprocesses won't. (Without ESS subprocesses will also report 8L
  # colors, this is a problem, but we expect most people use ESS in Emacs.)
  if (
    os_type() == "windows" &&
      "--ess" %in% commandArgs() &&
      is_emacs_with_color()
  ) {
    default <- get_default_number_of_colors()
    return(default %||% 8L)
  }

  #' 1. If `stream` is not the standard output or standard error  in a
  #'    terminal, then 1L is returned.

  if (!isatty(stream)) return(1L)
  if (!is_std) return(1L)

  #' 1. Otherwise we use and cache the result of the terminal color
  #'     detection (see below).

  # Otherwise use/set the cache
  if (is.null(clienv$num_colors)) clienv$num_colors <- list()
  clienv$num_colors[[std]] <- clienv$num_colors[[std]] %||% detect_tty_colors()
  clienv$num_colors[[std]]
}

#' @rdname num_ansi_colors
#' @details
#' The terminal color detection algorithm:

detect_tty_colors <- function() {
  default <- get_default_number_of_colors()

  #' 1. If the `COLORTERM` environment variable is set to `truecolor` or
  #'    `24bit`, then we return 16 million colors.
  #' 1. If the `COLORTERM` environment variable is set to anything else,
  #'    then we return the value of the `cli.num_default_colors` option,
  #'    8L if unset.

  ct <- Sys.getenv("COLORTERM", NA_character_)
  if (!is.na(ct)) {
    if (ct == "truecolor" || ct == "24bit") {
      return(truecolor)
    } else {
      return(default %||% 8L)
    }
  }

  #' 1. If R is running on Unix, inside an Emacs version that is recent
  #'    enough to support ANSI colors, then the value of the
  #'    `cli.default_num_colors` option is returned, or 8L if unset.

  if (os_type() == "unix" && is_emacs_with_color()) return(default %||% 8L)

  #' 1. If we are on Windows in an RStudio terminal, then apparently
  #'    we only have eight colors, but the `cli.default_num_colors` option
  #'    can be used to override this.

  win10 <- win10_build()
  if (
    os_type() == "windows" &&
      win10 >= 10586 &&
      rstudio_detect()$type == "rstudio_terminal"
  ) {
    # this is rather weird, but echo turns on color support :D
    system2("cmd", c("/c", "echo 1 >NUL"))
    return(default %||% 8L)
  }

  #' 1. If we are in a recent enough Windows 10 terminal, then there
  #'    is either true color (from build 14931) or 256 color (from
  #'    build 10586) support. You can also use the `cli.default_num_colors`
  #'    option to override these.

  if (os_type() == "windows" && win10 >= 10586) {
    # this is rather weird, but echo turns on color support :D
    system2("cmd", c("/c", "echo 1 >NUL"))
    # https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/
    if (win10 >= 14931) {
      return(default %||% truecolor)
    } else {
      return(default %||% 256L)
    }
  }

  if (os_type() == "windows") {
    #' 1. If we are on Windows, under ConEmu or cmder, or ANSICON is loaded,
    #'    then the value of `cli.default_num_colors`, or 8L if unset, is
    #'    returned.

    if (
      Sys.getenv("ConEmuANSI") == "ON" ||
        Sys.getenv("CMDER_ROOT") != ""
    ) {
      return(default %||% 8L)
    }
    if (Sys.getenv("ANSICON") != "") return(default %||% 8L)

    #' 1. Otherwise if we are on Windows, return 1L.

    return(1L)
  }

  #' 1. Otherwise we are on Unix and try to run `tput colors` to determine
  #'    the number of colors. If this succeeds, we return its return value.

  cols <- suppressWarnings(try(
    silent = TRUE,
    as.numeric(system("tput colors 2>/dev/null", intern = TRUE))[1]
  ))
  if (inherits(cols, "try-error") || !length(cols) || is.na(cols)) {
    return(guess_tty_colors())
  }
  if (cols %in% c(-1, 0, 1)) {
    return(1)
  }

  #'    If the `TERM` environment variable is `xterm` and `tput`
  #'    returned 8L, we return 256L, because xterm compatible terminals
  #'    tend to support 256 colors
  #'    (<https://github.com/r-lib/crayon/issues/17>)
  #'    You can override this with the `cli.default_num_colors` option.

  if (cols == 8 && identical(Sys.getenv("TERM"), "xterm")) {
    cols <- default %||% 256
  }

  #' 1. If `TERM` is set to `dumb`, we return 1L.
  #' 1. If `TERM` starts with `screen`, `xterm`, or `vt100`, we return 8L.
  #' 1. If `TERM` contains `color`, `ansi`, `cygwin` or `linux`, we return 8L.
  #' 1. Otherwise we return 1L.

  cols
}

get_default_number_of_colors <- function() {
  dft <- getOption("cli.default_num_colors")
  if (!is.null(dft)) {
    if (!is_count(dft)) {
      warning(
        "The `cli.default_num_colors` option must be an integer scalar"
      )
      dft <- NULL
    }
  }
  dft
}

guess_tty_colors <- function() {
  term <- Sys.getenv("TERM")
  if (term == "dumb") return(1L)

  if (
    grepl(
      "^screen|^xterm|^vt100|color|ansi|cygwin|linux",
      term,
      ignore.case = TRUE,
      perl = TRUE
    )
  ) {
    8L
  } else {
    1L
  }
}

is_emacs_with_color <- function() {
  (Sys.getenv("EMACS") != "" || Sys.getenv("INSIDE_EMACS") != "") &&
    !is.na(emacs_version()[1]) &&
    emacs_version()[1] >= 23
}

emacs_version <- function() {
  ver <- Sys.getenv("INSIDE_EMACS")
  if (ver == "") return(NA_integer_)

  ver <- gsub("'", "", ver)
  ver <- strsplit(ver, ",", fixed = TRUE)[[1]]
  ver <- strsplit(ver, ".", fixed = TRUE)[[1]]
  as.numeric(ver)
}

win10_build <- function() {
  os <- utils::sessionInfo()$running %||% ""
  if (!grepl("^Windows 10 ", os)) return(0L)
  mch <- re_match(os, "[(]build (?<build>[0-9]+)[)]")
  mch <- suppressWarnings(as.integer(mch))
  if (is.na(mch)) return(0L)
  mch
}


================================================
FILE: R/aac-num-ansi-colors.R
================================================
# Helper functions from cli, for the ANSI color detection

#' @include aab-num-ansi-colors.R

get_real_output <- function(output) {
  cli_output_connection()
}

cli_output_connection <- function() {
  if (is_interactive() && no_sink()) stdout() else stderr()
}

is_interactive <- function() {
  opt <- getOption("rlib_interactive")
  if (isTRUE(opt)) {
    TRUE
  } else if (identical(opt, FALSE)) {
    FALSE
  } else if (tolower(getOption("knitr.in.progress", "false")) == "true") {
    FALSE
  } else if (
    tolower(getOption("rstudio.notebook.executing", "false")) == "true"
  ) {
    FALSE
  } else if (identical(Sys.getenv("TESTTHAT"), "true")) {
    FALSE
  } else {
    interactive()
  }
}

no_sink <- function() {
  sink.number() == 0 && sink.number("message") == 2
}

`%||%` <- function(l, r) if (is.null(l)) r else l

clienv <- new.env(parent = emptyenv())


================================================
FILE: R/ansi-256.R
================================================
# nocov start
fgcodes <- c(paste0('\x1b[38;5;', 0:255, 'm'), '\x1b[39m')
bgcodes <- c(paste0('\x1b[48;5;', 0:255, 'm'), '\x1b[49m')

rgb_index <- 17:232
gray_index <- 233:256
reset_index <- 257
#nocov end

ansi256 <- function(rgb, bg = FALSE, grey = FALSE) {
  codes <- if (bg) bgcodes else fgcodes
  if (grey) {
    ## Gray
    list(
      open = codes[gray_index][scale(rgb[1], to = c(0, 23)) + 1],
      close = codes[reset_index]
    )
  } else {
    ## Not gray
    list(
      open = codes[ansi256_rgb_index(rgb[1L], rgb[2L], rgb[3L])],
      close = codes[reset_index]
    )
  }
}

## This is based off the algorithm in the ruby "paint" gem, as
## implemented in rainbowrite.
ansi256_rgb_index <- function(red, green, blue) {
  gray_possible <- TRUE
  sep <- 42.5
  while (gray_possible) {
    if (red < sep || green < sep || blue < sep) {
      gray <- red < sep && green < sep && blue < sep
      gray_possible <- FALSE
    }
    sep <- sep + 42.5
  }

  ## NOTE: The +1 here translates from base0 to base1 for the index
  ## that does the same.  Not ideal, but that does get the escape
  ## characters in nicely.
  if (gray) {
    232 + round((red + green + blue) / 33) + 1
  } else {
    16 + sum(floor(6 * c(red, green, blue) / 256) * c(36, 6, 1)) + 1
  }
}


================================================
FILE: R/ansi-palette.R
================================================
get_palette_color <- function(style, colors = num_ansi_colors()) {
  opt <- getOption("cli.palette")
  if (is.null(opt) || colors < 256) return(style)
  cache_palette_color(opt, style$palette, colors)
}

palette_cache <- new.env(parent = emptyenv())

cache_palette_color <- function(pal, idx, colors = num_ansi_colors()) {
  if (is_string(pal)) {
    if (!pal %in% rownames(ansi_palettes)) {
      stop("Cannot find cli ANSI palette '", pal, "'.")
    }
    pal <- ansi_palettes[pal, ]
  }

  bg <- idx < 0
  idx <- abs(idx)
  col <- pal[[idx]]

  colkey <- as.character(colors)
  key <- paste0(col, bg)
  if (key %in% names(palette_cache[[colkey]])) {
    return(palette_cache[[colkey]][[key]])
  }

  val <- ansi_style_from_r_color(
    col,
    bg = bg,
    colors,
    grey = FALSE
  )

  if (is.null(palette_cache[[colkey]])) {
    palette_cache[[colkey]] <- new.env(parent = emptyenv())
  }
  palette_cache[[colkey]][[key]] <- val

  return(val)
}

#' @details
#' `truecolor` is an integer constant for the number of 24 bit ANSI colors.
#'
#' @format `truecolor` is an integer scalar.
#'
#' @noRd
#' @rdname ansi_palettes

truecolor <- as.integer(256^3)

#' ANSI colors palettes
#'
#' If your platform supports at least 256 colors, then you can configure
#' the colors that cli uses for the eight base and the eight bright colors.
#' (I.e. the colors of [col_black()], [col_red()], and [col_br_black()],
#' [col_br_red()], etc.
#'
#' To customize the default palette, set the `cli.palette` option to the
#' name of a built-in palette (see `ansi_palettes()`), or the list of
#' 16 colors. Colors can be specified with RGB colors strings:
#' `#rrggbb` or R color names (see the output of [grDevices::colors()]).
#'
#' For example, you can put this in your R profile:
#' ```r
#' options(cli.palette = "vscode")
#' ```
#'
#' It is currently not possible to configure the background colors
#' separately, these will be always the same as the foreground colors.
#'
#' If your platform only has 256 colors, then the colors specified in the
#' palette have to be interpolated. On true color platforms they RGB
#' values are used as-is.
#'
#' `ansi_palettes` is a data frame of the built-in palettes, each row
#' is one palette.
#'
#' `ansi_palette_show()` shows the colors of an ANSI palette on the screen.
#'
#' @format `ansi_palettes` is a data frame with one row for each palette,
#'   and one column for each base ANSI color. `attr(ansi_palettes, "info")`
#'   contains a list with information about each palette.
#'
#' @noRd
#' @examples
#' ansi_palettes
#' ansi_palette_show("dichro", colors = truecolor)

ansi_palettes <- rbind(
  utils::read.table(
    "tools/ansi-palettes.txt",
    comment.char = ";",
    stringsAsFactors = FALSE
  ),
  utils::read.table(
    "tools/ansi-iterm-palettes.txt",
    comment.char = ";",
    stringsAsFactors = FALSE
  )
)

attr(ansi_palettes, "info") <-
  list(
    dichro = paste(
      "Colorblind friendly palette, from",
      "https://github.com/romainl/vim-dichromatic#dichromatic."
    ),
    vga = paste(
      "Typical colors that are used when booting PCs and leaving them in",
      "text mode, which used a 16-entry color table. The colors are",
      "different in the EGA/VGA graphic modes.",
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    winxp = paste(
      "Windows XP Console. Seen in Windows XP through Windows 8.1.",
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    vscode = paste(
      "Visual Studio Debug console, 'Dark+' theme.",
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    win10 = paste0(
      "Campbell theme, used as of Windows 10 version 1709. Also used",
      "by PowerShell 6.",
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    macos = paste0(
      "Terminal.app in macOS",
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    putty = paste0(
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    mirc = paste0(
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    xterm = paste0(
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    ubuntu = paste0(
      "For virtual terminals, from /etc/vtrgb.",
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    eclipse = paste0(
      "From https://en.wikipedia.org/wiki/ANSI_escape_code#SGR."
    ),
    iterm = "Built-in iTerm2 theme.",
    "iterm-pastel" = "Built-In iTerm2 theme.",
    "iterm-smoooooth" = "Built-In iTerm2 theme.",
    "iterm-snazzy" = "From https://github.com/sindresorhus/iterm2-snazzy.",
    "iterm-solarized" = "Built-In iTerm2 theme.",
    "iterm-tango" = "Built-In iTerm2 theme."
  )


================================================
FILE: R/combine.R
================================================
#' Combine two or more ANSI styles
#'
#' Combine two or more styles or style functions into a new style function
#' that can be called on strings to style them.
#'
#' It does not usually make sense to combine two foreground
#' colors (or two background colors), because only the first one
#' applied will be used.
#'
#' It does make sense to combine different kind of styles,
#' e.g. background color, foreground color, bold font.
#'
#' The `$` operator can also be used to combine styles.
#' Note that the left hand side of `$` is a style function,
#' and the right hand side is the name of a style in [styles()].
#'
#' @param ... The styles to combine. They will be applied from
#'   right to left.
#' @return The combined style function.
#'
#' @export
#' @examples
#' ## Use style names
#' alert <- combine_styles("bold", "red4", "bgCyan")
#' cat(alert("Warning!"), "\n")
#'
#' ## Or style functions
#' alert <- combine_styles(bold, red, bgCyan)
#' cat(alert("Warning!"), "\n")
#'
#' ## Combine a composite style
#' alert <- combine_styles(bold, combine_styles(red, bgCyan))
#' cat(alert("Warning!"), "\n")
#'
#' ## Shorter notation
#' alert <- bold $ red $ bgCyan
#' cat(alert("Warning!"), "\n")

combine_styles <- function(...) {
  styles <- lapply(list(...), use_or_make_style)
  all_ansi <- unlist(lapply(styles, attr, "_styles"), recursive = FALSE)
  make_crayon(all_ansi)
}

#' @rdname combine_styles
#' @param crayon A style function.
#' @param style A style name that is included in `names(styles())`.
#' @export
#' @method $ crayon

`$.crayon` <- function(crayon, style) {
  attr(crayon, "_styles") <-
    c(attr(crayon, "_styles"), data_env$my_styles[style])
  crayon
}


================================================
FILE: R/crayon-package.R
================================================
## ----------------------------------------------------------------------

#' Colored terminal output
#'
#' With crayon it is easy to add color to terminal output, create styles
#' for notes, warnings, errors; and combine styles.
#'
#' ANSI color support is automatically detected and used. Crayon was largely
#' inspired by chalk \url{https://github.com/chalk/chalk}.
#'
#' @include utils.R
#' @include string.R
#' @name crayon
"_PACKAGE"


================================================
FILE: R/disposable.R
================================================
## nocov start

install_quietly <- TRUE

with_wd <- function(dir, expr) {
  wd <- getwd()
  on.exit(setwd(wd))
  setwd(dir)
  eval(substitute(expr), envir = parent.frame())
}

#' @importFrom utils tar

build_pkg <- function(path, pkg_file = NULL) {
  if (!file.exists(path)) stop("path does not exist")
  pkg_name <- basename(path)
  if (is.null(pkg_file)) {
    pkg_file <- file.path(dirname(path), paste0(pkg_name, "_1.0.tar.gz"))
  }
  with_wd(
    dirname(path),
    tar(basename(pkg_file), pkg_name, compression = "gzip")
  )
  pkg_file
}

#' @importFrom utils package.skeleton install.packages

install_tmp_pkg <- function(..., pkg_name, lib_dir, imports = character()) {
  if (!file.exists(lib_dir)) stop("lib_dir does not exist")
  if (!is.character(pkg_name) || length(pkg_name) != 1) {
    stop("pkg_name is not a string")
  }

  ## Create a directory that will contain the source package
  src_dir <- tempfile()
  on.exit(try(unlink(src_dir, recursive = TRUE), silent = TRUE), add = TRUE)
  dir.create(src_dir)

  ## Create source package, need a non-empty environment,
  ## otherwise package.skeleton fails
  tmp_env <- new.env()
  assign("f", function(x) x, envir = tmp_env)
  suppressMessages(package.skeleton(
    pkg_name,
    path = src_dir,
    environment = tmp_env
  ))
  pkg_dir <- file.path(src_dir, pkg_name)

  ## Make it installable: remove man, add imports
  unlink(file.path(pkg_dir, "man"), recursive = TRUE)
  if (length(imports) != 0) {
    cat(
      "Imports: ",
      paste(imports, collapse = ", "),
      "\n",
      file = file.path(pkg_dir, "DESCRIPTION"),
      append = TRUE
    )
    cat(
      paste0("import(", imports, ")"),
      sep = "\n",
      file = file.path(pkg_dir, "NAMESPACE"),
      append = TRUE
    )
  }

  ## Put the code in it, dput is noisy, so we need to redirect it to
  ## temporary file
  exprs <- list(...)
  unlink(file.path(pkg_dir, "R"), recursive = TRUE)
  dir.create(file.path(pkg_dir, "R"))
  code_file <- file.path(pkg_dir, "R", "code.R")
  tmp_file <- tempfile()
  on.exit(try(unlink(tmp_file), silent = TRUE), add = TRUE)
  sapply(
    exprs,
    function(x)
      cat(
        deparse(dput(x, file = tmp_file)),
        file = code_file,
        append = TRUE,
        "\n",
        sep = "\n"
      )
  )

  ## Build it
  pkg_file <- build_pkg(pkg_dir)

  ## Install it into the supplied lib_dir
  install.packages(
    pkg_file,
    lib = lib_dir,
    repos = NULL,
    type = "source",
    quiet = install_quietly
  )
}

with_libpath <- function(lib_path, ...) {
  cur_lib_path <- .libPaths()
  on.exit(.libPaths(cur_lib_path), add = TRUE)
  .libPaths(c(lib_path, cur_lib_path))
  exprs <- c(as.list(match.call(expand.dots = FALSE)$...))
  sapply(exprs, eval, envir = parent.frame())
}

make_packages <- function(..., lib_dir = tempfile(), imports = character()) {
  if (!file.exists(lib_dir)) dir.create(lib_dir)
  exprs <- c(as.list(match.call(expand.dots = FALSE)$...))
  for (i in seq_along(exprs)) {
    expr <- exprs[[i]]
    name <- names(exprs)[i]
    install_tmp_pkg(expr, pkg_name = name, lib_dir = lib_dir, imports = imports)
    ## Unload everything if an error happens
    on.exit(try(unloadNamespace(name), silent = TRUE), add = TRUE)
    with_libpath(
      lib_dir,
      suppressMessages(library(name, quietly = TRUE, character.only = TRUE))
    )
    on.exit()
  }
  invisible(lib_dir)
}

## nocov end


================================================
FILE: R/enc-utils.R
================================================
# keep encoding, even if useBytes = TRUE

Encoding_ <- function(x) {
  if (is.factor(x) && length(levels(x)) < length(x)) {
    Encoding(levels(x))
  } else {
    if (!is.character(x)) x <- as.character(x)
    Encoding(x)
  }
}

sub_ <- function(pattern, replacement, x, ...) {
  enc <- Encoding_(x)
  ret <- sub(pattern, replacement, x, ...)
  if (length(ret)) Encoding(ret) <- enc
  ret
}

gsub_ <- function(pattern, replacement, x, ...) {
  enc <- Encoding_(x)
  ret <- gsub(pattern, replacement, x, ...)
  if (length(ret)) Encoding(ret) <- enc
  ret
}

strsplit_ <- function(x, ...) {
  enc <- Encoding_(x)
  ret <- strsplit(x, ...)
  for (i in seq_along(ret)) {
    Encoding(ret[[i]]) <- enc[i]
  }
  ret
}


================================================
FILE: R/has_ansi.R
================================================
ansi_regex <- paste0(
  "(?:(?:\\x{001b}\\[)|\\x{009b})",
  "(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])",
  "|\\x{001b}[A-M]"
)

#' Check if a string has some ANSI styling
#'
#' @param string The string to check. It can also be a character
#'   vector.
#' @return Logical vector, `TRUE` for the strings that have some
#'   ANSI styling.
#'
#' @export
#' @examples
#' ## The second one has style if crayon is enabled
#' has_style("foobar")
#' has_style(red("foobar"))

has_style <- function(string) {
  grepl(ansi_regex, string, perl = TRUE)
}

#' Remove ANSI escape sequences from a string
#'
#' @param string The input string.
#' @return The cleaned up string.
#'
#' @export
#' @examples
#' strip_style(red("foobar")) == "foobar"

strip_style <- function(string) {
  gsub_(ansi_regex, "", string, perl = TRUE, useBytes = TRUE)
}


================================================
FILE: R/has_color.R
================================================
#' Does the current R session support ANSI colors?
#'
#' From crayon 2.0.0, this function is simply a wrapper on
#' [num_ansi_colors()].
#'
#' @return `TRUE` if the current R session supports color.
#'
#' @export
#' @examples
#' has_color()

has_color <- function() {
  num_ansi_colors() > 1L
}

#' Number of colors the terminal supports
#'
#' From crayon version 2.0.0, this function is a simple wrapper on
#' [num_ansi_colors()], with the additional twist that the `crayon.colors`
#' option is still obseved, and takes precedence, for compatibility.
#'
#' @param forget Ignored. Included for backwards compatibility.
#' @return Number of ANSI colors.
#'
#' @export
#' @examples
#' num_colors()

num_colors <- function(forget = FALSE) {
  cray_opt_num <- getOption("crayon.colors", NULL)
  if (!is.null(cray_opt_num)) return(as.integer(cray_opt_num))
  num_ansi_colors()
}


================================================
FILE: R/link.R
================================================
#' Terminal Hyperlinks
#'
#' @details
#' hyperlink()` creates an ANSI hyperlink.
#'
#' `has_hyperlink()` checks if the current `stdout()` supports hyperlinks.
#' terminal links.
#'
#' See also
#' <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>.
#'
#' @param text Text to show. `text` and `url` are recycled to match their
#'   length, via a `paste0()` call.
#' @param url URL to link to.
#' @return Logical scalar, for `has_hyperlink()`.
#'
#' @export
#' @examples
#' cat("This is an", hyperlink("R", "https://r-project.org"), "link.\n")

hyperlink <- function(text, url) {
  if (has_hyperlink()) {
    paste0("\u001B]8;;", url, "\u0007", text, "\u001B]8;;\u0007")
  } else {
    text
  }
}

#' @export
#' @name hyperlink
#' @examples
#' has_hyperlink()

has_hyperlink <- function() {
  ## Hyperlinks forced?
  enabled <- getOption("crayon.hyperlink")
  if (!is.null(enabled)) {
    return(isTRUE(enabled))
  }

  ## Are we in a terminal? No?
  if (!isatty(stdout())) {
    return(FALSE)
  }

  ## Are we in a windows terminal?
  if (os_type() == "windows") {
    return(TRUE)
  }

  ## Better to avoid it in CIs
  if (
    nzchar(Sys.getenv("CI")) ||
      nzchar(Sys.getenv("TEAMCITY_VERSION"))
  ) {
    return(FALSE)
  }

  ## iTerm
  if (nzchar(TERM_PROGRAM <- Sys.getenv("TERM_PROGRAM"))) {
    version <- package_version(
      Sys.getenv("TERM_PROGRAM_VERSION"),
      strict = FALSE
    )

    if (TERM_PROGRAM == "iTerm.app") {
      if (!is.na(version) && version >= "3.1") return(TRUE)
    }
  }

  if (nzchar(VTE_VERSION <- Sys.getenv("VTE_VERSION"))) {
    if (package_version(VTE_VERSION) >= "0.50.1") return(TRUE)
  }

  FALSE
}


================================================
FILE: R/machinery.R
================================================
## ----------------------------------------------------------------------

crayon_template <- function(...) {
  my_styles <- attr(sys.function(), "_styles")
  text <- mypaste(...)
  nc <- num_ansi_colors()
  if (nc > 1) {
    for (st in rev(my_styles)) {
      if (!is.null(st$palette)) st <- get_palette_color(st, nc)
      text <- st$open %+%
        gsub_(st$close, st$open, text, fixed = TRUE, useBytes = TRUE) %+%
        st$close
    }
  }
  text
}

hash_color_regex <- "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$"

is_builtin_style <- function(x) {
  is_string(x) && x %in% names(builtin_styles)
}

#' @importFrom grDevices colors

is_r_color <- function(x) {
  if (!is.character(x) || length(x) != 1 || is.na(x)) {
    FALSE
  } else {
    x %in% grDevices::colors() || grepl(hash_color_regex, x)
  }
}

is_rgb_matrix <- function(x) {
  is.matrix(x) && is.numeric(x) && (nrow(x) == 3 || nrow(x) == 4)
}

#' @importFrom grDevices col2rgb

ansi_style_from_r_color <- function(color, bg, num_colors, grey) {
  style_from_rgb(col2rgb(color), bg, num_colors, grey)
}

# multicolor depends on this name, apparently

style_from_r_color <- ansi_style_from_r_color

style_8_from_rgb <- function(rgb, bg) {
  ansi_cols <- if (bg) ansi_bg_rgb else ansi_fg_rgb
  dist <- colSums((ansi_cols - as.vector(rgb))^2)
  builtin_name <- names(which.min(dist))[1]
  builtin_styles[[builtin_name]]
}

style_from_rgb <- function(rgb, bg, num_colors, grey) {
  if (num_colors < 256) {
    return(style_8_from_rgb(rgb, bg))
  }
  ansi256(rgb, bg, grey)
}

#' Create an ANSI color style
#'
#' Create a style, or a style function, or both. This function
#' is intended for those who wish to use 256 ANSI colors,
#' instead of the more widely supported eight colors.
#'
#' @details
#' The crayon package comes with predefined styles (see
#' [styles()] for a list) and functions for the basic eight-color
#' ANSI standard (`red`, `blue`, etc., see \link{crayon}).
#'
#' There are no predefined styles or style functions for the 256 color
#' ANSI mode, however, because we simply did not want to create that
#' many styles and functions. Instead, `make_style()` can be
#' used to create a style (or a style function, or both).
#'
#' There are two ways to use this function: \enumerate{
#'   \item If its first argument is not named, then it returns a function
#'     that can be used to color strings.
#'   \item If its first argument is named, then it also creates a
#'     style with the given name. This style can be used in
#'     [style()]. One can still use the return value
#'     of the function, to create a style function.
#' }
#'
#' The style (the \code{...} argument) can be anything of the
#' following: \itemize{
#'   \item An R color name, see [colors()].
#'   \item A 6- or 8-digit hexa color string, e.g. `#ff0000` means
#'     red. Transparency (alpha channel) values are ignored.
#'   \item A one-column matrix with three rows for the red, green
#'     and blue channels, as returned by `col2rgb` (in the base
#'     grDevices package).
#' }
#'
#' `make_style()` detects the number of colors to use
#' automatically (this can be overridden using the `colors`
#' argument). If the number of colors is less than 256 (detected or given),
#' then it falls back to the color in the ANSI eight color mode that
#' is closest to the specified (RGB or R) color.
#'
#' See the examples below.
#'
#' @param ... The style to create. See details and examples below.
#' @param bg Whether the color applies to the background.
#' @param grey Whether to specifically create a grey color.
#'   This flag is included because ANSI 256 has a finer color scale
#'   for greys than the usual 0:5 scale for R, G and B components.
#'   It is only used for RGB color specifications (either numerically
#'   or via a hexa string) and is ignored on eigth color ANSI
#'   terminals.
#' @param colors Number of colors, detected automatically
#'   by default.
#' @return A function that can be used to color strings.
#'
#' @family styles
#' @export
#' @examples
#' ## Create a style function without creating a style
#' pink <- make_style("pink")
#' bgMaroon <- make_style(rgb(0.93, 0.19, 0.65), bg = TRUE)
#' cat(bgMaroon(pink("I am pink if your terminal wants it, too.\n")))
#'
#' ## Create a new style for pink and maroon background
#' make_style(pink = "pink")
#' make_style(bgMaroon = rgb(0.93, 0.19, 0.65), bg = TRUE)
#' "pink" %in% names(styles())
#' "bgMaroon" %in% names(styles())
#' cat(style("I am pink, too!\n", "pink", bg = "bgMaroon"))

make_style <- function(..., bg = FALSE, grey = FALSE, colors = num_colors()) {
  args <- list(...)
  stopifnot(length(args) == 1)
  style <- args[[1]]
  orig_style_name <- style_name <- names(args)[1]

  stopifnot(
    is.character(style) &&
      length(style) == 1 ||
      is_rgb_matrix(style) && ncol(style) == 1,
    is.logical(bg) && length(bg) == 1,
    is.numeric(colors) && length(colors) == 1
  )

  ansi_seqs <- if (is_builtin_style(style)) {
    if (bg && substr(style, 1, 2) != "bg") {
      style <- "bg" %+% capitalize(style)
    }
    if (is.null(style_name)) style_name <- style
    builtin_styles[[style]]
  } else if (is_r_color(style)) {
    if (is.null(style_name)) style_name <- style
    ansi_style_from_r_color(style, bg, colors, grey)
  } else if (is_rgb_matrix(style)) {
    style_from_rgb(style, bg, colors, grey)
  } else {
    stop("Unknown style specification: ", style)
  }

  if (!is.null(orig_style_name)) define_style(orig_style_name, ansi_seqs)

  make_crayon(structure(list(ansi_seqs), names = style_name))
}

make_crayon <- function(ansi_seq) {
  crayon <- crayon_template
  attr(crayon, "_styles") <- ansi_seq
  class(crayon) <- "crayon"
  crayon
}

#' @include styles.R
#'
#' @usage
#' ## Simple styles
#' red(...)
#' bold(...)
#' # ...
#'
#' ## See more styling below
#'
#' @param ... Strings to style.
#' @name crayon
#
#' @details
#'
#' Crayon defines several styles, that can be combined. Each style in the list
#' has a corresponding function with the same name.
#'
#' @section Genaral styles:
#'
#' \itemize{
#'   \item reset
#'   \item bold
#'   \item blurred (usually called \sQuote{dim}, renamed to avoid name clash)
#'   \item italic (not widely supported)
#'   \item underline
#'   \item inverse
#'   \item hidden
#'   \item strikethrough (not widely supported)
#' }
#'
#' @section Text colors:
#'
#' \itemize{
#'   \item black
#'   \item red
#'   \item green
#'   \item yellow
#'   \item blue
#'   \item magenta
#'   \item cyan
#'   \item white
#'   \item silver (usually called \sQuote{gray}, renamed to avoid name clash)
#' }
#'
#' @section Background colors:
#'
#' \itemize{
#'   \item bgBlack
#'   \item bgRed
#'   \item bgGreen
#'   \item bgYellow
#'   \item bgBlue
#'   \item bgMagenta
#'   \item bgCyan
#'   \item bgWhite
#' }
#'
#' @section Styling:
#'
#' The styling functions take any number of character vectors as arguments,
#' and they concatenate and style them: \preformatted{  library(crayon)
#'   cat(blue("Hello", "world!\n"))
#' }
#'
#' Crayon defines the \code{\%+\%} string concatenation operator, to make it easy
#' to assemble stings with different styles. \preformatted{  cat("... to highlight the " \%+\% red("search term") \%+\%
#'       " in a block of text\n")
#' }
#'
#' Styles can be combined using the `$` operator: \preformatted{  cat(yellow$bgMagenta$bold('Hello world!\n'))
#' } See also [combine_styles()].
#'
#' Styles can also be nested, and then inner style takes
#' precedence: \preformatted{  cat(green(
#'     'I am a green line ' \%+\%
#'     blue$underline$bold('with a blue substring') \%+\%
#'     ' that becomes green again!\n'
#'   ))
#' }
#'
#' It is easy to define your own themes: \preformatted{  error <- red $ bold
#'   warn <- magenta $ underline
#'   note <- cyan
#'   cat(error("Error: subscript out of bounds!\n"))
#'   cat(warn("Warning: shorter argument was recycled.\n"))
#'   cat(note("Note: no such directory.\n"))
#' }
#'
#' @aliases
#'    reset bold blurred italic underline inverse hidden strikethrough
#'    black red green yellow blue magenta cyan white silver
#'    bgBlack bgRed bgGreen bgYellow bgBlue bgMagenta bgCyan bgWhite
#'
#' @export reset bold blurred italic underline inverse hidden strikethrough
#' @export black red green yellow blue magenta cyan white silver
#' @export bgBlack bgRed bgGreen bgYellow bgBlue bgMagenta bgCyan bgWhite
#'
#' @seealso [make_style()] for using the 256 ANSI colors.
#' @examples
#' cat(blue("Hello", "world!"))
#'
#' cat("... to highlight the " %+% red("search term") %+%
#'     " in a block of text")
#'
#' cat(yellow$bgMagenta$bold('Hello world!'))
#'
#' cat(green(
#'  'I am a green line ' %+%
#'  blue$underline$bold('with a blue substring') %+%
#'  ' that becomes green again!'
#' ))
#'
#' error <- red $ bold
#' warn <- magenta $ underline
#' note <- cyan
#' cat(error("Error: subscript out of bounds!\n"))
#' cat(warn("Warning: shorter argument was recycled.\n"))
#' cat(note("Note: no such directory.\n"))
#'
NULL

#' ANSI escape sequences of crayon styles
#'
#' You can use this function to list all availables crayon styles,
#' via `names(styles())`, or to explicitly apply an ANSI
#' escape seauence to a string.
#'
#' @return A named list. Each list element is a list of two
#'   strings, named \sQuote{open} and \sQuote{close}.
#'
#' @seealso [crayon()] for the beginning of the crayon manual.
#' @export
#' @examples
#' names(styles())
#' cat(styles()[["bold"]]$close)

styles <- function() {
  data_env$my_styles
}

data_env <- new.env(parent = emptyenv())

data_env$my_styles <- structure(list(), names = character())

sapply(names(builtin_styles), function(style) {
  data_env$my_styles[[style]] <- builtin_styles[[style]]
  assign(style, make_style(style), envir = asNamespace("crayon"))
})

define_style <- function(name, ansi_seq) {
  data_env$my_styles[[name]] <- ansi_seq
}

#' Remove a style
#'
#' @param style The name of the style to remove. No error is given
#'   for non-existing names.
#' @return Nothing.
#'
#' @family styles
#' @export
#' @examples
#' make_style(new_style = "maroon", bg = TRUE)
#' cat(style("I am maroon", "new_style"), "\n")
#' drop_style("new_style")
#' "new_style" %in% names(styles())

drop_style <- function(style) {
  data_env$my_styles[[style]] <- NULL
  invisible()
}


================================================
FILE: R/parts.R
================================================
#' Switch on or off a style
#'
#' Make a style active. The text printed to the screen from now
#' on will use this style.
#'
#' @details
#' This function is very rarely needed, e.g. for colored user
#' input. For other reasons, just call the style as a function on
#' the string.
#'
#' @param x Style.
#' @param ... Ignored.
#'
#' @export
#' @examples
#' ## The input is red (if color is supported)
#' get_name <- function() {
#'   cat("Enter your name:", start(red))
#'   input <- readline()
#'   cat(finish(red))
#'   input
#' }
#' name <- get_name()
#' name

start.crayon <- function(x, ...) {
  if (has_color()) {
    paste(
      vapply(attr(x, "_styles"), "[[", "", "open"),
      collapse = ""
    )
  } else {
    ""
  }
}

#' @importFrom stats start
#' @rdname start.crayon
#' @export

finish <- function(x, ...) UseMethod("finish")

#' @rdname start.crayon
#' @export
#' @method finish crayon

finish.crayon <- function(x, ...) {
  if (has_color()) {
    paste(
      rev(vapply(attr(x, "_styles"), "[[", "", "close")),
      collapse = ""
    )
  } else {
    ""
  }
}


================================================
FILE: R/print.R
================================================
#' @export
#' @method print crayon

print.crayon <- function(x, ...) {
  st <- paste(names(attr(x, "_styles")), collapse = ", ")
  cat(sep = "", "Crayon style function, ", st, ": ", x("example output."), "\n")
}


================================================
FILE: R/show.R
================================================
#' @include style-var.R
NULL

#' Show the ANSI color table on the screen
#'
#' @param colors Number of colors to show, meaningful values
#'   are 8 and 256. It is automatically set to the number of
#'   supported colors, if not specified.
#' @return The printed string, invisibly.
#'
#' @export

show_ansi_colors <- function(colors = num_colors()) {
  if (colors < 8) {
    cat("Colors are not supported")
  } else if (colors < 256) {
    cat(ansi_colors_8(), sep = "")
    invisible(ansi_colors_8)
  } else {
    cat(ansi_colors_256(), sep = "")
    invisible(ansi_colors_256)
  }
}

#' @importFrom grDevices rgb

ansi_colors_256_col <- function() {
  sapply(0:5, function(r) {
    sapply(0:5, function(g) {
      c(
        sapply(0:5, function(b) {
          s <- paste0("r:", r, " g:", g, " b:", b, " ")
          style(s, as = "grey", bg = rgb(r, g, b, maxColorValue = 5))
        }),
        "\n"
      )
    })
  })
}

#' @importFrom grDevices grey

ansi_colors_256_grey <- function() {
  sapply(0:23, function(g) {
    s <- paste0(" grey ", format(g, width = 2), "    ")
    style(
      s,
      as = "grey",
      bg = make_style(grey(g / 23), grey = TRUE, bg = TRUE)
    ) %+%
      (if ((g + 1) %% 6) "" else "\n")
  })
}

ansi_colors_256 <- function() {
  c(ansi_colors_256_col(), "\n", ansi_colors_256_grey())
}

ansi_colors_8 <- function() {
  multicol(sapply(seq_along(builtin_styles), function(s) {
    st <- names(builtin_styles)[s]
    styled <- st %+% ": " %+% style("foobar", as = st) %+% " "
  }))
}


================================================
FILE: R/string.R
================================================
#' Convert to character
#'
#' This function just calls [as.character()], but it is
#' easier to type and read.
#'
#' @param x Object to be coerced.
#' @param ... Further arguments to pass to `as.character()`.
#' @return Character value.
#'
#' @export

chr <- function(x, ...) as.character(x, ...)

#' Concatenate character vectors
#'
#' The length of the two arguments must match, or
#' one of them must be of length one. If the length of
#' one argument is one, then the output's length will
#' match the length of the other argument. See examples
#' below.
#'
#' @param lhs Left hand side, character vector.
#' @param rhs Right hand side, character vector.
#' @return Concatenated vectors.
#'
#' @name concat
#' @export
#'
#' @examples
#' "foo" %+% "bar"
#'
#' letters[1:10] %+% chr(1:10)
#'
#' letters[1:10] %+% "-" %+% chr(1:10)
#'
#' ## This is empty (unlike for parse)
#' character() %+% "*"

`%+%` <- function(lhs, rhs) {
  stopifnot(is.character(lhs), is.character(rhs))
  stopifnot(length(lhs) == length(rhs) || length(lhs) == 1 || length(rhs) == 1)

  if (length(lhs) == 0 && length(rhs) == 0) {
    paste0(lhs, rhs)
  } else if (length(lhs) == 0) {
    lhs
  } else if (length(rhs) == 0) {
    rhs
  } else {
    paste0(lhs, rhs)
  }
}

#' @export
as.character.crayon <- function(x, ...) {
  start(x)
}

#' @export
`-.crayon` <- function(e1, e2) {
  if (!missing(e2)) {
    base::`-`(e1, e2)
  }
  my_styles <- attr(e1, "_styles")
  if (has_color()) {
    for (i in seq_along(my_styles)) {
      my_styles[[i]]$open <- my_styles[[i]]$close
    }
  }
  attr(e1, "_styles") <- my_styles
  e1
}


================================================
FILE: R/string_operations.R
================================================
## Create a mapping between the string and its style-less version.
## This is useful to work with the colored string.

#' @importFrom utils tail

map_to_ansi <- function(x, text = NULL) {
  if (is.null(text)) {
    text <- non_matching(re_table(ansi_regex, x), x, empty = TRUE)
  }

  map <- lapply(
    text,
    function(text) {
      cbind(
        pos = cumsum(c(1, text[, "length"], Inf)),
        offset = c(text[, "start"] - 1, tail(text[, "end"], 1), NA)
      )
    }
  )

  function(pos) {
    pos <- rep(pos, length.out = length(map))
    mapply(pos, map, FUN = function(pos, table) {
      if (pos < 1) {
        pos
      } else {
        slot <- which(pos < table[, "pos"])[1] - 1
        table[slot, "offset"] + pos - table[slot, "pos"] + 1
      }
    })
  }
}


#' Count number of characters in an ANSI colored string
#'
#' This is a color-aware counterpart of [base::nchar()],
#' which does not do well, since it also counts the ANSI control
#' characters.
#'
#' @param x Character vector, potentially ANSO styled, or a vector to be
#'   coarced to character.
#' @param ... Additional arguments, passed on to `base::nchar()`
#'   after removing ANSI escape sequences.
#' @return Numeric vector, the length of the strings in the character
#'   vector.
#'
#' @family ANSI string operations
#' @export
#' @examples
#' str <- paste(
#'   red("red"),
#'   "default",
#'   green("green")
#' )
#'
#' cat(str, "\n")
#' nchar(str)
#' col_nchar(str)
#' nchar(strip_style(str))

col_nchar <- function(x, ...) {
  base::nchar(strip_style(x), ...)
}


#' Substring(s) of an ANSI colored string
#'
#' This is a color-aware counterpart of [base::substr()].
#' It works exactly like the original, but keeps the colors
#' in the substrings. The ANSI escape sequences are ignored when
#' calculating the positions within the string.
#'
#' @param x Character vector, potentially ANSI styled, or a vector to
#'   coarced to character.
#' @param start Starting index or indices, recycled to match the length
#'   of `x`.
#' @param stop Ending index or indices, recycled to match the length
#'   of `x`.
#' @return Character vector of the same length as `x`, containing
#'   the requested substrings. ANSI styles are retained.
#'
#' @family ANSI string operations
#' @export
#' @examples
#' str <- paste(
#'   red("red"),
#'   "default",
#'   green("green")
#' )
#'
#' cat(str, "\n")
#' cat(col_substr(str, 1, 5), "\n")
#' cat(col_substr(str, 1, 15), "\n")
#' cat(col_substr(str, 3, 7), "\n")
#'
#' substr(strip_style(str), 1, 5)
#' substr(strip_style(str), 1, 15)
#' substr(strip_style(str), 3, 7)
#'
#' str2 <- "another " %+%
#'   red("multi-", sep = "", underline("style")) %+%
#'   " text"
#'
#' cat(str2, "\n")
#' cat(col_substr(c(str, str2), c(3,5), c(7, 18)), sep = "\n")
#' substr(strip_style(c(str, str2)), c(3,5), c(7, 18))

col_substr <- function(x, start, stop) {
  if (!is.character(x)) x <- as.character(x)
  if (!length(x)) return(x)
  start <- as.integer(start)
  stop <- as.integer(stop)
  if (!length(start) || !length(stop)) stop("invalid substring arguments")
  if (anyNA(start) || anyNA(stop))
    stop("non-numeric substring arguments not supported")
  ansi <- re_table(ansi_regex, x)
  text <- non_matching(ansi, x, empty = TRUE)
  mapper <- map_to_ansi(x, text = text)
  nstart <- mapper(start)
  nstop <- mapper(stop)

  bef <- base::substr(x, 1, nstart - 1)
  aft <- base::substr(x, nstop + 1, base::nchar(x))
  ansi_bef <- vapply(
    regmatches(bef, gregexpr(ansi_regex, bef)),
    paste,
    collapse = "",
    FUN.VALUE = ""
  )
  ansi_aft <- vapply(
    regmatches(aft, gregexpr(ansi_regex, aft)),
    paste,
    collapse = "",
    FUN.VALUE = ""
  )

  paste(sep = "", ansi_bef, base::substr(x, nstart, nstop), ansi_aft)
}

#' Substring(s) of an ANSI colored string
#'
#' This is the color-aware counterpart of [base::substring()].
#' It works exactly like the original, but keeps the colors in the
#' substrings. The ANSI escape sequences are ignored when
#' calculating the positions within the string.
#'
#' @param text Character vector, potentially ANSI styled, or a vector to
#'   coarced to character. It is recycled to the longest of `first`
#'   and `last`.
#' @param first Starting index or indices, recycled to match the length
#'   of `x`.
#' @param last Ending index or indices, recycled to match the length
#'   of `x`.
#' @return Character vector of the same length as `x`, containing
#'   the requested substrings. ANSI styles are retained.
#'
#' @family ANSI string operations
#' @export
#' @examples
#' str <- paste(
#'   red("red"),
#'   "default",
#'   green("green")
#' )
#'
#' cat(str, "\n")
#' cat(col_substring(str, 1, 5), "\n")
#' cat(col_substring(str, 1, 15), "\n")
#' cat(col_substring(str, 3, 7), "\n")
#'
#' substring(strip_style(str), 1, 5)
#' substring(strip_style(str), 1, 15)
#' substring(strip_style(str), 3, 7)
#'
#' str2 <- "another " %+%
#'   red("multi-", sep = "", underline("style")) %+%
#'   " text"
#'
#' cat(str2, "\n")
#' cat(col_substring(str2, c(3,5), c(7, 18)), sep = "\n")
#' substring(strip_style(str2), c(3,5), c(7, 18))

col_substring <- function(text, first, last = 1000000L) {
  if (!is.character(text)) text <- as.character(text)
  n <- max(lt <- length(text), length(first), length(last))
  if (lt && lt < n) text <- rep_len(text, length.out = n)
  col_substr(text, as.integer(first), as.integer(last))
}


#' Split an ANSI colored string
#'
#' This is the color-aware counterpart of [base::strsplit()].
#' It works almost exactly like the original, but keeps the colors in the
#' substrings.
#'
#' @param x Character vector, potentially ANSI styled, or a vector to
#'   coarced to character.
#' @param split Character vector of length 1 (or object which can be coerced to
#'   such) containing regular expression(s) (unless `fixed = TRUE`) to use
#'   for splitting.  If empty matches occur, in particular if `split` has
#'   zero characters, `x` is split into single characters.
#' @param ... Extra arguments are passed to `base::strsplit()`.
#' @return A list of the same length as `x`, the \eqn{i}-th element of
#'   which contains the vector of splits of `x[i]`. ANSI styles are
#'   retained.
#'
#' @family ANSI string operations
#' @export
#' @importFrom utils head
#' @examples
#' str <- red("I am red---") %+%
#'   green("and I am green-") %+%
#'   underline("I underlined")
#'
#' cat(str, "\n")
#'
#' # split at dashes, keep color
#' cat(col_strsplit(str, "[-]+")[[1]], sep = "\n")
#' strsplit(strip_style(str), "[-]+")
#'
#' # split to characters, keep color
#' cat(col_strsplit(str, "")[[1]], "\n", sep = " ")
#' strsplit(strip_style(str), "")

col_strsplit <- function(x, split, ...) {
  split <- try(as.character(split), silent = TRUE)
  if (
    inherits(split, "try-error") || !is.character(split) || length(split) > 1L
  )
    stop("`split` must be character of length <= 1, or must coerce to that")
  if (!length(split)) split <- ""
  plain <- strip_style(x)
  splits <- re_table(split, plain, ...)
  chunks <- non_matching(splits, plain, empty = TRUE)
  # silently recycle `split`; doesn't matter currently since we don't support
  # split longer than 1, but might in future
  split.r <- rep(split, length.out = length(x))
  # Drop empty chunks to align with `substr` behavior
  chunks <- lapply(
    seq_along(chunks),
    function(i) {
      y <- chunks[[i]]
      # empty split means drop empty first match
      if (nrow(y) && !nzchar(split.r[[i]]) && !head(y, 1L)[, "length"]) {
        y <- y[-1L, , drop = FALSE]
      }
      # drop empty last matches
      if (nrow(y) && !tail(y, 1L)[, "length"]) y[-nrow(y), , drop = FALSE] else
        y
    }
  )
  zero.chunks <- !vapply(chunks, nrow, integer(1L))
  # Pull out zero chunks from colored string b/c col_substring won't work
  # with them
  res <- vector("list", length(chunks))
  res[zero.chunks] <- list(character(0L))
  res[!zero.chunks] <- mapply(
    chunks[!zero.chunks],
    x[!zero.chunks],
    SIMPLIFY = FALSE,
    FUN = function(tab, xx) col_substring(xx, tab[, "start"], tab[, "end"])
  )
  res
}

#' Align an ANSI colored string
#'
#' @param text The character vector to align.
#' @param width Width of the field to align in.
#' @param align Whether to align `"left"`, `"center"` or `"right"`.
#' @param type Passed on to [col_nchar()] and there to [nchar()]
#' @return The aligned character vector.
#'
#' @family ANSI string operations
#' @export
#' @examples
#' col_align(red("foobar"), 20, "left")
#' col_align(red("foobar"), 20, "center")
#' col_align(red("foobar"), 20, "right")

col_align <- function(
  text,
  width = getOption("width"),
  align = c("left", "center", "right"),
  type = "width"
) {
  align <- match.arg(align)
  nc <- col_nchar(text, type = type)

  if (!length(text)) return(text)

  if (align == "left") {
    paste0(text, make_space(width - nc))
  } else if (align == "center") {
    paste0(
      make_space(ceiling((width - nc) / 2)),
      text,
      make_space(floor((width - nc) / 2))
    )
  } else {
    paste0(make_space(width - nc), text)
  }
}

make_space <- function(num, filling = " ") {
  num <- pmax(0, num)
  res <- strrep(filling, num)
  Encoding(res) <- Encoding(filling)
  res
}

strrep <- function(x, times) {
  x = as.character(x)
  if (length(x) == 0L) return(x)

  mapply(
    function(x, times) {
      if (is.na(x) || is.na(times)) {
        NA_character_
      } else if (times <= 0L) {
        ""
      } else {
        paste0(rep(x, times), collapse = "")
      }
    },
    x,
    times,
    USE.NAMES = FALSE
  )
}


================================================
FILE: R/style-var.R
================================================
#' Add style to a string
#'
#' See `names(styles)`, or the crayon manual for available styles.
#'
#' @param string Character vector to style.
#' @param as Style function to apply, either the function object,
#'   or its name, or an object to pass to [make_style()].
#' @param bg Background style, a style function, or a name that
#'   is passed to [make_style()].
#' @return Styled character vector.
#'
#' @export
#' @importFrom methods is
#'
#' @examples
#' ## These are equivalent
#' style("foobar", bold)
#' style("foobar", "bold")
#' bold("foobar")

style <- function(string, as = NULL, bg = NULL) {
  as <- use_or_make_style(as)
  bg <- use_or_make_style(bg, bg = TRUE)

  if (!is(as, "crayon")) stop("Cannot make style from 'as'")
  if (!is(bg, "crayon")) stop("Cannot make style from 'bg'")

  as(bg(string))
}

#' @importFrom methods is

use_or_make_style <- function(style, bg = FALSE) {
  if (is.null(style)) {
    structure(base::identity, class = "crayon")
  } else if (is(style, "crayon")) {
    style
  } else if (style %in% names(styles())) {
    make_crayon(styles()[style])
  } else {
    make_style(style, bg = bg)
  }
}


================================================
FILE: R/styles.R
================================================
palette_idx <- function(id) {
  ifelse(
    id < 38,
    id - (30 - 1),
    ifelse(
      id < 48,
      -(id - (40 - 1)),
      ifelse(
        id < 98,
        id - (90 - 9),
        -(id - (100 - 9))
      )
    )
  )
}

palette_color <- function(x) {
  c(x, palette = palette_idx(x[[1]]))
}

## ----------------------------------------------------------------------
## Styles

codes <- list(
  reset = list(0, c(0, 22, 23, 24, 27, 28, 29, 39, 49)),
  bold = list(1, 22), # 21 isn't widely supported and 22 does the same thing
  blurred = list(2, 22),
  italic = list(3, 23),
  underline = list(4, 24),
  inverse = list(7, 27),
  hidden = list(8, 28),
  strikethrough = list(9, 29),

  black = palette_color(list(30, 39)),
  red = palette_color(list(31, 39)),
  green = palette_color(list(32, 39)),
  yellow = palette_color(list(33, 39)),
  blue = palette_color(list(34, 39)),
  magenta = palette_color(list(35, 39)),
  cyan = palette_color(list(36, 39)),
  white = palette_color(list(37, 39)),
  silver = palette_color(list(90, 39)),

  bgBlack = palette_color(list(40, 49)),
  bgRed = palette_color(list(41, 49)),
  bgGreen = palette_color(list(42, 49)),
  bgYellow = palette_color(list(43, 49)),
  bgBlue = palette_color(list(44, 49)),
  bgMagenta = palette_color(list(45, 49)),
  bgCyan = palette_color(list(46, 49)),
  bgWhite = palette_color(list(47, 49))
)

## ANSI fg color -> R color

ansi_fg_r <- c(
  "black" = "black",
  "red" = "red",
  "green" = "green",
  "yellow" = "yellow",
  "blue" = "blue",
  "magenta" = "magenta",
  "cyan" = "cyan",
  "white" = "white",
  "silver" = "grey"
)

ansi_fg_rgb <- col2rgb(ansi_fg_r)

ansi_bg_r <- c(
  "bgBlack" = "black",
  "bgRed" = "red",
  "bgGreen" = "green",
  "bgYellow" = "yellow",
  "bgBlue" = "blue",
  "bgMagenta" = "magenta",
  "bgCyan" = "cyan",
  "bgWhite" = "white"
)

ansi_bg_rgb <- col2rgb(ansi_bg_r)

# code can have length > 1, used to generate the closing tags for reset

make_chr_ansi_tag <- function(code)
  paste0('\u001b[', chr(code), 'm', collapse = "")

make_chr_style <- function(code) {
  list(
    open = make_chr_ansi_tag(codes[[code]][[1]]),
    close = make_chr_ansi_tag(codes[[code]][[2]]),
    palette = if (length(codes[[code]]) >= 3) codes[[code]][[3]]
  )
}

builtin_styles <- lapply(names(codes), make_chr_style)
names(builtin_styles) <- names(codes)


================================================
FILE: R/utils.R
================================================
is_string <- function(x) {
  is.character(x) && length(x) == 1 && !is.na(x)
}

check_string <- function(x) {
  stopifnot(is_string(x))
}

mypaste <- function(..., sep = " ") {
  args <- lapply(list(...), as.character)
  len <- setdiff(sapply(args, length), 1)
  if (length(len) > 1) {
    stop("All character vectors must have the same length (or length 1)")
  }

  paste(..., sep = sep)
}

scale <- function(x, from = c(0, 255), to = c(0, 5), round = TRUE) {
  y <- (x - from[1]) /
    (from[2] - from[1]) *
    (to[2] - to[1]) +
    to[1]

  if (round) {
    round(y)
  } else {
    y
  }
}

capitalize <- function(x) {
  substr(x, 1, 1) <- toupper(substr(x, 1, 1))
  x
}

multicol <- function(x) {
  xs <- strip_style(x)
  max_len <- max(nchar(xs))
  to_add <- max_len - nchar(xs)
  x <- paste0(x, substring("            ", 1, to_add))
  screen_width <- getOption("width")
  num_cols <- trunc(screen_width / max_len)
  num_rows <- ceiling(length(x) / num_cols)
  x <- c(x, rep("", num_cols * num_rows - length(x)))
  xm <- matrix(x, ncol = num_cols, byrow = TRUE)
  apply(xm, 1, paste, collapse = "") %+% "\n"
}

re_table <- function(...) {
  lapply(gregexpr(...), function(x) {
    res <- cbind(
      start = x,
      end = x + attr(x, "match.length") - 1,
      length = attr(x, "match.length")
    )
    res <- res[res[, "start"] != -1, , drop = FALSE]
  })
}

## Create the non-matching table from the matching table

non_matching <- function(table, str, empty = FALSE) {
  mapply(table, str, SIMPLIFY = FALSE, FUN = function(t, s) {
    if (!nrow(t)) {
      cbind(start = 1, end = base::nchar(s), length = base::nchar(s))
    } else {
      start <- c(1, t[, "end"] + 1)
      end <- c(t[, "start"] - 1, base::nchar(s))
      res <- cbind(start = start, end = end, length = end - start + 1)
      if (!empty) res[res[, "length"] != 0, , drop = FALSE] else res
    }
  })
}

myseq <- function(from, to, by = 1) {
  stopifnot(by != 0)
  if (by > 0) {
    if (to < from) {
      integer()
    } else {
      seq(from, to, by = by)
    }
  } else {
    if (to > from) {
      integer()
    } else {
      seq(from, to, by = by)
    }
  }
}

`%:%` <- myseq

emacs_version <- function() {
  ver <- Sys.getenv("INSIDE_EMACS")
  ver <- gsub("[^0-9\\.]+", "", ver)
  if (ver == "") return(NA_integer_)
  ver <- strsplit(ver, ".", fixed = TRUE)[[1]]
  as.numeric(ver)
}

inside_emacs <- function() {
  Sys.getenv("EMACS") != "" || Sys.getenv("INSIDE_EMACS") != ""
}

rstudio_with_ansi_support <- function() {
  if (Sys.getenv("RSTUDIO", "") == "") return(FALSE)

  ## This is set *before* the rstudio initialization, in 1.1 and above
  if (
    (cols <- Sys.getenv("RSTUDIO_CONSOLE_COLOR", "")) != "" &&
      !is.na(as.numeric(cols))
  ) {
    return(TRUE)
  }

  ## This only works if the initialization is complete
  requireNamespace("rstudioapi", quietly = TRUE) &&
    rstudioapi::isAvailable() &&
    rstudioapi::hasFun("getConsoleHasColor")
}

rstudio_initialized <- function() {
  ## Not in RStudio, so no worries
  rs <- Sys.getenv("RSTUDIO")
  if (rs == "" || rs == "0") return(TRUE)

  ## Otherwise check
  requireNamespace("rstudioapi", quietly = TRUE) &&
    rstudioapi::isAvailable()
}

os_type <- function() {
  .Platform$OS.type
}

rstudio_detect <- function() {
  rstudio$detect()
}

is_count <- function(x) {
  is.numeric(x) &&
    length(x) == 1 &&
    !is.na(x) &&
    as.integer(x) == x &&
    x >= 0
}


================================================
FILE: README.md
================================================

## 🚀 crayon is now superseded by the cli package. 🚀

> Please use [cli](https://github.com/r-lib/cli) for new projects.
>
> crayon is still supported and will receive important bug fixes,
> but no new features.

<h1 align="center">
    <br>
    <br>
    <img width="400" src="https://user-images.githubusercontent.com/660288/102484487-41cd2900-405e-11eb-87d4-65793ad9db6a.png" alt="crayon">
    <br>
    <br>
    <br>
</h1>

> Stylish terminal output in R

<!-- badges: start -->

[![Lifecycle: superseded](https://img.shields.io/badge/lifecycle-superseded-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html#superseded)
[![](https://www.r-pkg.org/badges/version/crayon)](https://r-pkg.org/pkg/crayon)
[![CRAN RStudio mirror downloads](https://cranlogs.r-pkg.org/badges/crayon)](https://r-pkg.org/pkg/crayon)
[![R-CMD-check](https://github.com/r-lib/crayon/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/crayon/actions/workflows/R-CMD-check.yaml)
[![Codecov test coverage](https://codecov.io/gh/r-lib/crayon/graph/badge.svg)](https://app.codecov.io/gh/r-lib/crayon)
<!-- badges: end -->

With crayon it is easy to add color to terminal output, create styles for notes, warnings, errors; and combine styles.

ANSI color support is automatically detected and used. Crayon was largely
inspired by [chalk](https://github.com/chalk/chalk).

## Installation

Stable version:

```r
install.packages("crayon")
```

Development version:

```r
pak::pak("r-lib/crayon")
```

## Styles

Crayon defines several styles that can be combined. Each style in the list
has a corresponding function with the same name.

### General styles

* `reset`
* `bold`
* `blurred` (usually called `dim`, renamed to avoid name clash)
* `italic` (not widely supported)
* `underline`
* `inverse`
* `hidden`
* `strikethrough` (not widely supported)

### Text colors

* `black`
* `red`
* `green`
* `yellow`
* `blue`
* `magenta`
* `cyan`
* `white`
* `silver` (usually called `gray`, renamed to avoid name clash)

### Background colors

* `bgBlack`
* `bgRed`
* `bgGreen`
* `bgYellow`
* `bgBlue`
* `bgMagenta`
* `bgCyan`
* `bgWhite`

### Screenshot on OSX

![](https://user-images.githubusercontent.com/660288/102484516-4d205480-405e-11eb-93fa-37a6cd6d4066.png)

## Usage

The styling functions take any number of character vectors as arguments,
and they concatenate and style them:

```r
library(crayon)
cat(blue("Hello", "world!\n"))
```

Crayon defines the `%+%` string concatenation operator to make it easy
to assemble strings with different styles.

```r
cat("... to highlight the " %+% red("search term") %+% " in a block of text\n")
```

Styles can be combined using the `$` operator:

```r
cat(yellow$bgMagenta$bold('Hello world!\n'))
```

Styles can also be nested, and then inner style takes precedence:

```r
cat(green(
  'I am a green line ' %+%
  blue$underline$bold('with a blue substring') %+%
  ' that becomes green again!\n'
))
```
  
It is easy to define your own themes:

```r
error <- red $ bold
warn <- magenta $ underline
note <- cyan
cat(error("Error: subscript out of bounds!\n"))
cat(warn("Warning: shorter argument was recycled.\n"))
cat(note("Note: no such directory.\n"))
```

## 256 colors

Most modern terminals support the ANSI standard for 256 colors,
and you can define new styles that make use of them. The `make_style`
function defines a new style. It can handle R's built in color names
(see the output of `colors()`) as well as RGB specifications via the
`rgb()` function. It automatically chooses the ANSI colors that
are closest to the specified R and RGB colors, and it also has
a fallback to terminals with 8 ANSI colors only.

```r
ivory <- make_style("ivory")
bgMaroon <- make_style("maroon", bg = TRUE)
fancy <- combine_styles(ivory, bgMaroon)
cat(fancy("This will have some fancy colors"), "\n")
```

![](https://user-images.githubusercontent.com/660288/102484539-53aecc00-405e-11eb-9f24-85b4c10b5e38.png)

## License

MIT @ Gábor Csárdi


================================================
FILE: _pkgdown.yml
================================================
url: http://r-lib.github.io/crayon/
template:
  bootstrap: 5

  includes:
    in_header: |
      <script src="https://cdn.jsdelivr.net/gh/posit-dev/supported-by-posit/js/badge.min.js" data-max-height="43" data-light-bg="#666f76" data-light-fg="#f9f9f9"></script>
      <script defer data-domain="r-lib.github.io/crayon,all.tidyverse.org" src="https://plausible.io/js/plausible.js"></script>

development:
  mode: auto


================================================
FILE: air.toml
================================================


================================================
FILE: codecov.yml
================================================
comment: false

coverage:
  status:
    project:
      default:
        target: auto
        threshold: 1%
        informational: true
    patch:
      default:
        target: auto
        threshold: 1%
        informational: true


================================================
FILE: crayon.Rproj
================================================
Version: 1.0

RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default

EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8

RnwWeave: Sweave
LaTeX: pdfLaTeX

AutoAppendNewline: Yes
StripTrailingWhitespace: Yes

BuildType: Package
PackageUseDevtools: Yes
PackageInstallArgs: --no-multiarch --with-keep.source


================================================
FILE: man/chr.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string.R
\name{chr}
\alias{chr}
\title{Convert to character}
\usage{
chr(x, ...)
}
\arguments{
\item{x}{Object to be coerced.}

\item{...}{Further arguments to pass to \code{as.character()}.}
}
\value{
Character value.
}
\description{
This function just calls \code{\link[=as.character]{as.character()}}, but it is
easier to type and read.
}


================================================
FILE: man/col_align.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string_operations.R
\name{col_align}
\alias{col_align}
\title{Align an ANSI colored string}
\usage{
col_align(
  text,
  width = getOption("width"),
  align = c("left", "center", "right"),
  type = "width"
)
}
\arguments{
\item{text}{The character vector to align.}

\item{width}{Width of the field to align in.}

\item{align}{Whether to align \code{"left"}, \code{"center"} or \code{"right"}.}

\item{type}{Passed on to \code{\link[=col_nchar]{col_nchar()}} and there to \code{\link[=nchar]{nchar()}}}
}
\value{
The aligned character vector.
}
\description{
Align an ANSI colored string
}
\examples{
col_align(red("foobar"), 20, "left")
col_align(red("foobar"), 20, "center")
col_align(red("foobar"), 20, "right")
}
\seealso{
Other ANSI string operations: 
\code{\link{col_nchar}()},
\code{\link{col_strsplit}()},
\code{\link{col_substr}()},
\code{\link{col_substring}()}
}
\concept{ANSI string operations}


================================================
FILE: man/col_nchar.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string_operations.R
\name{col_nchar}
\alias{col_nchar}
\title{Count number of characters in an ANSI colored string}
\usage{
col_nchar(x, ...)
}
\arguments{
\item{x}{Character vector, potentially ANSO styled, or a vector to be
coarced to character.}

\item{...}{Additional arguments, passed on to \code{base::nchar()}
after removing ANSI escape sequences.}
}
\value{
Numeric vector, the length of the strings in the character
vector.
}
\description{
This is a color-aware counterpart of \code{\link[base:nchar]{base::nchar()}},
which does not do well, since it also counts the ANSI control
characters.
}
\examples{
str <- paste(
  red("red"),
  "default",
  green("green")
)

cat(str, "\n")
nchar(str)
col_nchar(str)
nchar(strip_style(str))
}
\seealso{
Other ANSI string operations: 
\code{\link{col_align}()},
\code{\link{col_strsplit}()},
\code{\link{col_substr}()},
\code{\link{col_substring}()}
}
\concept{ANSI string operations}


================================================
FILE: man/col_strsplit.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string_operations.R
\name{col_strsplit}
\alias{col_strsplit}
\title{Split an ANSI colored string}
\usage{
col_strsplit(x, split, ...)
}
\arguments{
\item{x}{Character vector, potentially ANSI styled, or a vector to
coarced to character.}

\item{split}{Character vector of length 1 (or object which can be coerced to
such) containing regular expression(s) (unless \code{fixed = TRUE}) to use
for splitting.  If empty matches occur, in particular if \code{split} has
zero characters, \code{x} is split into single characters.}

\item{...}{Extra arguments are passed to \code{base::strsplit()}.}
}
\value{
A list of the same length as \code{x}, the \eqn{i}-th element of
which contains the vector of splits of \code{x[i]}. ANSI styles are
retained.
}
\description{
This is the color-aware counterpart of \code{\link[base:strsplit]{base::strsplit()}}.
It works almost exactly like the original, but keeps the colors in the
substrings.
}
\examples{
str <- red("I am red---") \%+\%
  green("and I am green-") \%+\%
  underline("I underlined")

cat(str, "\n")

# split at dashes, keep color
cat(col_strsplit(str, "[-]+")[[1]], sep = "\n")
strsplit(strip_style(str), "[-]+")

# split to characters, keep color
cat(col_strsplit(str, "")[[1]], "\n", sep = " ")
strsplit(strip_style(str), "")
}
\seealso{
Other ANSI string operations: 
\code{\link{col_align}()},
\code{\link{col_nchar}()},
\code{\link{col_substr}()},
\code{\link{col_substring}()}
}
\concept{ANSI string operations}


================================================
FILE: man/col_substr.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string_operations.R
\name{col_substr}
\alias{col_substr}
\title{Substring(s) of an ANSI colored string}
\usage{
col_substr(x, start, stop)
}
\arguments{
\item{x}{Character vector, potentially ANSI styled, or a vector to
coarced to character.}

\item{start}{Starting index or indices, recycled to match the length
of \code{x}.}

\item{stop}{Ending index or indices, recycled to match the length
of \code{x}.}
}
\value{
Character vector of the same length as \code{x}, containing
the requested substrings. ANSI styles are retained.
}
\description{
This is a color-aware counterpart of \code{\link[base:substr]{base::substr()}}.
It works exactly like the original, but keeps the colors
in the substrings. The ANSI escape sequences are ignored when
calculating the positions within the string.
}
\examples{
str <- paste(
  red("red"),
  "default",
  green("green")
)

cat(str, "\n")
cat(col_substr(str, 1, 5), "\n")
cat(col_substr(str, 1, 15), "\n")
cat(col_substr(str, 3, 7), "\n")

substr(strip_style(str), 1, 5)
substr(strip_style(str), 1, 15)
substr(strip_style(str), 3, 7)

str2 <- "another " \%+\%
  red("multi-", sep = "", underline("style")) \%+\%
  " text"

cat(str2, "\n")
cat(col_substr(c(str, str2), c(3,5), c(7, 18)), sep = "\n")
substr(strip_style(c(str, str2)), c(3,5), c(7, 18))
}
\seealso{
Other ANSI string operations: 
\code{\link{col_align}()},
\code{\link{col_nchar}()},
\code{\link{col_strsplit}()},
\code{\link{col_substring}()}
}
\concept{ANSI string operations}


================================================
FILE: man/col_substring.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string_operations.R
\name{col_substring}
\alias{col_substring}
\title{Substring(s) of an ANSI colored string}
\usage{
col_substring(text, first, last = 1000000L)
}
\arguments{
\item{text}{Character vector, potentially ANSI styled, or a vector to
coarced to character. It is recycled to the longest of \code{first}
and \code{last}.}

\item{first}{Starting index or indices, recycled to match the length
of \code{x}.}

\item{last}{Ending index or indices, recycled to match the length
of \code{x}.}
}
\value{
Character vector of the same length as \code{x}, containing
the requested substrings. ANSI styles are retained.
}
\description{
This is the color-aware counterpart of \code{\link[base:substr]{base::substring()}}.
It works exactly like the original, but keeps the colors in the
substrings. The ANSI escape sequences are ignored when
calculating the positions within the string.
}
\examples{
str <- paste(
  red("red"),
  "default",
  green("green")
)

cat(str, "\n")
cat(col_substring(str, 1, 5), "\n")
cat(col_substring(str, 1, 15), "\n")
cat(col_substring(str, 3, 7), "\n")

substring(strip_style(str), 1, 5)
substring(strip_style(str), 1, 15)
substring(strip_style(str), 3, 7)

str2 <- "another " \%+\%
  red("multi-", sep = "", underline("style")) \%+\%
  " text"

cat(str2, "\n")
cat(col_substring(str2, c(3,5), c(7, 18)), sep = "\n")
substring(strip_style(str2), c(3,5), c(7, 18))
}
\seealso{
Other ANSI string operations: 
\code{\link{col_align}()},
\code{\link{col_nchar}()},
\code{\link{col_strsplit}()},
\code{\link{col_substr}()}
}
\concept{ANSI string operations}


================================================
FILE: man/combine_styles.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/combine.R
\name{combine_styles}
\alias{combine_styles}
\alias{$.crayon}
\title{Combine two or more ANSI styles}
\usage{
combine_styles(...)

\method{$}{crayon}(crayon, style)
}
\arguments{
\item{...}{The styles to combine. They will be applied from
right to left.}

\item{crayon}{A style function.}

\item{style}{A style name that is included in \code{names(styles())}.}
}
\value{
The combined style function.
}
\description{
Combine two or more styles or style functions into a new style function
that can be called on strings to style them.
}
\details{
It does not usually make sense to combine two foreground
colors (or two background colors), because only the first one
applied will be used.

It does make sense to combine different kind of styles,
e.g. background color, foreground color, bold font.

The \code{$} operator can also be used to combine styles.
Note that the left hand side of \code{$} is a style function,
and the right hand side is the name of a style in \code{\link[=styles]{styles()}}.
}
\examples{
## Use style names
alert <- combine_styles("bold", "red4", "bgCyan")
cat(alert("Warning!"), "\n")

## Or style functions
alert <- combine_styles(bold, red, bgCyan)
cat(alert("Warning!"), "\n")

## Combine a composite style
alert <- combine_styles(bold, combine_styles(red, bgCyan))
cat(alert("Warning!"), "\n")

## Shorter notation
alert <- bold $ red $ bgCyan
cat(alert("Warning!"), "\n")
}


================================================
FILE: man/concat.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/string.R
\name{concat}
\alias{concat}
\alias{\%+\%}
\title{Concatenate character vectors}
\usage{
lhs \%+\% rhs
}
\arguments{
\item{lhs}{Left hand side, character vector.}

\item{rhs}{Right hand side, character vector.}
}
\value{
Concatenated vectors.
}
\description{
The length of the two arguments must match, or
one of them must be of length one. If the length of
one argument is one, then the output's length will
match the length of the other argument. See examples
below.
}
\examples{
"foo" \%+\% "bar"

letters[1:10] \%+\% chr(1:10)

letters[1:10] \%+\% "-" \%+\% chr(1:10)

## This is empty (unlike for parse)
character() \%+\% "*"
}


================================================
FILE: man/crayon.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/crayon-package.R, R/machinery.R
\docType{package}
\name{crayon}
\alias{crayon-package}
\alias{crayon}
\alias{reset}
\alias{bold}
\alias{blurred}
\alias{italic}
\alias{underline}
\alias{inverse}
\alias{hidden}
\alias{strikethrough}
\alias{black}
\alias{red}
\alias{green}
\alias{yellow}
\alias{blue}
\alias{magenta}
\alias{cyan}
\alias{white}
\alias{silver}
\alias{bgBlack}
\alias{bgRed}
\alias{bgGreen}
\alias{bgYellow}
\alias{bgBlue}
\alias{bgMagenta}
\alias{bgCyan}
\alias{bgWhite}
\title{Colored terminal output}
\usage{
## Simple styles
red(...)
bold(...)
# ...

## See more styling below
}
\arguments{
\item{...}{Strings to style.}
}
\description{
With crayon it is easy to add color to terminal output, create styles
for notes, warnings, errors; and combine styles.
}
\details{
ANSI color support is automatically detected and used. Crayon was largely
inspired by chalk \url{https://github.com/chalk/chalk}.

Crayon defines several styles, that can be combined. Each style in the list
has a corresponding function with the same name.
}
\section{Genaral styles}{


\itemize{
\item reset
\item bold
\item blurred (usually called \sQuote{dim}, renamed to avoid name clash)
\item italic (not widely supported)
\item underline
\item inverse
\item hidden
\item strikethrough (not widely supported)
}
}

\section{Text colors}{


\itemize{
\item black
\item red
\item green
\item yellow
\item blue
\item magenta
\item cyan
\item white
\item silver (usually called \sQuote{gray}, renamed to avoid name clash)
}
}

\section{Background colors}{


\itemize{
\item bgBlack
\item bgRed
\item bgGreen
\item bgYellow
\item bgBlue
\item bgMagenta
\item bgCyan
\item bgWhite
}
}

\section{Styling}{


The styling functions take any number of character vectors as arguments,
and they concatenate and style them: \preformatted{  library(crayon)
  cat(blue("Hello", "world!\n"))
}

Crayon defines the \code{\%+\%} string concatenation operator, to make it easy
to assemble stings with different styles. \preformatted{  cat("... to highlight the " \%+\% red("search term") \%+\%
      " in a block of text\n")
}

Styles can be combined using the \code{$} operator: \preformatted{  cat(yellow$bgMagenta$bold('Hello world!\n'))
} See also \code{\link[=combine_styles]{combine_styles()}}.

Styles can also be nested, and then inner style takes
precedence: \preformatted{  cat(green(
    'I am a green line ' \%+\%
    blue$underline$bold('with a blue substring') \%+\%
    ' that becomes green again!\n'
  ))
}

It is easy to define your own themes: \preformatted{  error <- red $ bold
  warn <- magenta $ underline
  note <- cyan
  cat(error("Error: subscript out of bounds!\n"))
  cat(warn("Warning: shorter argument was recycled.\n"))
  cat(note("Note: no such directory.\n"))
}
}

\examples{
cat(blue("Hello", "world!"))

cat("... to highlight the " \%+\% red("search term") \%+\%
    " in a block of text")

cat(yellow$bgMagenta$bold('Hello world!'))

cat(green(
 'I am a green line ' \%+\%
 blue$underline$bold('with a blue substring') \%+\%
 ' that becomes green again!'
))

error <- red $ bold
warn <- magenta $ underline
note <- cyan
cat(error("Error: subscript out of bounds!\n"))
cat(warn("Warning: shorter argument was recycled.\n"))
cat(note("Note: no such directory.\n"))

}
\seealso{
Useful links:
\itemize{
  \item \url{https://r-lib.github.io/crayon/}
  \item \url{https://github.com/r-lib/crayon}
  \item Report bugs at \url{https://github.com/r-lib/crayon/issues}
}


\code{\link[=make_style]{make_style()}} for using the 256 ANSI colors.
}
\author{
\strong{Maintainer}: Gábor Csárdi \email{csardi.gabor@gmail.com}

Other contributors:
\itemize{
  \item Brodie Gaslam \email{brodie.gaslam@yahoo.com} [contributor]
  \item Posit Software, PBC [copyright holder, funder]
}

}


================================================
FILE: man/drop_style.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/machinery.R
\name{drop_style}
\alias{drop_style}
\title{Remove a style}
\usage{
drop_style(style)
}
\arguments{
\item{style}{The name of the style to remove. No error is given
for non-existing names.}
}
\value{
Nothing.
}
\description{
Remove a style
}
\examples{
make_style(new_style = "maroon", bg = TRUE)
cat(style("I am maroon", "new_style"), "\n")
drop_style("new_style")
"new_style" \%in\% names(styles())
}
\seealso{
Other styles: 
\code{\link{make_style}()}
}
\concept{styles}


================================================
FILE: man/has_color.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/has_color.R
\name{has_color}
\alias{has_color}
\title{Does the current R session support ANSI colors?}
\usage{
has_color()
}
\value{
\code{TRUE} if the current R session supports color.
}
\description{
From crayon 2.0.0, this function is simply a wrapper on
\code{\link[=num_ansi_colors]{num_ansi_colors()}}.
}
\examples{
has_color()
}


================================================
FILE: man/has_style.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/has_ansi.R
\name{has_style}
\alias{has_style}
\title{Check if a string has some ANSI styling}
\usage{
has_style(string)
}
\arguments{
\item{string}{The string to check. It can also be a character
vector.}
}
\value{
Logical vector, \code{TRUE} for the strings that have some
ANSI styling.
}
\description{
Check if a string has some ANSI styling
}
\examples{
## The second one has style if crayon is enabled
has_style("foobar")
has_style(red("foobar"))
}


================================================
FILE: man/hyperlink.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/link.R
\name{hyperlink}
\alias{hyperlink}
\alias{has_hyperlink}
\title{Terminal Hyperlinks}
\usage{
hyperlink(text, url)

has_hyperlink()
}
\arguments{
\item{text}{Text to show. \code{text} and \code{url} are recycled to match their
length, via a \code{paste0()} call.}

\item{url}{URL to link to.}
}
\value{
Logical scalar, for \code{has_hyperlink()}.
}
\description{
Terminal Hyperlinks
}
\details{
hyperlink()` creates an ANSI hyperlink.

\code{has_hyperlink()} checks if the current \code{stdout()} supports hyperlinks.
terminal links.

See also
\url{https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda}.
}
\examples{
cat("This is an", hyperlink("R", "https://r-project.org"), "link.\n")
has_hyperlink()
}


================================================
FILE: man/make_style.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/machinery.R
\name{make_style}
\alias{make_style}
\title{Create an ANSI color style}
\usage{
make_style(..., bg = FALSE, grey = FALSE, colors = num_colors())
}
\arguments{
\item{...}{The style to create. See details and examples below.}

\item{bg}{Whether the color applies to the background.}

\item{grey}{Whether to specifically create a grey color.
This flag is included because ANSI 256 has a finer color scale
for greys than the usual 0:5 scale for R, G and B components.
It is only used for RGB color specifications (either numerically
or via a hexa string) and is ignored on eigth color ANSI
terminals.}

\item{colors}{Number of colors, detected automatically
by default.}
}
\value{
A function that can be used to color strings.
}
\description{
Create a style, or a style function, or both. This function
is intended for those who wish to use 256 ANSI colors,
instead of the more widely supported eight colors.
}
\details{
The crayon package comes with predefined styles (see
\code{\link[=styles]{styles()}} for a list) and functions for the basic eight-color
ANSI standard (\code{red}, \code{blue}, etc., see \link{crayon}).

There are no predefined styles or style functions for the 256 color
ANSI mode, however, because we simply did not want to create that
many styles and functions. Instead, \code{make_style()} can be
used to create a style (or a style function, or both).

There are two ways to use this function: \enumerate{
\item If its first argument is not named, then it returns a function
that can be used to color strings.
\item If its first argument is named, then it also creates a
style with the given name. This style can be used in
\code{\link[=style]{style()}}. One can still use the return value
of the function, to create a style function.
}

The style (the \code{...} argument) can be anything of the
following: \itemize{
\item An R color name, see \code{\link[=colors]{colors()}}.
\item A 6- or 8-digit hexa color string, e.g. \verb{#ff0000} means
red. Transparency (alpha channel) values are ignored.
\item A one-column matrix with three rows for the red, green
and blue channels, as returned by \code{col2rgb} (in the base
grDevices package).
}

\code{make_style()} detects the number of colors to use
automatically (this can be overridden using the \code{colors}
argument). If the number of colors is less than 256 (detected or given),
then it falls back to the color in the ANSI eight color mode that
is closest to the specified (RGB or R) color.

See the examples below.
}
\examples{
## Create a style function without creating a style
pink <- make_style("pink")
bgMaroon <- make_style(rgb(0.93, 0.19, 0.65), bg = TRUE)
cat(bgMaroon(pink("I am pink if your terminal wants it, too.\n")))

## Create a new style for pink and maroon background
make_style(pink = "pink")
make_style(bgMaroon = rgb(0.93, 0.19, 0.65), bg = TRUE)
"pink" \%in\% names(styles())
"bgMaroon" \%in\% names(styles())
cat(style("I am pink, too!\n", "pink", bg = "bgMaroon"))
}
\seealso{
Other styles: 
\code{\link{drop_style}()}
}
\concept{styles}


================================================
FILE: man/num_ansi_colors.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/aab-num-ansi-colors.R
\name{num_ansi_colors}
\alias{num_ansi_colors}
\alias{detect_tty_colors}
\title{Detect the number of ANSI colors to use}
\usage{
num_ansi_colors(stream = "auto")

detect_tty_colors()
}
\arguments{
\item{stream}{The stream that will be used for output, an R connection
object. It can also be a string, one of \code{"auto"}, \code{"message"},
\code{"stdout"}, \code{"stderr"}. \code{"auto"} will select \code{stdout()} if the session is
interactive and there are no sinks, otherwise it will select \code{stderr()}.}
}
\value{
Integer, the number of ANSI colors the current R session
supports for \code{stream}.
}
\description{
Certain Unix and Windows terminals, and also certain R GUIs, e.g.
RStudio, support styling terminal output using special control
sequences (ANSI sequences).

\code{num_ansi_colors()} detects if the current R session supports ANSI
sequences, and if it does how many colors are supported.
}
\details{
The detection mechanism is quite involved and it is designed to work
out of the box on most systems. If it does not work on your system,
please report a bug. Setting options and environment variables to turn
on ANSI support is error prone, because they are inherited in other
environments, e.g. knitr, that might not have ANSI support.

If you want to \emph{turn off} ANSI colors, set the \code{NO_COLOR} environment
variable to a non-empty value.

The exact detection mechanism is as follows:
\enumerate{
\item If the \code{cli.num_colors} options is set, that is returned.
\item If the \code{R_CLI_NUM_COLORS} environment variable is set to a
non-empty value, then it is used.
\item If the \code{crayon.enabled} option is set to \code{FALSE}, 1L is returned.
(This is for compatibility with code that uses the crayon package.)
\item If the \code{crayon.enabled} option is set to \code{TRUE} and the
\code{crayon.colors} option is not set, then the value of the
\code{cli.default_num_colors} option, or if it is unset, then 8L is
returned.
\item If the \code{crayon.enabled} option is set to \code{TRUE} and the
\code{crayon.colors} option is also set, then the latter is returned.
(This is for compatibility with code that uses the crayon package.)
\item If the \code{NO_COLOR} environment variable is set, then 1L is returned.
\item If we are in knitr, then 1L is returned, to turn off colors in
\code{.Rmd} chunks.
\item If \code{stream} is \code{"auto"} (the default) and there is an active
sink (either for \code{"output"} or \code{"message"}), then we return 1L.
(In theory we would only need to check the stream that will be
be actually used, but there is no easy way to tell that.)
\item If \code{stream} is not \code{"auto"}, but it is \code{stderr()} and there is an
active sink for it, then 1L is returned.
(If a sink is active for "output", then R changes the \code{stdout()}
stream, so this check is not needed.)
\item If the \code{cli.default_num_colors} option is set, then we use that.
\item If R is running inside RGui on Windows, or R.app on macOS, then we
return 1L.
\item If R is running inside RStudio, with color support, then the
appropriate number of colors is returned, usually 256L.
\item If R is running on Windows, inside an Emacs version that is recent
enough to support ANSI colors, then the value of the
\code{cli.default_num_colors} option, or if unset 8L is returned.
(On Windows, Emacs has \code{isatty(stdout()) == FALSE}, so we need to
check for this here before dealing with terminals.)
\item If \code{stream} is not the standard output or standard error  in a
terminal, then 1L is returned.
\item Otherwise we use and cache the result of the terminal color
detection (see below).
}

The terminal color detection algorithm:
\enumerate{
\item If the \code{COLORTERM} environment variable is set to \code{truecolor} or
\verb{24bit}, then we return 16 million colors.
\item If the \code{COLORTERM} environment variable is set to anything else,
then we return the value of the \code{cli.num_default_colors} option,
8L if unset.
\item If R is running on Unix, inside an Emacs version that is recent
enough to support ANSI colors, then the value of the
\code{cli.default_num_colors} option is returned, or 8L if unset.
\item If we are on Windows in an RStudio terminal, then apparently
we only have eight colors, but the \code{cli.default_num_colors} option
can be used to override this.
\item If we are in a recent enough Windows 10 terminal, then there
is either true color (from build 14931) or 256 color (from
build 10586) support. You can also use the \code{cli.default_num_colors}
option to override these.
\item If we are on Windows, under ConEmu or cmder, or ANSICON is loaded,
then the value of \code{cli.default_num_colors}, or 8L if unset, is
returned.
\item Otherwise if we are on Windows, return 1L.
\item Otherwise we are on Unix and try to run \verb{tput colors} to determine
the number of colors. If this succeeds, we return its return value.
If the \code{TERM} environment variable is \code{xterm} and \code{tput}
returned 8L, we return 256L, because xterm compatible terminals
tend to support 256 colors
(\url{https://github.com/r-lib/crayon/issues/17})
You can override this with the \code{cli.default_num_colors} option.
\item If \code{TERM} is set to \code{dumb}, we return 1L.
\item If \code{TERM} starts with \code{screen}, \code{xterm}, or \code{vt100}, we return 8L.
\item If \code{TERM} contains \code{color}, \code{ansi}, \code{cygwin} or \code{linux}, we return 8L.
\item Otherwise we return 1L.
}
}
\examples{
num_ansi_colors()

}
\concept{ANSI styling}


================================================
FILE: man/num_colors.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/has_color.R
\name{num_colors}
\alias{num_colors}
\title{Number of colors the terminal supports}
\usage{
num_colors(forget = FALSE)
}
\arguments{
\item{forget}{Ignored. Included for backwards compatibility.}
}
\value{
Number of ANSI colors.
}
\description{
From crayon version 2.0.0, this function is a simple wrapper on
\code{\link[=num_ansi_colors]{num_ansi_colors()}}, with the additional twist that the \code{crayon.colors}
option is still obseved, and takes precedence, for compatibility.
}
\examples{
num_colors()
}


================================================
FILE: man/show_ansi_colors.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/show.R
\name{show_ansi_colors}
\alias{show_ansi_colors}
\title{Show the ANSI color table on the screen}
\usage{
show_ansi_colors(colors = num_colors())
}
\arguments{
\item{colors}{Number of colors to show, meaningful values
are 8 and 256. It is automatically set to the number of
supported colors, if not specified.}
}
\value{
The printed string, invisibly.
}
\description{
Show the ANSI color table on the screen
}


================================================
FILE: man/start.crayon.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/parts.R
\name{start.crayon}
\alias{start.crayon}
\alias{finish}
\alias{finish.crayon}
\title{Switch on or off a style}
\usage{
\method{start}{crayon}(x, ...)

finish(x, ...)

\method{finish}{crayon}(x, ...)
}
\arguments{
\item{x}{Style.}

\item{...}{Ignored.}
}
\description{
Make a style active. The text printed to the screen from now
on will use this style.
}
\details{
This function is very rarely needed, e.g. for colored user
input. For other reasons, just call the style as a function on
the string.
}
\examples{
## The input is red (if color is supported)
get_name <- function() {
  cat("Enter your name:", start(red))
  input <- readline()
  cat(finish(red))
  input
}
name <- get_name()
name
}


================================================
FILE: man/strip_style.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/has_ansi.R
\name{strip_style}
\alias{strip_style}
\title{Remove ANSI escape sequences from a string}
\usage{
strip_style(string)
}
\arguments{
\item{string}{The input string.}
}
\value{
The cleaned up string.
}
\description{
Remove ANSI escape sequences from a string
}
\examples{
strip_style(red("foobar")) == "foobar"
}


================================================
FILE: man/style.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/style-var.R
\name{style}
\alias{style}
\title{Add style to a string}
\usage{
style(string, as = NULL, bg = NULL)
}
\arguments{
\item{string}{Character vector to style.}

\item{as}{Style function to apply, either the function object,
or its name, or an object to pass to \code{\link[=make_style]{make_style()}}.}

\item{bg}{Background style, a style function, or a name that
is passed to \code{\link[=make_style]{make_style()}}.}
}
\value{
Styled character vector.
}
\description{
See \code{names(styles)}, or the crayon manual for available styles.
}
\examples{
## These are equivalent
style("foobar", bold)
style("foobar", "bold")
bold("foobar")
}


================================================
FILE: man/styles.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/machinery.R
\name{styles}
\alias{styles}
\title{ANSI escape sequences of crayon styles}
\usage{
styles()
}
\value{
A named list. Each list element is a list of two
strings, named \sQuote{open} and \sQuote{close}.
}
\description{
You can use this function to list all availables crayon styles,
via \code{names(styles())}, or to explicitly apply an ANSI
escape seauence to a string.
}
\examples{
names(styles())
cat(styles()[["bold"]]$close)
}
\seealso{
\code{\link[=crayon]{crayon()}} for the beginning of the crayon manual.
}


================================================
FILE: tests/testthat/helper.R
================================================
local_colors <- function(colors = 256, .local_envir = parent.frame()) {
  withr::local_envvar(
    list(R_CLI_NUM_COLORS = as.character(colors)),
    .local_envir = .local_envir
  )
}


================================================
FILE: tests/testthat/test-ansi256.R
================================================
## These are really basic cases...

test_that("ansi256", {
  cases <- list(
    list(c(0, 0, 0), 233),
    list(c(255, 0, 0), 197),
    list(c(0, 255, 0), 47),
    list(c(0, 0, 255), 22),
    list(c(255, 255, 255), 256)
  )

  for (case in cases) {
    exp1 <- list(
      open = paste0("\033[38;5;", case[[2]] - 1, "m"),
      close = "\033[39m"
    )
    exp2 <- list(
      open = paste0("\033[48;5;", case[[2]] - 1, "m"),
      close = "\033[49m"
    )
    expect_equal(ansi256(case[[1]], bg = FALSE), exp1)
    expect_equal(ansi256(case[[1]], bg = TRUE), exp2)
  }
})

test_that("ansi256, grey", {
  cases <- list(
    list(c(0, 0, 0), 233),
    list(c(128, 128, 128), 245),
    list(c(255, 255, 255), 256)
  )

  for (case in cases) {
    exp1 <- list(
      open = paste0("\033[38;5;", case[[2]] - 1, "m"),
      close = "\033[39m"
    )
    exp2 <- list(
      open = paste0("\033[48;5;", case[[2]] - 1, "m"),
      close = "\033[49m"
    )
    expect_equal(ansi256(case[[1]], grey = TRUE, bg = FALSE), exp1)
    expect_equal(ansi256(case[[1]], grey = TRUE, bg = TRUE), exp2)
  }
})

test_that("ansi256_rgb_index", {
  cases <- list(
    list(c(0, 0, 0), 233),
    list(c(255, 0, 0), 197),
    list(c(0, 255, 0), 47),
    list(c(0, 0, 255), 22),
    list(c(255, 255, 255), 256)
  )

  for (case in cases) {
    expect_equal(do.call(ansi256_rgb_index, as.list(case[[1]])), case[[2]])
  }
})


================================================
FILE: tests/testthat/test-color.R
================================================
test_that("Coloring and highlighting works", {
  local_colors()
  expect_equal(underline("foo"), '\u001b[4mfoo\u001b[24m')
  expect_equal(red('foo'), '\u001b[31mfoo\u001b[39m')
  expect_equal(bgRed('foo'), '\u001b[41mfoo\u001b[49m')
})

test_that("Applying multiple styles at once works", {
  local_colors()
  expect_equal(
    red$bgGreen$underline('foo'),
    '\u001b[31m\u001b[42m\u001b[4mfoo\u001b[24m\u001b[49m\u001b[39m'
  )
  expect_equal(
    underline$red$bgGreen('foo'),
    '\u001b[4m\u001b[31m\u001b[42mfoo\u001b[49m\u001b[39m\u001b[24m'
  )
})

test_that("Nested styles are supported", {
  local_colors()
  expect_equal(
    red('foo' %+% underline$bgBlue('bar') %+% '!'),
    '\u001b[31mfoo\u001b[4m\u001b[44mbar\u001b[49m\u001b[24m!\u001b[39m'
  )
})

test_that("Nested styles of the same type are supported", {
  local_colors()
  expect_equal(
    red('a' %+% blue('b' %+% green('c') %+% 'b') %+% 'c'),
    '\u001b[31ma\u001b[34mb\u001b[32mc\u001b[34mb\u001b[31mc\u001b[39m'
  )
})

test_that("Reset all styles", {
  local_colors()
  expect_equal(
    reset(red$bgGreen$underline('foo') %+% 'foo'),

    '\033[0m\033[31m\033[42m\033[4mfoo\033[24m\033[49m\033[39mfoo\033[0m\033[22m\033[23m\033[24m\033[27m\033[28m\033[29m\033[39m\033[49m'
  )

  # reset, but only in middle of string

  expect_equal(
    red$bgGreen$underline('bunny bunny', reset(' foo '), 'foo'),
    '\033[31m\033[42m\033[4mbunny bunny \033[0m foo \033[0m\033[22m\033[23m\033[4m\033[27m\033[28m\033[29m\033[31m\033[42m foo\033[24m\033[49m\033[39m'
  )
})

test_that("Variable number of arguments", {
  local_colors()
  expect_equal(red('foo', 'bar'), '\u001b[31mfoo bar\u001b[39m')
})


================================================
FILE: tests/testthat/test-combine.R
================================================
test_that("one style", {
  # Need 3rd edition for testthat's comparison that works for these
  # functions. `all_equal()`, used by the 2nd edition gives a
  # deprecation warning.
  local_edition(3)
  expect_equal(combine_styles(red), red)
  expect_equal(combine_styles(bold), bold)
})

test_that("style objects", {
  withr::with_options(
    list(crayon.enabled = TRUE, crayon.colors = 256),
    {
      expect_equal(
        combine_styles(red, bold)("blah"),
        red(bold("blah"))
      )
      expect_equal(
        combine_styles(red, bold, underline)("foo"),
        red(bold(underline("foo")))
      )
    }
  )
})

test_that("create styles on the fly", {
  withr::with_options(
    list(crayon.enabled = TRUE, crayon.colors = 256),
    {
      expect_equal(
        combine_styles("darkolivegreen", bold)("blah"),
        make_style("darkolivegreen")((bold("blah")))
      )
      expect_equal(
        combine_styles(bold, "darkolivegreen", underline)("foo"),
        bold(make_style("darkolivegreen")(underline("foo")))
      )
    }
  )
})


================================================
FILE: tests/testthat/test-has-style.R
================================================
test_that("has_style works", {
  expect_false(has_style("foobar"))
  for (st in names(styles)) {
    expect_true(has_style(style("foobar", st)))
  }
})

test_that("strip_style works", {
  expect_equal("", strip_style(""))
  expect_equal("foobar", strip_style("foobar"))
  expect_equal("foobar", strip_style(red$underline$bold("foobar")))

  for (st in names(styles)) {
    expect_equal("foobar", strip_style(style("foobar", st)))
  }
})


================================================
FILE: tests/testthat/test-hyperlink.R
================================================
test_that("hyperlinks", {
  withr::local_options(crayon.hyperlink = TRUE)
  expect_equal(
    hyperlink("foo", "https://bar"),
    "\033]8;;https://bar\afoo\033]8;;\a"
  )
})


================================================
FILE: tests/testthat/test-make-style.R
================================================
test_that("make_style without name", {
  st <- styles()
  pink <- make_style("pink")
  expect_true(is(pink, "crayon"))
  expect_identical(st, styles())
})

test_that("make_style with name", {
  st <- styles()
  make_style(foobarnonono = "pink")
  expect_true("foobarnonono" %in% names(styles()))
  drop_style("foobarnonono")
  expect_false("foobarnonono" %in% names(styles()))
})

test_that("hexa color regex works", {
  positive <- c(
    "#000000",
    "#ffffff",
    "#0f0f0f",
    "#f0f0f0",
    "#00000000",
    "#ffffffff",
    "#0f0f0f00",
    "#f0f0f055"
  )

  negative <- c(
    "",
    "#12345",
    "123456",
    "1234567",
    "12345678",
    "#1234567",
    "#1234ffg",
    "#gggggx",
    "foo#123456",
    "foo#123456bar"
  )

  for (color in positive) {
    expect_true(grepl(hash_color_regex, color))
    expect_true(grepl(hash_color_regex, toupper(color)))
  }

  for (color in negative) {
    expect_false(grepl(hash_color_regex, color))
    expect_false(grepl(hash_color_regex, toupper(color)))
  }
})

test_that("we fall back for ANSI 8 if needed", {
  yellow3 <- make_style("yellow3", colors = 8)
  expect_equal(attr(yellow, "_styles")[[1]], attr(yellow3, "_styles")[[1]])
})

test_that("we can create a style from an R color", {
  red4 <- make_style("red4")
  red_text <- red4("text")
  expect_true(!has_color() || has_style(red_text))
})


================================================
FILE: tests/testthat/test-operations.R
================================================
str <- c(
  "",
  "plain",
  "\033[31m",
  "\033[39m",
  "\033[31mred\033[39m",
  "\033[31mred\033[39m\033[31mred\033[39m",
  "foo\033[31mred\033[39m",
  "\033[31mred\033[39mfoo"
)

test_that("col_nchar", {
  for (s in str) {
    expect_equal(col_nchar(s), nchar(strip_style(s)), info = s)
  }
})

test_that("col_substr", {
  for (s in str) {
    for (i in 1 %:% col_nchar(s)) {
      for (j in i %:% col_nchar(s)) {
        expect_equal(
          strip_style(col_substr(s, i, j)),
          substr(strip_style(s), i, j),
          info = paste(s, i, j)
        )
      }
    }
  }
})

test_that("col_substr keeps color", {
  expect_equal(col_substr("\033[31mred\033[39m", 1, 1), "\033[31mr\033[39m")
  expect_equal(col_substr("foo\033[31mred\033[39m", 4, 4), "\033[31mr\033[39m")
  expect_equal(
    col_substr("foo\033[31mred\033[39mbar", 4, 4),
    "\033[31mr\033[39m"
  )
  expect_equal(
    col_substr("\033[31mred\033[39mfoo\033[31mred\033[39mbar", 7, 7),
    "\033[31m\033[39m\033[31mr\033[39m"
  )
})

test_that("col_substr, start after string end", {
  expect_equal(col_substr("red", 4, 4), "")
  expect_equal(col_substr("red", 4, 5), "")
  expect_equal(strip_style(col_substr("\033[31mred\033[39m", 4, 4)), "")
  expect_equal(strip_style(col_substr("\033[31mred\033[39m", 4, 5)), "")

  expect_equal(col_substr("red", 3, 4), "d")
  expect_equal(col_substr("red", 3, 5), "d")
  expect_equal(strip_style(col_substr("\033[31mred\033[39m", 3, 4)), "d")
  expect_equal(strip_style(col_substr("\033[31mred\033[39m", 3, 5)), "d")
})

test_that("col_substr, multiple strings", {
  set.seed(42)
  for (i in 1:100) {
    strs <- sample(str, 4)
    num_starts <- sample(1:5, 1)
    num_stops <- sample(1:5, 1)
    starts <- sample(1:5, num_starts, replace = TRUE)
    stops <- sample(1:5, num_stops, replace = TRUE)
    r1 <- strip_style(col_substr(strs, starts, stops))
    r2 <- substr(strip_style(strs), starts, stops)
    expect_equal(r1, r2)
  }
})

test_that("col_substr corner cases", {
  # Zero length input

  c0 <- character(0L)
  o0 <- structure(list(), class = "abc")
  co0 <- structure(character(0L), class = "abc")
  expect_identical(col_substr(c0, 1, 1), substr(c0, 1, 1))
  expect_identical(col_substr(o0, 1, 1), substr(o0, 1, 1))
  expect_identical(col_substr(co0, 1, 1), substr(co0, 1, 1))

  expect_identical(col_substring(c0, 1, 1), substring(c0, 1, 1))
  expect_identical(col_substring(o0, 1, 1), substring(o0, 1, 1))
  expect_identical(col_substring(co0, 1, 1), substring(co0, 1, 1))

  # Character start/stop
  expect_identical(col_substr("abc", "1", 1), substr("abc", "1", 1))
  expect_identical(col_substr("abc", 1, "1"), substr("abc", 1, "1"))

  # non-numeric arguments cause errors; NOTE: this actually "works"
  # with 'substr' but not implemented in 'col_substr'
  suppressWarnings(
    expect_error(col_substr("abc", "hello", 1), "non-numeric")
  )
})

test_that("col_substring", {
  for (s in str) {
    for (i in 1 %:% col_nchar(s)) {
      for (j in i %:% col_nchar(s)) {
        expect_equal(
          strip_style(col_substring(s, i, j)),
          substring(strip_style(s), i, j),
          info = paste(s, i, j)
        )
      }
    }
  }
})

test_that("col_substring, multiple strings", {
  set.seed(42)
  for (i in 1:100) {
    strs <- sample(str, 4)
    num_starts <- sample(1:5, 1)
    num_stops <- sample(1:5, 1)
    starts <- sample(1:5, num_starts, replace = TRUE)
    stops <- sample(1:5, num_stops, replace = TRUE)
    r1 <- strip_style(col_substring(strs, starts, stops))
    r2 <- substring(strip_style(strs), starts, stops)
    expect_equal(r1, r2)
  }
})

test_that("col_substring corner cases", {
  # Zero length input

  c0 <- character(0L)
  o0 <- structure(list(), class = "abc")
  co0 <- structure(character(0L), class = "abc")
  expect_identical(col_substring(c0, 1, 1), substring(c0, 1, 1))
  expect_identical(col_substring(o0, 1, 1), substring(o0, 1, 1))
  expect_identical(col_substring(co0, 1, 1), substring(co0, 1, 1))
})

test_that("col_strsplit", {
  red <- "\033[31mred\033[39m"

  str <- "plain-plain"
  expect_equal(col_strsplit(str, "-"), strsplit(str, "-"))

  str <- red %+% "-plain"
  expect_equal(
    strip_style(col_strsplit(str, "-")[[1]]),
    strsplit(strip_style(str), "-")[[1]]
  )

  expect_equal(
    col_strsplit(str, "e"),
    list(c("\033[31mr\033[39m", "\033[31md\033[39m-plain"))
  )

  str <- red %+% "-" %+% red %+% "-" %+% red
  expect_equal(
    strip_style(col_strsplit(str, "-")[[1]]),
    strsplit(strip_style(str), "-")[[1]]
  )

  # with leading and trailing separators
  str.2 <- "-" %+% red %+% "-" %+% red %+% "-" %+% red %+% "-"
  expect_equal(
    strip_style(col_strsplit(str.2, "-")[[1]]),
    strsplit(strip_style(str.2), "-")[[1]]
  )

  # greater than length 1
  str.3 <- paste0("-", c(green("hello"), red("goodbye")), "-world-")
  expect_equal(
    strip_style(unlist(col_strsplit(str.3, "-"))),
    unlist(strsplit(strip_style(str.3), "-"))
  )
})

test_that("col_strsplit multiple strings", {
  red <- "\033[31mred\033[39m"
  str <- c(
    "plain-plain-" %+% red %+% "-plain-" %+% red,
    red %+% "-" %+% red,
    red
  )

  r1 <- lapply(col_strsplit(str, "-"), strip_style)
  r2 <- strsplit(strip_style(str), "-")
})

test_that("col_strsplit edge cases", {
  expect_equal(col_strsplit("", "-"), list(character(0L)))
  expect_equal(
    strip_style(col_strsplit("\033[31m\033[39m", "-")[[1]]),
    character(0L)
  )
  # special cases
  expect_equal(col_strsplit("", ""), strsplit("", ""))
  expect_equal(col_strsplit("a", "a"), strsplit("a", "a"))
  # this following test isn't working yet
  expect_equal(col_strsplit("a", ""), strsplit("a", ""))
  expect_equal(col_strsplit("", "a"), strsplit("", "a"))
  # Longer strings
  expect_identical(
    col_strsplit(c("", "a", "aa"), "a"),
    strsplit(c("", "a", "aa"), "a")
  )
  expect_identical(
    col_strsplit(c("abaa", "ababza"), "b."),
    strsplit(c("abaa", "ababza"), "b.")
  )
})

test_that("Weird length 'split'", {
  expect_error(col_strsplit(c("ab", "bd"), c("b", "d")), "must be character")
  expect_identical(col_strsplit("ab", NULL), strsplit("ab", NULL))
  expect_identical(
    col_strsplit("ab", character(0L)),
    strsplit("ab", character(0L))
  )
})

test_that("col_align", {
  expect_equal(col_align(character()), character())
  expect_equal(col_align("", 0), "")
  expect_equal(col_align(" ", 0), " ")
  expect_equal(col_align(" ", 1), " ")
  expect_equal(col_align(" ", 2), "  ")
  expect_equal(col_align("a", 1), "a")
  expect_equal(col_align(letters, 1), letters)
  expect_equal(col_align(letters, 0), letters)
  expect_equal(col_align(letters, -1), letters)

  expect_equal(col_align(letters, 2), paste0(letters, " "))
  expect_equal(col_align(letters, 3, "center"), paste0(" ", letters, " "))
  expect_equal(col_align(letters, 2, "right"), paste0(" ", letters))

  expect_equal(
    col_align(c("foo", "foobar", "", "a"), 6, "left"),
    c("foo   ", "foobar", "      ", "a     ")
  )

  expect_equal(
    col_align(c("foo", "foobar", "", "a"), 6, "center"),
    c("  foo ", "foobar", "      ", "   a  ")
  )

  expect_equal(
    col_align(c("foo", "foobar", "", "a"), 6, "right"),
    c("   foo", "foobar", "      ", "     a")
  )

  # #54: alignment of wide characters
  expect_equal(
    col_align(c("foo", "\u6210\u4ea4\u65e5", "", "a"), 6, "left"),
    c("foo   ", "\u6210\u4ea4\u65e5", "      ", "a     ")
  )

  expect_equal(
    col_align(c("foo", "\u6210\u4ea4\u65e5", "", "a"), 6, "center"),
    c("  foo ", "\u6210\u4ea4\u65e5", "      ", "   a  ")
  )

  expect_equal(
    col_align(c("foo", "\u6210\u4ea4\u65e5", "", "a"), 6, "right"),
    c("   foo", "\u6210\u4ea4\u65e5", "      ", "     a")
  )
})


================================================
FILE: tests/testthat/test-style-var.R
================================================
test_that("style works", {
  x1 <- style("foobar", bold)
  x2 <- style("foobar", "bold")
  x3 <- bold("foobar")

  expect_equal(x1, x2)
  expect_equal(x2, x3)
})


================================================
FILE: tests/testthat/test-styles.R
================================================
test_that("new styles are local to importing package", {
  skip("This is not implemented, yet.")

  lib_dir <- tempfile()

  on.exit(try(unloadNamespace("foo1"), silent = TRUE), add = TRUE)
  on.exit(try(unloadNamespace("foo2"), silent = TRUE), add = TRUE)
  on.exit(try(unlink(lib_dir, recursive = TRUE), silent = TRUE), add = TRUE)

  make_packages(
    lib_dir = lib_dir,
    imports = "crayon",

    foo1 = {
      f <- function() {
        make_style(pink = "pink")
      }
      g <- function() {
        names(styles())
      }
    },

    foo2 = {
      f <- function() {
        make_style(maroon = "maroon")
      }
      g <- function() {
        names(styles())
      }
    }
  )

  ## Add style in 'foo1', does not effect 'foo2', or attached crayon
  foo1::f()
  expect_true("pink" %in% foo1::g())
  expect_false("pink" %in% foo2::g())
  expect_false("pink" %in% names(styles()))

  ## Attached style change does not affect imports in packages
  on.exit(drop_style("ivory444"), add = TRUE)
  make_style(ivory444 = "ivory")
  expect_true("ivory444" %in% names(styles()))
  expect_false("ivory444" %in% foo1::g())
  expect_false("ivory444" %in% foo2::g())

  ## TODO: what if the package(s) are not attached
})


================================================
FILE: tests/testthat/test-utils.R
================================================
test_that("non_matching", {
  chr <- "abc"
  splits <- crayon:::re_table("", chr)
  res.mx <- cbind(start = c(1L, 1L:3L), end = 0L:3L, length = c(0L, 1L, 1L, 1L))
  expect_equal(crayon:::non_matching(splits, chr, empty = TRUE)[[1L]], res.mx)
  expect_equal(crayon:::non_matching(splits, chr)[[1L]], res.mx[-1L, ])
})


================================================
FILE: tests/testthat/test-vectors.R
================================================
foobar <- c("foo", "bar")
bigyo <- c("bi", "gyo")

test_that("Coloring and highlighting works", {
  local_colors()
  expect_equal(
    underline(foobar),
    c('\u001b[4mfoo\u001b[24m', '\u001b[4mbar\u001b[24m')
  )
  expect_equal(
    red(foobar),
    c('\u001b[31mfoo\u001b[39m', '\u001b[31mbar\u001b[39m')
  )
  expect_equal(
    bgRed(foobar),
    c('\u001b[41mfoo\u001b[49m', '\u001b[41mbar\u001b[49m')
  )
})

test_that("Applying multiple styles at once works", {
  local_colors()
  expect_equal(
    red$bgGreen$underline(foobar),
    c(
      '\u001b[31m\u001b[42m\u001b[4mfoo\u001b[24m\u001b[49m\u001b[39m',
      '\u001b[31m\u001b[42m\u001b[4mbar\u001b[24m\u001b[49m\u001b[39m'
    )
  )
  expect_equal(
    underline$red$bgGreen(foobar),
    c(
      '\u001b[4m\u001b[31m\u001b[42mfoo\u001b[49m\u001b[39m\u001b[24m',
      '\u001b[4m\u001b[31m\u001b[42mbar\u001b[49m\u001b[39m\u001b[24m'
    )
  )
})

test_that("Nested styles are supported", {
  local_colors()
  expect_equal(
    red(foobar %+% underline$bgBlue(bigyo) %+% '!'),
    c(
      '\u001b[31mfoo\u001b[4m\u001b[44mbi\u001b[49m\u001b[24m!\u001b[39m',
      '\u001b[31mbar\u001b[4m\u001b[44mgyo\u001b[49m\u001b[24m!\u001b[39m'
    )
  )
})

test_that("Nested styles of the same type are supported", {
  local_colors()
  aA <- c("a", "A")
  bB <- c("b", "B")
  cC <- c("c", "C")
  expect_equal(
    red(aA %+% blue(bB %+% green(cC) %+% bB) %+% cC),
    c(
      '\u001b[31ma\u001b[34mb\u001b[32mc\u001b[34mb\u001b[31mc\u001b[39m',
      '\u001b[31mA\u001b[34mB\u001b[32mC\u001b[34mB\u001b[31mC\u001b[39m'
    )
  )
})

test_that("Reset all styles", {
  local_colors()
  expect_equal(
    reset(red$bgGreen$underline(foobar) %+% foobar),

    c(
      "\033[0m\033[31m\033[42m\033[4mfoo\033[24m\033[49m\033[39mfoo\033[0m\033[22m\033[23m\033[24m\033[27m\033[28m\033[29m\033[39m\033[49m",
      "\033[0m\033[31m\033[42m\033[4mbar\033[24m\033[49m\033[39mbar\033[0m\033[22m\033[23m\033[24m\033[27m\033[28m\033[29m\033[39m\033[49m"
    )
  )
})

test_that("Variable number of arguments", {
  local_colors()
  expect_equal(
    red(foobar, 'bar'),
    c('\u001b[31mfoo bar\u001b[39m', '\u001b[31mbar bar\u001b[39m')
  )
})


================================================
FILE: tests/testthat.R
================================================
library(crayon)
library(testthat)
test_check("crayon")


================================================
FILE: tools/ansi-iterm-palettes.txt
================================================
black red green yellow blue magenta cyan white br_black br_red br_green br_yellow br_blue br_magenta br_cyan br_white
iterm #000000 #c91b00 #00c200 #c7c400 #0225c7 #ca30c7 #00c5c7 #c7c7c7 #686868 #ff6e67 #5ffa68 #fffc67 #6871ff #ff77ff #60fdff #ffffff
iterm-pastel #626262 #ff8373 #b4fb73 #fffdc3 #a5d5fe #ff90fe #d1d1fe #f1f1f1 #8f8f8f #ffc4be #d6fcba #fffed5 #c2e3ff #ffb2fe #e6e7fe #ffffff
iterm-smoooooth #14191e #b43c2a #00c200 #c7c400 #2744c7 #c040be #00c5c7 #c7c7c7 #686868 #dd7975 #58e790 #ece100 #a7abf2 #e17ee1 #60fdff #ffffff
iterm-snazzy #000000 #ff5c57 #5af78e #f3f99d #57c7ff #ff6ac1 #9aedfe #f1f1f0 #686868 #ff5c57 #5af78e #f3f99d #57c7ff #ff6ac1 #9aedfe #f1f1f0
iterm-solarized #073642 #dc322f #859900 #b58900 #268bd2 #d33682 #2aa198 #eee8d5 #002b36 #cb4b16 #586e75 #657b83 #839496 #6c71c4 #93a1a1 #fdf6e3
iterm-tango #000000 #d81e00 #5ea702 #cfae00 #427ab3 #89658e #00a7aa #dbded8 #686a66 #f54235 #99e343 #fdeb61 #84b0d8 #bc94b7 #37e6e8 #f1f1f0


================================================
FILE: tools/ansi-palettes.txt
================================================
        black   red     green   yellow  blue    magenta cyan    white   br_black br_red br_green br_yellow br_blue br_magenta br_cyan br_white
dichro  #000000 #882255 #117733 #ddcc77 #332288 #aa4499 #88ccee #e5e5e5 #000000 #cc6677 #999933 #ddcc77 #44aa99 #aa4499 #88ccee #ffffff
vga     #000000 #aa0000 #00aa00 #aa5500 #0000aa #aa00aa #00aaaa #aaaaaa #555555 #ff5555 #55ff55 #ffff55 #5555ff #ff55ff #55ffff #ffffff
winxp   #000000 #800000 #008000 #808000 #000080 #800080 #008080 #c0c0c0 #808080 #ff0000 #00ff00 #ffff00 #0000ff #ff00ff #00ffff #ffffff
vscode  #000000 #cd3131 #0dbc79 #e5e510 #2472c8 #bc3fbc #11a8cd #e5e5e5 #666666 #f14c4c #23d18b #f5f543 #3b8eea #d670d6 #29b8db #e5e5e5
win10   #0c0c0c #c50f1f #13a10e #c19c00 #0037da #881798 #3a96dd #cccccc #767676 #e74856 #16c60c #f9f1a5 #3b78ff #b4009e #61d6d6 #f2f2f2
macos   #000000 #c23621 #25bc24 #adad27 #492ee1 #d338d3 #33bbc8 #cbcccd #818383 #fc391f #31e722 #eaec23 #5833ff #f935f8 #14f0f0 #e9ebeb
putty   #000000 #bb0000 #00bb00 #bbbb00 #0000bb #bb00bb #00bbbb #bbbbbb #555555 #ff5555 #55ff55 #ffff55 #5555ff #ff55ff #55ffff #ffffff
mirc    #000000 #7f0000 #009300 #fc7f00 #00007f #9c009c #009393 #d2d2d2 #7f7f7f #ff0000 #00fc00 #ffff00 #0000fc #ff00ff #00ffff #ffffff
xterm   #000000 #cd0000 #00cd00 #cdcd00 #0000ee #cd00cd #00cdcd #e5e5e5 #7f7f7f #ff0000 #00ff00 #ffff00 #5c5cff #ff00ff #00ffff #ffffff
ubuntu  #010101 #de382b #39b54a #ffc706 #006fb8 #762671 #2cb5e9 #cccccc #808080 #ff0000 #00ff00 #ffff00 #0000ff #ff00ff #00ffff #ffffff
eclipse #000000 #cd0000 #00cd00 #cdcd00 #0000ee #cd00cd #00cdcd #e5e5e5 #000000 #ff0000 #00ff00 #ffff00 #5c5cff #ff00ff #00ffff #ffffff
Download .txt
gitextract_d59v6gvb/

├── .Rbuildignore
├── .github/
│   ├── .gitignore
│   ├── CODE_OF_CONDUCT.md
│   └── workflows/
│       ├── R-CMD-check.yaml
│       ├── pkgdown.yaml
│       ├── pr-commands.yaml
│       └── test-coverage.yaml
├── .gitignore
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── NAMESPACE
├── NEWS.md
├── R/
│   ├── aaa-rstudio-detect.R
│   ├── aaaa-rematch2.R
│   ├── aab-num-ansi-colors.R
│   ├── aac-num-ansi-colors.R
│   ├── ansi-256.R
│   ├── ansi-palette.R
│   ├── combine.R
│   ├── crayon-package.R
│   ├── disposable.R
│   ├── enc-utils.R
│   ├── has_ansi.R
│   ├── has_color.R
│   ├── link.R
│   ├── machinery.R
│   ├── parts.R
│   ├── print.R
│   ├── show.R
│   ├── string.R
│   ├── string_operations.R
│   ├── style-var.R
│   ├── styles.R
│   └── utils.R
├── README.md
├── _pkgdown.yml
├── air.toml
├── codecov.yml
├── crayon.Rproj
├── man/
│   ├── chr.Rd
│   ├── col_align.Rd
│   ├── col_nchar.Rd
│   ├── col_strsplit.Rd
│   ├── col_substr.Rd
│   ├── col_substring.Rd
│   ├── combine_styles.Rd
│   ├── concat.Rd
│   ├── crayon.Rd
│   ├── drop_style.Rd
│   ├── has_color.Rd
│   ├── has_style.Rd
│   ├── hyperlink.Rd
│   ├── make_style.Rd
│   ├── num_ansi_colors.Rd
│   ├── num_colors.Rd
│   ├── show_ansi_colors.Rd
│   ├── start.crayon.Rd
│   ├── strip_style.Rd
│   ├── style.Rd
│   └── styles.Rd
├── tests/
│   ├── testthat/
│   │   ├── helper.R
│   │   ├── test-ansi256.R
│   │   ├── test-color.R
│   │   ├── test-combine.R
│   │   ├── test-has-style.R
│   │   ├── test-hyperlink.R
│   │   ├── test-make-style.R
│   │   ├── test-operations.R
│   │   ├── test-style-var.R
│   │   ├── test-styles.R
│   │   ├── test-utils.R
│   │   └── test-vectors.R
│   └── testthat.R
└── tools/
    ├── ansi-iterm-palettes.txt
    └── ansi-palettes.txt
Condensed preview — 78 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (152K chars).
[
  {
    "path": ".Rbuildignore",
    "chars": 149,
    "preview": "^tags$\n^\\.github$\n^crayon\\.Rproj$\n^\\.Rproj\\.user$\n^revdep$\n^codecov\\.yml$\n^_pkgdown\\.yml$\n^docs$\n^pkgdown$\n^LICENSE\\.md$"
  },
  {
    "path": ".github/.gitignore",
    "chars": 7,
    "preview": "*.html\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 5244,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": ".github/workflows/R-CMD-check.yaml",
    "chars": 1827,
    "preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
  },
  {
    "path": ".github/workflows/pkgdown.yaml",
    "chars": 1300,
    "preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
  },
  {
    "path": ".github/workflows/pr-commands.yaml",
    "chars": 2501,
    "preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
  },
  {
    "path": ".github/workflows/test-coverage.yaml",
    "chars": 1813,
    "preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
  },
  {
    "path": ".gitignore",
    "chars": 23,
    "preview": "/tags\n.Rproj.user\ndocs\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 62,
    "preview": "{\n    \"recommendations\": [\n        \"Posit.air-vscode\"\n    ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 114,
    "preview": "{\n    \"[r]\": {\n        \"editor.formatOnSave\": true,\n        \"editor.defaultFormatter\": \"Posit.air-vscode\"\n    }\n}\n"
  },
  {
    "path": "DESCRIPTION",
    "chars": 1605,
    "preview": "Package: crayon\nTitle: Colored Terminal Output\nVersion: 1.5.3.9000\nAuthors@R: c(\n    person(\"Gábor\", \"Csárdi\", , \"csardi"
  },
  {
    "path": "LICENSE",
    "chars": 44,
    "preview": "YEAR: 2025\nCOPYRIGHT HOLDER: crayon authors\n"
  },
  {
    "path": "LICENSE.md",
    "chars": 1073,
    "preview": "# MIT License\n\nCopyright (c) 2025 crayon authors\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "NAMESPACE",
    "chars": 1260,
    "preview": "# Generated by roxygen2: do not edit by hand\n\nS3method(\"$\",crayon)\nS3method(\"-\",crayon)\nS3method(as.character,crayon)\nS3"
  },
  {
    "path": "NEWS.md",
    "chars": 3823,
    "preview": "# crayon (development version)\n\n# crayon 1.5.3\n\n* The semantics of the `cli.default_num_colors` options is now different"
  },
  {
    "path": "R/aaa-rstudio-detect.R",
    "chars": 5738,
    "preview": "rstudio <- local({\n  standalone_env <- environment()\n  parent.env(standalone_env) <- baseenv()\n\n  # -- Collect data ----"
  },
  {
    "path": "R/aaaa-rematch2.R",
    "chars": 968,
    "preview": "re_match <- function(text, pattern, perl = TRUE, ...) {\n  stopifnot(is.character(pattern), length(pattern) == 1, !is.na("
  },
  {
    "path": "R/aab-num-ansi-colors.R",
    "chars": 11146,
    "preview": "#' Detect the number of ANSI colors to use\n#'\n#' @description\n#' Certain Unix and Windows terminals, and also certain R "
  },
  {
    "path": "R/aac-num-ansi-colors.R",
    "chars": 870,
    "preview": "# Helper functions from cli, for the ANSI color detection\n\n#' @include aab-num-ansi-colors.R\n\nget_real_output <- functio"
  },
  {
    "path": "R/ansi-256.R",
    "chars": 1270,
    "preview": "# nocov start\nfgcodes <- c(paste0('\\x1b[38;5;', 0:255, 'm'), '\\x1b[39m')\nbgcodes <- c(paste0('\\x1b[48;5;', 0:255, 'm'), "
  },
  {
    "path": "R/ansi-palette.R",
    "chars": 4756,
    "preview": "get_palette_color <- function(style, colors = num_ansi_colors()) {\n  opt <- getOption(\"cli.palette\")\n  if (is.null(opt) "
  },
  {
    "path": "R/combine.R",
    "chars": 1683,
    "preview": "#' Combine two or more ANSI styles\n#'\n#' Combine two or more styles or style functions into a new style function\n#' that"
  },
  {
    "path": "R/crayon-package.R",
    "chars": 440,
    "preview": "## ----------------------------------------------------------------------\n\n#' Colored terminal output\n#'\n#' With crayon "
  },
  {
    "path": "R/disposable.R",
    "chars": 3399,
    "preview": "## nocov start\n\ninstall_quietly <- TRUE\n\nwith_wd <- function(dir, expr) {\n  wd <- getwd()\n  on.exit(setwd(wd))\n  setwd(d"
  },
  {
    "path": "R/enc-utils.R",
    "chars": 712,
    "preview": "# keep encoding, even if useBytes = TRUE\n\nEncoding_ <- function(x) {\n  if (is.factor(x) && length(levels(x)) < length(x)"
  },
  {
    "path": "R/has_ansi.R",
    "chars": 837,
    "preview": "ansi_regex <- paste0(\n  \"(?:(?:\\\\x{001b}\\\\[)|\\\\x{009b})\",\n  \"(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])\",\n  \"|\\\\x"
  },
  {
    "path": "R/has_color.R",
    "chars": 874,
    "preview": "#' Does the current R session support ANSI colors?\n#'\n#' From crayon 2.0.0, this function is simply a wrapper on\n#' [num"
  },
  {
    "path": "R/link.R",
    "chars": 1666,
    "preview": "#' Terminal Hyperlinks\n#'\n#' @details\n#' hyperlink()` creates an ANSI hyperlink.\n#'\n#' `has_hyperlink()` checks if the c"
  },
  {
    "path": "R/machinery.R",
    "chars": 10293,
    "preview": "## ----------------------------------------------------------------------\n\ncrayon_template <- function(...) {\n  my_style"
  },
  {
    "path": "R/parts.R",
    "chars": 1080,
    "preview": "#' Switch on or off a style\n#'\n#' Make a style active. The text printed to the screen from now\n#' on will use this style"
  },
  {
    "path": "R/print.R",
    "chars": 212,
    "preview": "#' @export\n#' @method print crayon\n\nprint.crayon <- function(x, ...) {\n  st <- paste(names(attr(x, \"_styles\")), collapse"
  },
  {
    "path": "R/show.R",
    "chars": 1522,
    "preview": "#' @include style-var.R\nNULL\n\n#' Show the ANSI color table on the screen\n#'\n#' @param colors Number of colors to show, m"
  },
  {
    "path": "R/string.R",
    "chars": 1603,
    "preview": "#' Convert to character\n#'\n#' This function just calls [as.character()], but it is\n#' easier to type and read.\n#'\n#' @pa"
  },
  {
    "path": "R/string_operations.R",
    "chars": 9549,
    "preview": "## Create a mapping between the string and its style-less version.\n## This is useful to work with the colored string.\n\n#"
  },
  {
    "path": "R/style-var.R",
    "chars": 1139,
    "preview": "#' Add style to a string\n#'\n#' See `names(styles)`, or the crayon manual for available styles.\n#'\n#' @param string Chara"
  },
  {
    "path": "R/styles.R",
    "chars": 2342,
    "preview": "palette_idx <- function(id) {\n  ifelse(\n    id < 38,\n    id - (30 - 1),\n    ifelse(\n      id < 48,\n      -(id - (40 - 1)"
  },
  {
    "path": "R/utils.R",
    "chars": 3424,
    "preview": "is_string <- function(x) {\n  is.character(x) && length(x) == 1 && !is.na(x)\n}\n\ncheck_string <- function(x) {\n  stopifnot"
  },
  {
    "path": "README.md",
    "chars": 3980,
    "preview": "\n## 🚀 crayon is now superseded by the cli package. 🚀\n\n> Please use [cli](https://github.com/r-lib/cli) for new projects."
  },
  {
    "path": "_pkgdown.yml",
    "chars": 418,
    "preview": "url: http://r-lib.github.io/crayon/\ntemplate:\n  bootstrap: 5\n\n  includes:\n    in_header: |\n      <script src=\"https://cd"
  },
  {
    "path": "air.toml",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "codecov.yml",
    "chars": 232,
    "preview": "comment: false\n\ncoverage:\n  status:\n    project:\n      default:\n        target: auto\n        threshold: 1%\n        infor"
  },
  {
    "path": "crayon.Rproj",
    "chars": 356,
    "preview": "Version: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSp"
  },
  {
    "path": "man/chr.Rd",
    "chars": 420,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string.R\n\\name{chr}\n\\alias{chr}\n\\title{Con"
  },
  {
    "path": "man/col_align.Rd",
    "chars": 986,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string_operations.R\n\\name{col_align}\n\\alia"
  },
  {
    "path": "man/col_nchar.Rd",
    "chars": 1011,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string_operations.R\n\\name{col_nchar}\n\\alia"
  },
  {
    "path": "man/col_strsplit.Rd",
    "chars": 1550,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string_operations.R\n\\name{col_strsplit}\n\\a"
  },
  {
    "path": "man/col_substr.Rd",
    "chars": 1561,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string_operations.R\n\\name{col_substr}\n\\ali"
  },
  {
    "path": "man/col_substring.Rd",
    "chars": 1660,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string_operations.R\n\\name{col_substring}\n\\"
  },
  {
    "path": "man/combine_styles.Rd",
    "chars": 1492,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/combine.R\n\\name{combine_styles}\n\\alias{com"
  },
  {
    "path": "man/concat.Rd",
    "chars": 720,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/string.R\n\\name{concat}\n\\alias{concat}\n\\ali"
  },
  {
    "path": "man/crayon.Rd",
    "chars": 3852,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/crayon-package.R, R/machinery.R\n\\docType{p"
  },
  {
    "path": "man/drop_style.Rd",
    "chars": 563,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/machinery.R\n\\name{drop_style}\n\\alias{drop_"
  },
  {
    "path": "man/has_color.Rd",
    "chars": 414,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/has_color.R\n\\name{has_color}\n\\alias{has_co"
  },
  {
    "path": "man/has_style.Rd",
    "chars": 531,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/has_ansi.R\n\\name{has_style}\n\\alias{has_sty"
  },
  {
    "path": "man/hyperlink.Rd",
    "chars": 803,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/link.R\n\\name{hyperlink}\n\\alias{hyperlink}\n"
  },
  {
    "path": "man/make_style.Rd",
    "chars": 3130,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/machinery.R\n\\name{make_style}\n\\alias{make_"
  },
  {
    "path": "man/num_ansi_colors.Rd",
    "chars": 5639,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/aab-num-ansi-colors.R\n\\name{num_ansi_color"
  },
  {
    "path": "man/num_colors.Rd",
    "chars": 599,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/has_color.R\n\\name{num_colors}\n\\alias{num_c"
  },
  {
    "path": "man/show_ansi_colors.Rd",
    "chars": 494,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/show.R\n\\name{show_ansi_colors}\n\\alias{show"
  },
  {
    "path": "man/start.crayon.Rd",
    "chars": 782,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/parts.R\n\\name{start.crayon}\n\\alias{start.c"
  },
  {
    "path": "man/strip_style.Rd",
    "chars": 400,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/has_ansi.R\n\\name{strip_style}\n\\alias{strip"
  },
  {
    "path": "man/style.Rd",
    "chars": 727,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/style-var.R\n\\name{style}\n\\alias{style}\n\\ti"
  },
  {
    "path": "man/styles.Rd",
    "chars": 604,
    "preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/machinery.R\n\\name{styles}\n\\alias{styles}\n\\"
  },
  {
    "path": "tests/testthat/helper.R",
    "chars": 184,
    "preview": "local_colors <- function(colors = 256, .local_envir = parent.frame()) {\n  withr::local_envvar(\n    list(R_CLI_NUM_COLORS"
  },
  {
    "path": "tests/testthat/test-ansi256.R",
    "chars": 1398,
    "preview": "## These are really basic cases...\n\ntest_that(\"ansi256\", {\n  cases <- list(\n    list(c(0, 0, 0), 233),\n    list(c(255, 0"
  },
  {
    "path": "tests/testthat/test-color.R",
    "chars": 1670,
    "preview": "test_that(\"Coloring and highlighting works\", {\n  local_colors()\n  expect_equal(underline(\"foo\"), '\\u001b[4mfoo\\u001b[24m"
  },
  {
    "path": "tests/testthat/test-combine.R",
    "chars": 1055,
    "preview": "test_that(\"one style\", {\n  # Need 3rd edition for testthat's comparison that works for these\n  # functions. `all_equal()"
  },
  {
    "path": "tests/testthat/test-has-style.R",
    "chars": 437,
    "preview": "test_that(\"has_style works\", {\n  expect_false(has_style(\"foobar\"))\n  for (st in names(styles)) {\n    expect_true(has_sty"
  },
  {
    "path": "tests/testthat/test-hyperlink.R",
    "chars": 175,
    "preview": "test_that(\"hyperlinks\", {\n  withr::local_options(crayon.hyperlink = TRUE)\n  expect_equal(\n    hyperlink(\"foo\", \"https://"
  },
  {
    "path": "tests/testthat/test-make-style.R",
    "chars": 1362,
    "preview": "test_that(\"make_style without name\", {\n  st <- styles()\n  pink <- make_style(\"pink\")\n  expect_true(is(pink, \"crayon\"))\n "
  },
  {
    "path": "tests/testthat/test-operations.R",
    "chars": 7698,
    "preview": "str <- c(\n  \"\",\n  \"plain\",\n  \"\\033[31m\",\n  \"\\033[39m\",\n  \"\\033[31mred\\033[39m\",\n  \"\\033[31mred\\033[39m\\033[31mred\\033[39"
  },
  {
    "path": "tests/testthat/test-style-var.R",
    "chars": 162,
    "preview": "test_that(\"style works\", {\n  x1 <- style(\"foobar\", bold)\n  x2 <- style(\"foobar\", \"bold\")\n  x3 <- bold(\"foobar\")\n\n  expec"
  },
  {
    "path": "tests/testthat/test-styles.R",
    "chars": 1222,
    "preview": "test_that(\"new styles are local to importing package\", {\n  skip(\"This is not implemented, yet.\")\n\n  lib_dir <- tempfile("
  },
  {
    "path": "tests/testthat/test-utils.R",
    "chars": 317,
    "preview": "test_that(\"non_matching\", {\n  chr <- \"abc\"\n  splits <- crayon:::re_table(\"\", chr)\n  res.mx <- cbind(start = c(1L, 1L:3L)"
  },
  {
    "path": "tests/testthat/test-vectors.R",
    "chars": 2187,
    "preview": "foobar <- c(\"foo\", \"bar\")\nbigyo <- c(\"bi\", \"gyo\")\n\ntest_that(\"Coloring and highlighting works\", {\n  local_colors()\n  exp"
  },
  {
    "path": "tests/testthat.R",
    "chars": 55,
    "preview": "library(crayon)\nlibrary(testthat)\ntest_check(\"crayon\")\n"
  },
  {
    "path": "tools/ansi-iterm-palettes.txt",
    "chars": 962,
    "preview": "black red green yellow blue magenta cyan white br_black br_red br_green br_yellow br_blue br_magenta br_cyan br_white\nit"
  },
  {
    "path": "tools/ansi-palettes.txt",
    "chars": 1639,
    "preview": "        black   red     green   yellow  blue    magenta cyan    white   br_black br_red br_green br_yellow br_blue br_ma"
  }
]

About this extraction

This page contains the full source code of the r-lib/crayon GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 78 files (136.5 KB), approximately 43.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!