{"id":16275873,"url":"https://github.com/brodieg/fansi","last_synced_at":"2025-04-05T19:12:27.571Z","repository":{"id":26719858,"uuid":"106195962","full_name":"brodieG/fansi","owner":"brodieG","description":"ANSI Control Sequence Aware String Functions","archived":false,"fork":false,"pushed_at":"2024-07-11T16:41:19.000Z","size":11253,"stargazers_count":54,"open_issues_count":2,"forks_count":7,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-31T15:29:37.485Z","etag":null,"topics":["r","string-manipulation"],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brodieG.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":"CONTRIBUTING.md","funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-08T17:33:01.000Z","updated_at":"2024-10-22T17:26:42.000Z","dependencies_parsed_at":"2024-05-18T20:31:24.033Z","dependency_job_id":"67fe317d-36b5-496e-b37f-63a366b86559","html_url":"https://github.com/brodieG/fansi","commit_stats":{"total_commits":1347,"total_committers":2,"mean_commits":673.5,"dds":0.0007423904974016571,"last_synced_commit":"edb5fee4e0cfe250d352509d4f28059fe49f1643"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brodieG%2Ffansi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brodieG%2Ffansi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brodieG%2Ffansi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brodieG%2Ffansi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brodieG","download_url":"https://codeload.github.com/brodieG/fansi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247386265,"owners_count":20930619,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["r","string-manipulation"],"created_at":"2024-10-10T18:45:55.567Z","updated_at":"2025-04-05T19:12:27.535Z","avatar_url":"https://github.com/brodieG.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- README.md is generated from README.Rmd. Please edit that file\nlibrary(rmarkdown)\nrender('README.Rmd', output_format=md_document())\nrender('README.Rmd', output_format=html_document())\n --\u003e\n```{r, echo = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"\",\n  fig.path = \"README-\",\n  error = TRUE\n)\nlibrary(fansi)\n```\n# fansi - ANSI Control Sequence Aware String Functions\n\n[![R build status](https://github.com/brodieG/fansi/workflows/R-CMD-check/badge.svg)](https://github.com/brodieG/fansi/actions)\n[![](https://codecov.io/gh/brodieG/fansi/branch/master/graphs/badge.svg?branch=master)](https://app.codecov.io/github/brodieG/fansi?branch=master)\n[![](http://www.r-pkg.org/badges/version/fansi)](https://cran.r-project.org/package=fansi)\n[![Dependencies direct/recursive](https://tinyverse.netlify.app/badge/fansi)](https://tinyverse.netlify.app/)\n\nCounterparts to R string manipulation functions that account for the effects of\nANSI text formatting control sequences.\n\n## Formatting Strings with Control Sequences\n\nMany terminals will recognize special sequences of characters in strings and\nchange display behavior as a result.  For example, on my terminal the sequences\n`\"\\033[3?m\"` and `\"\\033[4?m\"`, where `\"?\"` is a digit in 1-7, change the\nforeground and background colors of text respectively:\n\u003c!--\nWe tried to do everything using to_html, but github suppresses all html\n--\u003e\n\n```{r}\nfansi \u003c- \"\\033[30m\\033[41mF\\033[42mA\\033[43mN\\033[44mS\\033[45mI\\033[m\"\n```\n![](https://github.com/brodieG/fansi/raw/v1.0-rc/extra/images/fansi-1.png)\n\nThis type of sequence is called an ANSI CSI SGR control sequence.  Most \u0026#42;nix\nterminals support them, and newer versions of Windows and Rstudio consoles do\ntoo.  You can check whether your display supports them by running\n`term_cap_test()`.\n\nWhether the `fansi` functions behave as expected depends on many factors,\nincluding how your particular display handles Control Sequences.  See `?fansi`\nfor details, particularly if you are getting unexpected results.\n\n## Manipulation of Formatted Strings\n\nANSI control characters and sequences (_Control Sequences_ hereafter) break the\nrelationship between byte/character position in a string and display position.\nFor example, to extract the \"ANS\" part of our colored \"FANSI\", we would need to\ncarefully compute the character positions:\n\n![](https://github.com/brodieG/fansi/raw/v1.0-rc/extra/images/fansi-2.png)\n\nWith `fansi` we can select directly based on display position:\n\n![](https://github.com/brodieG/fansi/raw/v1.0-rc/extra/images/fansi-3.png)\n\nIf you look closely you'll notice that the text color for the `substr` version\nis wrong as the naïve string extraction loses the initial`\"\\033[37m\"` that sets\nthe foreground color.  Additionally, the color from the last letter bleeds out\ninto the next line.\n\n## `fansi` Functions\n\n`fansi` provides counterparts to the following string functions:\n\n* `substr` (and `substr\u003c-`)\n* `strsplit`\n* `strtrim`\n* `strwrap`\n* `nchar` / `nzchar`\n* `trimws`\n\nThese are drop-in replacements that behave (almost) identically to the base\ncounterparts, except for the _Control Sequence_ awareness.  There are also\nutility functions such as `strip_ctl` to remove _Control Sequences_ and\n`has_ctl` to detect whether strings contain them.\n\nMuch of `fansi` is written in C so you should find performance of the `fansi`\nfunctions to be slightly slower than the corresponding base functions, with the\nexception that `strwrap_ctl` is much faster.  Operations involving `type =\n\"width\"` will be slower still.  We have prioritized convenience and safety over\nraw speed in the C code, but unless your code is primarily engaged in string\nmanipulation `fansi` should be fast enough to avoid attention in benchmarking\ntraces.\n\n## Width Based Substrings\n\n`fansi` also includes improved versions of some of those functions, such as\n`substr2_ctl` which allows for width based substrings.  To illustrate, let's\ncreate an emoji string made up of two wide characters:\n\n```{r}\npizza.grin \u003c- sprintf(\"\\033[46m%s\\033[m\", strrep(\"\\U1F355\\U1F600\", 10))\n```\n![](https://github.com/brodieG/fansi/raw/v1.0-rc/extra/images/pizza-grin.png)\n\nAnd a colorful background made up of one wide characters:\n\n```{r}\nraw \u003c- paste0(\"\\033[45m\", strrep(\"FANSI\", 40))\nwrapped \u003c- strwrap2_ctl(raw, 41, wrap.always=TRUE)\n```\n![](https://github.com/brodieG/fansi/raw/df4019e/extra/images/wrapped-2.png)\n\nWhen we inject the 2-wide emoji into the 1-wide background their widths are\naccounted for as shown by the result remaining rectangular:\n\n```{r}\nstarts \u003c- c(18, 13, 8, 13, 18)\nends \u003c-   c(23, 28, 33, 28, 23)\nsubstr2_ctl(wrapped, type='width', starts, ends) \u003c- pizza.grin\n```\n![](https://github.com/brodieG/fansi/raw/v1.0-rc/extra/images/wrapped-1.png)\n\n`fansi` width calculations use heuristics to account for graphemes, including\ncombining emoji:\n\n```{r comment=\"##\"}\nemo \u003c- c(\n  \"\\U1F468\",\n  \"\\U1F468\\U1F3FD\",\n  \"\\U1F468\\U1F3FD\\u200D\\U1F9B3\",\n  \"\\U1F468\\u200D\\U1F469\\u200D\\U1F467\\u200D\\U1F466\"\n)\nwriteLines(\n  paste(\n    emo,\n    paste(\"base:\", nchar(emo, type='width')),\n    paste(\"fansi:\", nchar_ctl(emo, type='width'))\n) )\n```\n\n## HTML Translation\n\nYou can translate ANSI CSI SGR formatted strings into their HTML counterparts\nwith `to_html`:\n\n```{r echo=FALSE, eval=FALSE}\nN \u003c- readLines(file.path(R.home('doc'), 'NEWS'))\nN \u003c- fansi_lines(N, step=2)                             # color each line\nN \u003c- strwrap2_ctl(N, 25, pad.end=\" \", wrap.always=TRUE) # wrap to 25 chrs\nN \u003c- c(\"\", paste(N[1:15], N[100:115], N[200:215]), \"\")  # make 3 cols\nwriteLines(N)\nf \u003c- paste0(tempfile(), \".html\")\nwriteLines(\n  c(\"\u003chtml\u003e\u003cpre\u003e\",\n    to_html(html_esc(N)),\n    \"\u003c/pre\u003e\u003c/html\u003e\"\n  ),\n  f\n)\nbrowseURL(f)\n```\n![Translate to HTML](https://github.com/brodieG/fansi/raw/v1.0-rc/extra/images/sgr_to_html.png)\n\n## Rmarkdown\n\nIt is possible to set `knitr` hooks such that R output that contains ANSI CSI\nSGR is automatically converted to the HTML formatted equivalent and displayed as\nintended.  See the\n[vignette](https://htmlpreview.github.io/?https://raw.githubusercontent.com/brodieG/fansi/rc/extra/sgr-in-rmd.html)\nfor details.\n\n## Installation\n\nThis package is available on CRAN:\n\n```{r eval=FALSE}\ninstall.packages('fansi')\n```\n\nIt has no runtime dependencies.\n\nFor the development version use\n`remotes::install_github('brodieg/fansi@development')` or:\n\n```{r eval=FALSE}\nf.dl \u003c- tempfile()\nf.uz \u003c- tempfile()\ngithub.url \u003c- 'https://github.com/brodieG/fansi/archive/development.zip'\ndownload.file(github.url, f.dl)\nunzip(f.dl, exdir=f.uz)\ninstall.packages(file.path(f.uz, 'fansi-development'), repos=NULL, type='source')\nunlink(c(f.dl, f.uz))\n```\n\nThere is no guarantee that development versions are stable or even working.  The\nmaster branch typically mirrors CRAN and should be stable.\n\n## Related Packages and References\n\n* [crayon](https://github.com/r-lib/crayon), the library that started it all.\n* [ansistrings](https://github.com/r-lib/ansistrings/), which implements similar\n  functionality.\n* [ECMA-48 - Control Functions For Coded Character\n  Sets](https://ecma-international.org/publications-and-standards/standards/ecma-48/),\n  in particular pages 10-12, and 61.\n* [CCITT Recommendation T.416](https://www.itu.int/rec/dologin_pub.asp?lang=e\u0026id=T-REC-T.416-199303-I!!PDF-E\u0026type=items)\n* [ANSI Escape Code - Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code)\n  for a gentler introduction.\n\n## Acknowledgments\n\n* R Core for developing and maintaining such a wonderful language.\n* CRAN maintainers, for patiently shepherding packages onto CRAN and maintaining\n  the repository, and Uwe Ligges in particular for maintaining\n  [Winbuilder](https://win-builder.r-project.org/).\n* [Gábor Csárdi](https://github.com/gaborcsardi) for getting me started on the\n  journey ANSI control sequences, and for many of the ideas on how to process\n  them.\n* [Jim Hester](https://github.com/jimhester) for\n  [covr](https://cran.r-project.org/package=covr), and with Rstudio for\n  [r-lib/actions](https://github.com/r-lib/actions).\n* [Dirk Eddelbuettel](https://github.com/eddelbuettel) and [Carl\n  Boettiger](https://github.com/cboettig) for the\n  [rocker](https://github.com/rocker-org/rocker) project, and [Gábor\n  Csárdi](https://github.com/gaborcsardi) and the\n  [R-consortium](https://www.r-consortium.org/) for\n  [Rhub](https://github.com/r-hub), without which testing bugs on R-devel and\n  other platforms would be a nightmare.\n* [Tomas Kalibera](https://github.com/kalibera) for\n  [rchk](https://github.com/kalibera/rchk) and the accompanying vagrant image,\n  and rcnst to help detect errors in compiled code.\n* [Winston Chang](https://github.com/wch) for the\n  [r-debug](https://hub.docker.com/r/wch1/r-debug/) docker container, in\n  particular because of the valgrind level 2 instrumented version of R.\n* George Nachman etal. for [Iterm2](https://iterm2.com/index.html), a Free\n  terminal emulator that supports truecolor CSI SGR.\n* [Hadley Wickham](https://github.com/hadley/) and [Peter\n  Danenberg](https://github.com/klutometis) for\n  [roxygen2](https://cran.r-project.org/package=roxygen2).\n* [Yihui Xie](https://github.com/yihui) for\n  [knitr](https://cran.r-project.org/package=knitr) and  [J.J.\n  Allaire](https://github.com/jjallaire) et al. for\n  [rmarkdown](https://cran.r-project.org/package=rmarkdown), and by extension\n  John MacFarlane for [pandoc](https://pandoc.org/).\n* [Gábor Csárdi](https://github.com/gaborcsardi), the\n  [R-consortium](https://www.r-consortium.org/), et al. for\n  [revdepcheck](https://github.com/r-lib/revdepcheck) to simplify reverse\n  dependency checks.\n* Olaf Mersmann for\n  [microbenchmark](https://cran.r-project.org/package=microbenchmark), because\n  microsecond matter, and [Joshua Ulrich](https://github.com/joshuaulrich) for\n  making it lightweight.\n* All open source developers out there that make their work freely available\n  for others to use.\n* [Github](https://github.com/), [Codecov](https://about.codecov.io/),\n  [Vagrant](https://www.vagrantup.com/), [Docker](https://www.docker.com/),\n  [Ubuntu](https://ubuntu.com/), [Brew](https://brew.sh/) for providing\n  infrastructure that greatly simplifies open source development.\n* [Free Software Foundation](https://www.fsf.org/) for developing the GPL\n  license and promotion of the free software movement.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrodieg%2Ffansi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrodieg%2Ffansi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrodieg%2Ffansi/lists"}