ansi-to-tui-6.0.0/.cargo_vcs_info.json0000644000000001360000000000100132100ustar { "git": { "sha1": "09a71073b94f9826eac8369f24a22338176b6b36" }, "path_in_vcs": "" }ansi-to-tui-6.0.0/.envrc000064400000000000000000000000120072674642500131370ustar 00000000000000use flake ansi-to-tui-6.0.0/.github/workflows/build.yaml000064400000000000000000000013570072674642500174160ustar 00000000000000name: Build on: push: pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install nix uses: cachix/install-nix-action@v27 with: github_access_token: ${{ secrets.GITHUB_TOKEN }} - run: nix flake check - run: nix --extra-experimental-features "nix-command flakes" build .#checks.x86_64-linux.ansi-to-tui-llvm-coverage - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 with: flags: unittests name: codecov-ansi-to-tui fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} files: ./result verbose: true ansi-to-tui-6.0.0/.gitignore000064400000000000000000000000200072674642500140100ustar 00000000000000/target .direnv ansi-to-tui-6.0.0/CHANGELOG.md000064400000000000000000000353350072674642500136520ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 3.0.0 (2023-03-21) ### Commit Statistics - 6 commits contributed to the release. - 5 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - [update] Changelog and readme ([`8b38176`](https://github.com/sayanarijit/ansi-to-tui/commit/8b38176c630fe6d5c9502c3bc8b53f0f0eb25efe)) - Update version ([`7617608`](https://github.com/sayanarijit/ansi-to-tui/commit/761760805afad70709b2158b04789b31ac4f12fc)) - Move to ratatui ([`0446f08`](https://github.com/sayanarijit/ansi-to-tui/commit/0446f08857469fbcb770af125a3abd637ea1f777)) - [feat] Bump version and update CHANGELOG.md ([`eec6f86`](https://github.com/sayanarijit/ansi-to-tui/commit/eec6f86e6ad66196370cb350f4451c829d6c5bb1)) - [fix] properly reset styles ([`50f5be0`](https://github.com/sayanarijit/ansi-to-tui/commit/50f5be0ec5e9b49efebf6cb34a967b550843c61b)) - [fix] Properly set background colors ([`f827e25`](https://github.com/sayanarijit/ansi-to-tui/commit/f827e25d7998d48f7bbf004440d7cb20b54b5d99))
## 2.0.1 (2023-03-16) ### Commit Statistics - 11 commits contributed to the release over the course of 214 calendar days. - 238 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - [fix] Write changelog ([`4324447`](https://github.com/sayanarijit/ansi-to-tui/commit/4324447763d9760ee1364d78e3392fcefc078619)) - Adjusting changelogs prior to release of ansi-to-tui v2.0.1 ([`874a3a8`](https://github.com/sayanarijit/ansi-to-tui/commit/874a3a8abf968b4112c454d231fa7dfb9c157a42)) - [feat] CHANGELOGS Using cargo changelog ([`604e1d1`](https://github.com/sayanarijit/ansi-to-tui/commit/604e1d1ca493706c090c4535fb0be019757ed250)) - [fix] Bump version prep for release ([`8e07c79`](https://github.com/sayanarijit/ansi-to-tui/commit/8e07c79c42b1b831570cd25027ec48bc83b30fc2)) - Add fg/bg colors for 90-110 ANSI code range ([`1bd18a9`](https://github.com/sayanarijit/ansi-to-tui/commit/1bd18a9ab1424d2a1bd785515384aab4d58f07cf)) - [fix] Don't include the empty spans but keep changing the style ([`acdb671`](https://github.com/sayanarijit/ansi-to-tui/commit/acdb671763c655727f7b5fee9d867ac977d97070)) - [fix] Don't exclude empty spans ([`0d1e9bc`](https://github.com/sayanarijit/ansi-to-tui/commit/0d1e9bc1786886aec7e1f4ddf058b278793b928a)) - Fix infinite loop on unsupported escape sequences ([`225669f`](https://github.com/sayanarijit/ansi-to-tui/commit/225669ff1849326dbd20d90639ce86243b8eb5dd)) - Add test for infinitely looping cases ([`8a59123`](https://github.com/sayanarijit/ansi-to-tui/commit/8a5912326da52c7fed32e815339664d7acd61a64)) - [update] Version bump ([`0f2ace5`](https://github.com/sayanarijit/ansi-to-tui/commit/0f2ace5449fce46fbf891e4899afc27226f3932e)) - [fix] Don't hang on screen modes ([`189d9c5`](https://github.com/sayanarijit/ansi-to-tui/commit/189d9c5021e8b69c046be4a4c2cd78095154dc45))
## v1.0.1 (2022-07-20) ### Commit Statistics - 2 commits contributed to the release. - 174 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Update the readme example ([`a527ba0`](https://github.com/sayanarijit/ansi-to-tui/commit/a527ba066f0a9bdc924849a9c086989ebda5f8c0)) - Complete rewrite of the whole parser logic ([`0e28929`](https://github.com/sayanarijit/ansi-to-tui/commit/0e289299a123635185f9907a01a379a51009f52b))
## v0.5.0 (2022-01-27) ## 0.5.0-sayanarijit (2022-01-27) ### Commit Statistics - 4 commits contributed to the release over the course of 109 calendar days. - 120 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump version to 0.5.0 for tui 0.17.0 ([`92736b3`](https://github.com/sayanarijit/ansi-to-tui/commit/92736b3a6d8cf1f6f0225ed3f95aabe1d27efcf8)) - Possibly fix #10 ([`5127c8c`](https://github.com/sayanarijit/ansi-to-tui/commit/5127c8c2016b76c0c9c64cc60d187bea37e2e685)) - Add drone build badge ([`2871347`](https://github.com/sayanarijit/ansi-to-tui/commit/28713475a094612bf894603c720095a243801b24)) - Add .drone.yml ([`e08a9d8`](https://github.com/sayanarijit/ansi-to-tui/commit/e08a9d8281bcbc31a419b33897c79e62544acd98))
## v0.4.1 (2021-09-28) ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - V0.4.1 ([`5225ac3`](https://github.com/sayanarijit/ansi-to-tui/commit/5225ac3f4ab26329e0aff938e25e2d123c25d9f0))
## v0.3.1 (2021-09-28) ### Commit Statistics - 6 commits contributed to the release over the course of 37 calendar days. - 119 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Update version and fix #9 release ([`e2d7656`](https://github.com/sayanarijit/ansi-to-tui/commit/e2d7656587d7edf313faa2201e0d1f261530b91a)) - Remove redundant println! lines ([`c108aea`](https://github.com/sayanarijit/ansi-to-tui/commit/c108aea34e7e42ee1ed43c162d0720db546a814d)) - Fix #9 Color reset not working ([`c159a29`](https://github.com/sayanarijit/ansi-to-tui/commit/c159a29de0ee518037eebfb2f789015379d83d8e)) - Update readme #8 ([`869a5f4`](https://github.com/sayanarijit/ansi-to-tui/commit/869a5f48f23e4d55eb5c8db24101b0b653c2ca01)) - Center 2nd column ([`7259a48`](https://github.com/sayanarijit/ansi-to-tui/commit/7259a483aa2654e518ff691a9cc487ca3110e024)) - Give the README a new look ([`5555bfb`](https://github.com/sayanarijit/ansi-to-tui/commit/5555bfbe825711b9fbbaec3d539c7546da155edb))
## v0.3.0 (2021-06-01) ### Commit Statistics - 1 commit contributed to the release. - 5 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Update tui to version v0.15.0 ([`f68a55d`](https://github.com/sayanarijit/ansi-to-tui/commit/f68a55de4ed496767b7063212e0430f66d78e03b))
## v0.2.1 (2021-05-27) ### Commit Statistics - 5 commits contributed to the release over the course of 2 calendar days. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Remove println! and bump version ([`7f0ad21`](https://github.com/sayanarijit/ansi-to-tui/commit/7f0ad21e3701f110f0d56c666e561e1195be3ff6)) - Remove println! ([`88da943`](https://github.com/sayanarijit/ansi-to-tui/commit/88da943d7300fdc1a867d1b03db33578f3ca7974)) - Merge branch 'master' of gh:uttarayan21/ansi-to-tui ([`9bcd9c9`](https://github.com/sayanarijit/ansi-to-tui/commit/9bcd9c9d78f4fb33521aef08385b35f0013974c3)) - Possible fix for #3 ([`7c41742`](https://github.com/sayanarijit/ansi-to-tui/commit/7c41742b2a8665a60c3798da0859b8087f695c9c)) - Implement StdError and thus, supporting anyhow ([`47d7ac3`](https://github.com/sayanarijit/ansi-to-tui/commit/47d7ac37d830d676fafea8528d8080d53751b1fc))
## v0.2.0 (2021-05-24) ### Commit Statistics - 2 commits contributed to the release. - 17 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Fix spelling in documentation and bump version ([`69a8b4c`](https://github.com/sayanarijit/ansi-to-tui/commit/69a8b4cbbb1d7c80592542674653ce8e13e444c8)) - Fix for #2 ([`985fe88`](https://github.com/sayanarijit/ansi-to-tui/commit/985fe8811ec9d651e034235d88ce5cd68d7d660b))
## v0.1.9 (2021-05-07) ### Commit Statistics - 8 commits contributed to the release over the course of 5 calendar days. - 6 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Removed AnsiColor enum ([`aa17233`](https://github.com/sayanarijit/ansi-to-tui/commit/aa1723376985bd4c457b8780d576daa09bf42d34)) - Minor changes to documentation ([`de7091d`](https://github.com/sayanarijit/ansi-to-tui/commit/de7091daf5e333f7a4b8d3d5aa21b33d6c5b20e6)) - Minor changes ([`d7fb6e6`](https://github.com/sayanarijit/ansi-to-tui/commit/d7fb6e6d451f7a5296a122db79ec4ef6f6565882)) - Removed useless AnsiColor enum ([`c66ca17`](https://github.com/sayanarijit/ansi-to-tui/commit/c66ca173845267f834adc97c94a19842499caada)) - Bump version number ([`6818ee5`](https://github.com/sayanarijit/ansi-to-tui/commit/6818ee5f6e06f59e4569e2bfced65fcd88a24aa2)) - Keep empty newlines ([`6a80c08`](https://github.com/sayanarijit/ansi-to-tui/commit/6a80c086394e23e380acdfecf16b6159c44846ca)) - Bump version to 0.1.7 ([`6203e87`](https://github.com/sayanarijit/ansi-to-tui/commit/6203e877974a83eadd273be329b45afc17246cd3)) - Pub fn ansi_to_text_override_style ([`0694221`](https://github.com/sayanarijit/ansi-to-tui/commit/0694221f34a468ab9dd1070e7b2b363876e65791))
## v0.1.6 (2021-04-30) ### Commit Statistics - 4 commits contributed to the release. - 1 day passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1](https://github.com/sayanarijit/ansi-to-tui/issues/1) ### Commit Details
view details * **[#1](https://github.com/sayanarijit/ansi-to-tui/issues/1)** - Optimize output text size ([`28c9077`](https://github.com/sayanarijit/ansi-to-tui/commit/28c9077a767a14337d8636c1cd47513d53e88711)) * **Uncategorized** - Prepare from release ([`8ad6643`](https://github.com/sayanarijit/ansi-to-tui/commit/8ad6643545b74a0ca0d2dfc3d9857923fe85df81)) - Fix tests paths ([`a4e50fa`](https://github.com/sayanarijit/ansi-to-tui/commit/a4e50faef2f18328a65f61f066b67eec8fe88bf4)) - Bump simdutf8 to v0.1.1 ([`2e47e63`](https://github.com/sayanarijit/ansi-to-tui/commit/2e47e63b3f9c03df5a62407e1a494400bfe55e0f))
## v0.1.5 (2021-04-29) ### Commit Statistics - 18 commits contributed to the release over the course of 4 calendar days. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Newlines with empty buffers are now shown correctly ([`dce8cf1`](https://github.com/sayanarijit/ansi-to-tui/commit/dce8cf1e5194813936987c9e5cd18f488b8a9409)) - Add additional test ([`371bd6f`](https://github.com/sayanarijit/ansi-to-tui/commit/371bd6fe5cb8f01c5f2aba1f62b3fa65c74150bc)) - Change AsRef<[u8]> to IntoIterator ([`83ccd02`](https://github.com/sayanarijit/ansi-to-tui/commit/83ccd025e021df414cda6fdfbbae8b5e2d49fe13)) - Large refactor ([`51481fa`](https://github.com/sayanarijit/ansi-to-tui/commit/51481fa9323c45036083ccecd677f42bdb3af1b9)) - Added tests ([`03b623e`](https://github.com/sayanarijit/ansi-to-tui/commit/03b623ec150bd25a55d98f24818c19cb9e20e931)) - Add additional tests ([`39f69a2`](https://github.com/sayanarijit/ansi-to-tui/commit/39f69a24b139eb226cabc484ea61a28d86cea1e6)) - Refactor ([`4c354d5`](https://github.com/sayanarijit/ansi-to-tui/commit/4c354d5c00ed5bbbd01aea3d3e3dfc4ec01aebd3)) - Allow empty ([`47bc5dd`](https://github.com/sayanarijit/ansi-to-tui/commit/47bc5dd13b03b17da9df8cf746fac6fecfaf7858)) - Added Documentation badge. ([`8972553`](https://github.com/sayanarijit/ansi-to-tui/commit/8972553bc2f3d77b9fcdb47290c3c67a01d908c5)) - Added documentation workflow ([`628d8ab`](https://github.com/sayanarijit/ansi-to-tui/commit/628d8abff0993bb6b3afde4915a866a335c1b1d3)) - Added unicode_width check ([`a3585ba`](https://github.com/sayanarijit/ansi-to-tui/commit/a3585bad0004a03223174bb94376e985813d8fd9)) - Removed a lot of unnsecessary code. ([`6e2e4dd`](https://github.com/sayanarijit/ansi-to-tui/commit/6e2e4ddc0849f39721a27cfe5bdf55db6f9d7bbd)) - Somewhat fixed ([`8581b1a`](https://github.com/sayanarijit/ansi-to-tui/commit/8581b1ab0f9840391505f808d24ece44e49a2ef4)) - Temp fix ([`486c3c9`](https://github.com/sayanarijit/ansi-to-tui/commit/486c3c98ddce0a954e1807f277c9f2d40a8f62ca)) - Possible fix ([`ec38cb3`](https://github.com/sayanarijit/ansi-to-tui/commit/ec38cb3e62ffcf06b33db28b57fbb6ab214571d1)) - Pub use ansi::ansi_to_text ([`ed57ab3`](https://github.com/sayanarijit/ansi-to-tui/commit/ed57ab359825a04dcdf866cdd6f58bd4b15caea2)) - Change from ansitui to ansi-to-tui ([`43b1562`](https://github.com/sayanarijit/ansi-to-tui/commit/43b15627111b8742749acf73232a18378423c05a)) - Initial Commit. ([`4cd9c8f`](https://github.com/sayanarijit/ansi-to-tui/commit/4cd9c8fc0b5bf1d6fd1274dcf34561b76d186d32))
ansi-to-tui-6.0.0/Cargo.toml0000644000000032070000000000100112100ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "ansi-to-tui" version = "6.0.0" authors = ["Uttarayan Mondal "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "A library to convert ansi color coded text into ratatui::text::Text type from ratatui library" readme = "README.md" keywords = [ "ansi", "ascii", "tui", "parser", ] license = "MIT" repository = "https://github.com/uttarayan21/ansi-to-tui" [profile.bench] debug = 2 [lib] name = "ansi_to_tui" path = "src/lib.rs" [[test]] name = "tests" path = "tests/tests.rs" [[bench]] name = "parsing" path = "benches/parsing.rs" harness = false [dependencies.nom] version = "7.1" [dependencies.simdutf8] version = "0.1" optional = true [dependencies.smallvec] version = "1.10.0" features = ["const_generics"] [dependencies.thiserror] version = "1.0" [dependencies.tui] version = "0.28" default-features = false package = "ratatui" [dev-dependencies.anyhow] version = "1.0" [dev-dependencies.criterion] version = "0.5" [dev-dependencies.pretty_assertions] version = "1.4.0" [features] default = [ "zero-copy", "simd", ] simd = ["dep:simdutf8"] zero-copy = [] ansi-to-tui-6.0.0/Cargo.toml.orig000064400000000000000000000015110072674642500147150ustar 00000000000000[package] name = "ansi-to-tui" version = "6.0.0" authors = ["Uttarayan Mondal "] edition = "2018" description = "A library to convert ansi color coded text into ratatui::text::Text type from ratatui library" keywords = ["ansi", "ascii", "tui", "parser"] license = "MIT" readme = "README.md" repository = "https://github.com/uttarayan21/ansi-to-tui" [dependencies] nom = "7.1" tui = { version = "0.28", default-features = false, package = "ratatui" } thiserror = "1.0" simdutf8 = { version = "0.1", optional = true } smallvec = { version = "1.10.0", features = ["const_generics"] } [dev-dependencies] anyhow = "1.0" criterion = "0.5" pretty_assertions = "1.4.0" [features] simd = ["dep:simdutf8"] zero-copy = [] default = ["zero-copy", "simd"] [[bench]] name = "parsing" harness = false [profile.bench] debug = true ansi-to-tui-6.0.0/LICENSE000064400000000000000000000020400072674642500130310ustar 00000000000000Copyright 2021 Uttarayan Mondal 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. ansi-to-tui-6.0.0/README.md000064400000000000000000000022360072674642500133120ustar 00000000000000# ansi-to-tui ![drone build](https://img.shields.io/drone/build/uttarayan21/ansi-to-tui?server=https%3A%2F%2Fdrone.uttarayan.me) [![github build](https://github.com/uttarayan21/ansi-to-tui/actions/workflows/build.yaml/badge.svg)][ansi-to-tui] [![downloads](https://img.shields.io/crates/d/ansi-to-tui)](https://crates.io/crates/ansi-to-tui) A nom parser to parse text with ANSI color codes and turn them into [`ratatui::text::Text`][Text]. For people still using [tui-rs](docs.rs/tui) use version `v2.*` for people migrating to [ratatui](docs.rs/ratatui) use version `v3.*` I recommend switching over to ratatui since tui-rs is currently unmaintained. | Color | Supported | Examples | | ------ | :-------: | ------------------------ | | 24 bit | ✓ | `\x1b[38;2;;;m` | | 8 bit | ✓ | `\x1b[38;5;m` | | 4 bit | ✓ | `\x1b[30..37;40..47m` | ## Example ```rust use ansi_to_tui::IntoText; let buffer = std::fs::read("ascii/text.ascii").unwrap(); let output = buffer.into_text(); ``` [Text]: https://docs.rs/ratatui/latest/ratatui/text/struct.Text.html [ansi-to-tui]: https://github.com/uttarayan21/ansi-to-tui ansi-to-tui-6.0.0/ascii/arch.ascii000064400000000000000000000022770072674642500150570ustar 00000000000000    ..         cl.      .llll.    ,oc  :l'  ..      .. ansi-to-tui-6.0.0/ascii/archlinux.ascii000064400000000000000000000250020072674642500161260ustar 00000000000000              ..                             cl                            :ooc                          ;oooo:                        .looooo:                      ;c;:looooc                    :ooooooooooc                  :ooooooooooool                coooool;;loooool.            .looooo'    .oooooo.          .ooooooc      ;oooocl'        'ooooooo:      'ooooo:,       ,oool:,..        ..,:looo;    :c,.                    .,c:  ..                          .' ansi-to-tui-6.0.0/ascii/code.ascii000064400000000000000000003301170072674642500150510ustar 00000000000000───────┬──────────────────────────────────────────────────────────────────────── │ File: src/parser.rs ───────┼────────────────────────────────────────────────────────────────────────  1 │ use crate::code::AnsiCode;  2 │ use nom::{  3 │  branch::alt,  4 │  bytes::complete::*,  5 │  character::complete::*,  6 │  character::is_alphabetic,  7 │  combinator::{map_res, opt, recognize, value},  8 │  error,  9 │  error::FromExternalError,  10 │  multi::*,  11 │  sequence::{delimited, preceded, terminated, tuple},  12 │  IResult, Parser,  13 │ };  14 │ use std::str::FromStr;  15 │ use tui::{  16 │  style::{Color, Modifier, Style},  17 │  text::{Line, Span, Text},  18 │ };  19 │  20 │ #[derive(Debug, Clone, Copy, Eq, PartialEq)]  21 │ enum ColorType {  22 │  /// Eight Bit color  23 │  EightBit,  24 │  /// 24-bit color or true color  25 │  TrueColor,  26 │ }  27 │  28 │ #[derive(Debug, Clone, PartialEq)]  29 │ struct AnsiItem {  30 │  code: AnsiCode,  31 │  color: Option<Color>,  32 │ }  33 │  34 │ #[derive(Debug, Clone, PartialEq)]  35 │ struct AnsiStates {  36 │  pub items: smallvec::SmallVec<[AnsiItem; 2]>,  37 │  pub style: Style,  38 │ }  39 │  40 │ impl From<AnsiStates> for tui::style::Style {  41 │  fn from(states: AnsiStates) -> Self {  42 │  let mut style = states.style;  43 │  for item in states.items {  44 │  match item.code {  45 │  AnsiCode::Reset => style = Style::default(),  46 │  AnsiCode::Bold => style = style.add_modifier(Modifier::BOLD),  47 │  AnsiCode::Faint => style = style.add_modifier(Modifier::DIM),  48 │  AnsiCode::Italic => style = style.add_modifier(Modifier::ITALIC),  49 │  AnsiCode::Underline => style = style.add_modifier(Modifier::UNDERLINED),  50 │  AnsiCode::SlowBlink => style = style.add_modifier(Modifier::SLOW_BLINK),  51 │  AnsiCode::RapidBlink => style = style.add_modifier(Modifier::RAPID_BLINK),  52 │  AnsiCode::Reverse => style = style.add_modifier(Modifier::REVERSED),  53 │  AnsiCode::Conceal => style = style.add_modifier(Modifier::HIDDEN),  54 │  AnsiCode::CrossedOut => style = style.add_modifier(Modifier::CROSSED_OUT),  55 │  AnsiCode::DefaultForegroundColor => style = style.fg(Color::Reset),  56 │  AnsiCode::DefaultBackgroundColor => style = style.bg(Color::Reset),  57 │  AnsiCode::SetForegroundColor => {  58 │  if let Some(color) = item.color {  59 │  style = style.fg(color)  60 │  }  61 │  }  62 │  AnsiCode::SetBackgroundColor => {  63 │  if let Some(color) = item.color {  64 │  style = style.bg(color)  65 │  }  66 │  }  67 │  AnsiCode::ForegroundColor(color) => style = style.fg(color),  68 │  AnsiCode::BackgroundColor(color) => style = style.bg(color),  69 │  AnsiCode::AlternateFonts(_) => (),  70 │  _ => (),  71 │  }  72 │  }  73 │  style  74 │  }  75 │ }  76 │  77 │ pub(crate) fn text(mut s: &[u8]) -> IResult<&[u8], Text<'static>> {  78 │  let mut lines = Vec::new();  79 │  let mut last = Default::default();  80 │  while let Ok((_s, (line, style))) = line(last)(s) {  81 │  lines.push(line);  82 │  last = style;  83 │  s = _s;  84 │  if s.is_empty() {  85 │  break;  86 │  }  87 │  }  88 │  Ok((s, Text::from(lines)))  89 │ }  90 │  91 │ #[cfg(feature = "zero-copy")]  92 │ pub(crate) fn text_fast(mut s: &[u8]) -> IResult<&[u8], Text<'_>> {  93 │  let mut lines = Vec::new();  94 │  let mut last = Default::default();  95 │  while let Ok((_s, (line, style))) = line_fast(last)(s) {  96 │  lines.push(line);  97 │  last = style;  98 │  s = _s;  99 │  if s.is_empty() {  100 │  break;  101 │  }  102 │  }  103 │  Ok((s, Text::from(lines)))  104 │ }  105 │  106 │ fn line(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], (Line<'static>, Style)> {  107 │  // let style_: Style = Default::default();  108 │  move |s: &[u8]| -> IResult<&[u8], (Line<'static>, Style)> {  109 │  let (s, mut text) = take_while(|c| c != b'\n')(s)?;  110 │  let (s, _) = opt(tag("\n"))(s)?;  111 │  let mut spans = Vec::new();  112 │  let mut last = style;  113 │  while let Ok((s, span)) = span(last)(text) {  114 │  if span.style == Style::default() && span.content.is_empty() {  115 │  // Reset styles  116 │  last = Style::default();  117 │  } else {  118 │  last = last.patch(span.style);  119 │  }  120 │  // Don't include empty spans but keep changing the style  121 │  if spans.is_empty() || span.content != "" {  122 │  spans.push(span);  123 │  }  124 │  text = s;  125 │  if text.is_empty() {  126 │  break;  127 │  }  128 │  }  129 │  130 │  Ok((s, (Line::from(spans), last)))  131 │  }  132 │ }  133 │  134 │ #[cfg(feature = "zero-copy")]  135 │ fn line_fast(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], (Line<'_>, Style)> {  136 │  // let style_: Style = Default::default();  137 │  move |s: &[u8]| -> IResult<&[u8], (Line<'_>, Style)> {  138 │  let (s, mut text) = take_while(|c| c != b'\n')(s)?;  139 │  let (s, _) = opt(tag("\n"))(s)?;  140 │  let mut spans = Vec::new();  141 │  let mut last = style;  142 │  while let Ok((s, span)) = span_fast(last)(text) {  143 │  if span.style == Style::default() && span.content.is_empty() {  144 │  // Reset styles  145 │  last = Style::default();  146 │  } else {  147 │  last = last.patch(span.style);  148 │  }  149 │  // Don't include empty spans but keep changing the style  150 │  if spans.is_empty() || !span.content.is_empty() {  151 │  spans.push(span);  152 │  }  153 │  text = s;  154 │  if text.is_empty() {  155 │  break;  156 │  }  157 │  }  158 │  159 │  Ok((s, (Line::from(spans), last)))  160 │  }  161 │ }  162 │  163 │ // fn span(s: &[u8]) -> IResult<&[u8], tui::text::Span> {  164 │ fn span(last: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Span<'static>, nom::error::Error<&[u8]>> {  165 │  move |s: &[u8]| -> IResult<&[u8], Span<'static>> {  166 │  let mut last = last;  167 │  let (s, style) = opt(style(last))(s)?;  168 │  169 │  #[cfg(feature = "simd")]  170 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  171 │  simdutf8::basic::from_utf8(t)  172 │  })(s)?;  173 │  174 │  #[cfg(not(feature = "simd"))]  175 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  176 │  std::str::from_utf8(t)  177 │  })(s)?;  178 │  179 │  if let Some(style) = style {  180 │  if style == Default::default() {  181 │  last = Default::default();  182 │  } else {  183 │  last = last.patch(style);  184 │  }  185 │  }  186 │  187 │  Ok((s, Span::styled(text.to_owned(), last)))  188 │  }  189 │ }  190 │  191 │ #[cfg(feature = "zero-copy")]  192 │ fn span_fast(last: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Span<'_>, nom::error::Error<&[u8]>> {  193 │  move |s: &[u8]| -> IResult<&[u8], Span<'_>> {  194 │  let mut last = last;  195 │  let (s, style) = opt(style_fast(last))(s)?;  196 │  197 │  #[cfg(feature = "simd")]  198 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  199 │  simdutf8::basic::from_utf8(t)  200 │  })(s)?;  201 │  202 │  #[cfg(not(feature = "simd"))]  203 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  204 │  std::str::from_utf8(t)  205 │  })(s)?;  206 │  207 │  if let Some(style) = style {  208 │  if style == Default::default() {  209 │  last = Default::default();  210 │  } else {  211 │  last = last.patch(style);  212 │  }  213 │  }  214 │  215 │  Ok((s, Span::styled(text.to_owned(), last)))  216 │  }  217 │ }  218 │  219 │ fn style(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Style, nom::error::Error<&[u8]>> {  220 │  move |s: &[u8]| -> IResult<&[u8], Style> {  221 │  let (s, r) = match opt(ansi_sgr_code)(s)? {  222 │  (s, Some(r)) => (s, r),  223 │  (s, None) => {  224 │  let (s, _) = any_escape_sequence(s)?;  225 │  (s, Vec::new())  226 │  }  227 │  };  228 │  Ok((  229 │  s,  230 │  Style::from(AnsiStates {  231 │  style,  232 │  items: r.into(),  233 │  }),  234 │  ))  235 │  }  236 │ }  237 │  238 │ #[cfg(feature = "zero-copy")]  239 │ fn style_fast(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Style, nom::error::Error<&[u8]>> {  240 │  move |s: &[u8]| -> IResult<&[u8], Style> {  241 │  let (s, r) = match opt(ansi_sgr_code_fast)(s)? {  242 │  (s, Some(r)) => (s, r),  243 │  (s, None) => {  244 │  let (s, _) = any_escape_sequence(s)?;  245 │  (s, Default::default())  246 │  }  247 │  };  248 │  Ok((s, Style::from(AnsiStates { style, items: r })))  249 │  }  250 │ }  251 │  252 │ /// A complete ANSI SGR code  253 │ fn ansi_sgr_code(s: &[u8]) -> IResult<&[u8], Vec<AnsiItem>, nom::error::Error<&[u8]>> {  254 │  delimited(  255 │  tag("\x1b["),  256 │  separated_list1(tag(";"), ansi_sgr_item),  257 │  char('m'),  258 │  )(s)  259 │ }  260 │  261 │ #[cfg(feature = "zero-copy")]  262 │ fn ansi_sgr_code_fast(  263 │  s: &[u8],  264 │ ) -> IResult<&[u8], smallvec::SmallVec<[AnsiItem; 2]>, nom::error::Error<&[u8]>> {  265 │  delimited(  266 │  tag("\x1b["),  267 │  fold_many1(ansi_sgr_item, smallvec::SmallVec::new, |mut items, item| {  268 │  items.push(item);  269 │  items  270 │  }),  271 │  char('m'),  272 │  )(s)  273 │ }  274 │  275 │ fn any_escape_sequence(s: &[u8]) -> IResult<&[u8], Option<&[u8]>> {  276 │  // Attempt to consume most escape codes, including a single escape char.  277 │  //  278 │  // Most escape codes begin with ESC[ and are terminated by an alphabetic character,  279 │  // but OSC codes begin with ESC] and are terminated by an ascii bell (\x07)  280 │  // and a truncated/invalid code may just be a standalone ESC or not be terminated.  281 │  //  282 │  // We should try to consume as much of it as possible to match behavior of most terminals;  283 │  // where we fail at that we should at least consume the escape char to avoid infinitely looping  284 │  285 │  preceded(  286 │  char('\x1b'),  287 │  opt(alt((  288 │  delimited(char('['), take_till(is_alphabetic), opt(take(1u8))),  289 │  delimited(char(']'), take_till(|c| c == b'\x07'), opt(take(1u8))),  290 │  ))),  291 │  )(s)  292 │ }  293 │  294 │ /// An ANSI SGR attribute  295 │ fn ansi_sgr_item(s: &[u8]) -> IResult<&[u8], AnsiItem> {  296 │  let (s, c) = u8(s)?;  297 │  let code = AnsiCode::from(c);  298 │  let (s, color) = match code {  299 │  AnsiCode::SetForegroundColor | AnsiCode::SetBackgroundColor => {  300 │  let (s, _) = opt(tag(";"))(s)?;  301 │  let (s, color) = color(s)?;  302 │  (s, Some(color))  303 │  }  304 │  _ => (s, None),  305 │  };  306 │  let (s, _) = opt(tag(";"))(s)?;  307 │  Ok((s, AnsiItem { code, color }))  308 │ }  309 │  310 │ fn color(s: &[u8]) -> IResult<&[u8], Color> {  311 │  let (s, c_type) = color_type(s)?;  312 │  let (s, _) = opt(tag(";"))(s)?;  313 │  match c_type {  314 │  ColorType::TrueColor => {  315 │  let (s, (r, _, g, _, b)) = tuple((u8, tag(";"), u8, tag(";"), u8))(s)?;  316 │  Ok((s, Color::Rgb(r, g, b)))  317 │  }  318 │  ColorType::EightBit => {  319 │  let (s, index) = u8(s)?;  320 │  Ok((s, Color::Indexed(index)))  321 │  }  322 │  }  323 │ }  324 │  325 │ fn color_type(s: &[u8]) -> IResult<&[u8], ColorType> {  326 │  let (s, t) = i64(s)?;  327 │  // NOTE: This isn't opt because a color type must always be followed by a color  328 │  // let (s, _) = opt(tag(";"))(s)?;  329 │  let (s, _) = tag(";")(s)?;  330 │  match t {  331 │  2 => Ok((s, ColorType::TrueColor)),  332 │  5 => Ok((s, ColorType::EightBit)),  333 │  _ => Err(nom::Err::Error(nom::error::Error::new(  334 │  s,  335 │  nom::error::ErrorKind::Alt,  336 │  ))),  337 │  }  338 │ }  339 │  340 │ #[test]  341 │ fn color_test() {  342 │  let c = color(b"2;255;255;255").unwrap();  343 │  assert_eq!(c.1, Color::Rgb(255, 255, 255));  344 │  let c = color(b"5;255").unwrap();  345 │  assert_eq!(c.1, Color::Indexed(255));  346 │ }  347 │  348 │ #[test]  349 │ fn ansi_items_test() {  350 │  let sc = Default::default();  351 │  let t = style(sc)(b"\x1b[38;2;3;3;3m").unwrap();  352 │  assert_eq!(  353 │  t.1,  354 │  Style::from(AnsiStates {  355 │  style: sc,  356 │  items: vec![AnsiItem {  357 │  code: AnsiCode::SetForegroundColor,  358 │  color: Some(Color::Rgb(3, 3, 3))  359 │  }]  360 │  .into()  361 │  })  362 │  );  363 │ } ───────┴──────────────────────────────────────────────────────────────────────── ansi-to-tui-6.0.0/ascii/text.ascii000064400000000000000000000000730072674642500151160ustar 00000000000000AAAABBBB TRUECOLOR ansi-to-tui-6.0.0/benches/parsing.rs000064400000000000000000000023010072674642500154440ustar 00000000000000pub use ansi_to_tui::IntoText; use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn criterion_benchmark(c: &mut Criterion) { const BASIC: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/ascii/archlinux.ascii" )); #[cfg(feature = "zero-copy")] c.bench_function("Parsing bench zero copy", |b| { b.iter(|| { let s = black_box(&BASIC); black_box(s.to_text()).unwrap(); }) }); c.bench_function("Parsing bench", |b| { b.iter(|| { let s = black_box(&BASIC); black_box(s.into_text()).unwrap(); }) }); const CODE: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/ascii/code.ascii")); #[cfg(feature = "zero-copy")] c.bench_function("Parsing bench zero copy code", |b| { b.iter(|| { let s = black_box(&CODE); black_box(s.to_text()).unwrap(); }) }); c.bench_function("Parsing bench code", |b| { b.iter(|| { let s = black_box(&CODE); black_box(s.into_text()).unwrap(); }) }); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); ansi-to-tui-6.0.0/flake.lock000064400000000000000000000047170072674642500137750ustar 00000000000000{ "nodes": { "crane": { "inputs": { "nixpkgs": [ "nixpkgs" ] }, "locked": { "lastModified": 1718730147, "narHash": "sha256-QmD6B6FYpuoCqu6ZuPJH896ItNquDkn0ulQlOn4ykN8=", "owner": "ipetkov", "repo": "crane", "rev": "32c21c29b034d0a93fdb2379d6fabc40fc3d0e6c", "type": "github" }, "original": { "owner": "ipetkov", "repo": "crane", "type": "github" } }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { "lastModified": 1710146030, "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1719241184, "narHash": "sha256-fz3Yc6bMhqhhF/xVrLE44Kew0/M78Xkl7sKXCfeFcI4=", "owner": "nixos", "repo": "nixpkgs", "rev": "b4a237a1b79ef60236afef7b85d78118f3874d1d", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "crane": "crane", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" } }, "rust-overlay": { "inputs": { "nixpkgs": [ "nixpkgs" ] }, "locked": { "lastModified": 1719195554, "narHash": "sha256-bFXHMjpYlEERexzXa1gLGJO/1l8dxaAtSNE56YALuTg=", "owner": "oxalica", "repo": "rust-overlay", "rev": "577ee84c69ba89894ac622d71a678a14d746b2f7", "type": "github" }, "original": { "owner": "oxalica", "repo": "rust-overlay", "type": "github" } }, "systems": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "owner": "nix-systems", "repo": "default", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default", "type": "github" } } }, "root": "root", "version": 7 } ansi-to-tui-6.0.0/flake.nix000064400000000000000000000071420072674642500136360ustar 00000000000000{ description = "A simple rust flake using rust-overlay and craneLib"; inputs = { nixpkgs.url = "github:nixos/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; crane = { url = "github:ipetkov/crane"; inputs.nixpkgs.follows = "nixpkgs"; }; rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { crane, flake-utils, nixpkgs, rust-overlay, ... }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = import nixpkgs { inherit system; overlays = [rust-overlay.overlays.default]; }; inherit (pkgs) lib; stableToolchain = pkgs.rust-bin.stable.latest.default; stableToolchainWithRustAnalyzer = pkgs.rust-bin.stable.latest.default.override { extensions = ["rust-src" "rust-analyzer" "llvm-tools"]; # Extra targets if required # targets = [ # "x86_64-unknown-linux-gnu" # "x86_64-unknown-linux-musl" # "x86_64-apple-darwin" # "aarch64-apple-darwin" # ]; }; craneLib = (crane.mkLib pkgs).overrideToolchain stableToolchain; craneLibLLvmTools = craneLib.overrideToolchain (pkgs.rust-bin.stable.latest.default.override { extensions = [ "cargo" "llvm-tools" "rustc" ]; }); src = craneLib.path ./.; commonArgs = { inherit src; buildInputs = with pkgs; [] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ libiconv # pkgs.darwin.apple_sdk.frameworks.Security # pkgs.darwin.apple_sdk.frameworks.CoreServices # pkgs.darwin.apple_sdk.frameworks.SystemConfiguration ]; # Inputs required for the TARGET system # nativeBuildInputs = []; # Intputs required for the HOST system # This is often requird for any ffi based packages that use bindgen # LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; # For using pkg-config that many libraries require # PKG_CONFIG_PATH = lib.makeSearchPath "lib/pkgconfig" (with pkgs;[ openssl.dev zlib.dev ]); }; cargoArtifacts = craneLib.buildDepsOnly commonArgs; in { checks = { ansi-to-tui-clippy = craneLib.cargoClippy (commonArgs // { inherit cargoArtifacts; cargoClippyExtraArgs = "--all-targets -- --deny warnings"; }); ansi-to-tui-fmt = craneLib.cargoFmt { inherit src; }; ansi-to-tui-nextest = craneLib.cargoNextest (commonArgs // { inherit cargoArtifacts; partitions = 1; partitionType = "count"; }); } // lib.optionalAttrs (!pkgs.stdenv.isDarwin) { ansi-to-tui-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs // { inherit cargoArtifacts; }); }; devShells.default = (craneLib.overrideToolchain stableToolchainWithRustAnalyzer).devShell (commonArgs // { buildInputs = []; nativeBuildInputs = []; packages = with pkgs; [ cargo-nextest cargo-criterion cargo-outdated cargo-mutants ] ++ lib.optionals pkgs.stdenv.isLinux [ cargo-llvm-cov ]; }); } ); } ansi-to-tui-6.0.0/src/code.rs000064400000000000000000000116740072674642500141100ustar 00000000000000use tui::style::Color; /// This enum stores most types of ansi escape sequences /// /// You can turn an escape sequence to this enum variant using /// AnsiCode::from(code: u8) /// This doesn't support all of them but does support most of them. #[derive(Debug, PartialEq, Clone)] #[non_exhaustive] pub enum AnsiCode { /// Reset the terminal Reset, /// Set font to bold Bold, /// Set font to faint Faint, /// Set font to italic Italic, /// Set font to underline Underline, /// Set cursor to slowblink SlowBlink, /// Set cursor to rapidblink RapidBlink, /// Invert the colors Reverse, /// Conceal text Conceal, /// Display crossed out text CrossedOut, /// Choose primary font PrimaryFont, /// Choose alternate font AlternateFont, /// Choose alternate fonts 1-9 #[allow(dead_code)] AlternateFonts(u8), // = 11..19, // from 11 to 19 /// Fraktur ? No clue Fraktur, /// Turn off bold BoldOff, /// Set text to normal Normal, /// Turn off Italic NotItalic, /// Turn off underline UnderlineOff, /// Turn off blinking BlinkOff, // 26 ? /// Don't invert colors InvertOff, /// Reveal text Reveal, /// Turn off Crossedout text CrossedOutOff, /// Set foreground color (4-bit) ForegroundColor(Color), //, 31..37//Issue 60553 https://github.com/rust-lang/rust/issues/60553 /// Set foreground color (8-bit and 24-bit) SetForegroundColor, /// Default foreground color DefaultForegroundColor, /// Set background color (4-bit) BackgroundColor(Color), // 41..47 /// Set background color (8-bit and 24-bit) SetBackgroundColor, /// Default background color DefaultBackgroundColor, // 49 /// Other / non supported escape codes Code(Vec), } impl From for AnsiCode { fn from(code: u8) -> Self { match code { 0 => AnsiCode::Reset, 1 => AnsiCode::Bold, 2 => AnsiCode::Faint, 3 => AnsiCode::Italic, 4 => AnsiCode::Underline, 5 => AnsiCode::SlowBlink, 6 => AnsiCode::RapidBlink, 7 => AnsiCode::Reverse, 8 => AnsiCode::Conceal, 9 => AnsiCode::CrossedOut, 10 => AnsiCode::PrimaryFont, 11 => AnsiCode::AlternateFont, // AnsiCode::// AlternateFont = 11..19, // from 11 to 19 20 => AnsiCode::Fraktur, 21 => AnsiCode::BoldOff, 22 => AnsiCode::Normal, 23 => AnsiCode::NotItalic, 24 => AnsiCode::UnderlineOff, 25 => AnsiCode::BlinkOff, // 26 ? 27 => AnsiCode::InvertOff, 28 => AnsiCode::Reveal, 29 => AnsiCode::CrossedOutOff, 30 => AnsiCode::ForegroundColor(Color::Black), 31 => AnsiCode::ForegroundColor(Color::Red), 32 => AnsiCode::ForegroundColor(Color::Green), 33 => AnsiCode::ForegroundColor(Color::Yellow), 34 => AnsiCode::ForegroundColor(Color::Blue), 35 => AnsiCode::ForegroundColor(Color::Magenta), 36 => AnsiCode::ForegroundColor(Color::Cyan), 37 => AnsiCode::ForegroundColor(Color::Gray), 38 => AnsiCode::SetForegroundColor, 39 => AnsiCode::DefaultForegroundColor, 40 => AnsiCode::BackgroundColor(Color::Black), 41 => AnsiCode::BackgroundColor(Color::Red), 42 => AnsiCode::BackgroundColor(Color::Green), 43 => AnsiCode::BackgroundColor(Color::Yellow), 44 => AnsiCode::BackgroundColor(Color::Blue), 45 => AnsiCode::BackgroundColor(Color::Magenta), 46 => AnsiCode::BackgroundColor(Color::Cyan), 47 => AnsiCode::BackgroundColor(Color::Gray), 48 => AnsiCode::SetBackgroundColor, 49 => AnsiCode::DefaultBackgroundColor, 90 => AnsiCode::ForegroundColor(Color::DarkGray), 91 => AnsiCode::ForegroundColor(Color::LightRed), 92 => AnsiCode::ForegroundColor(Color::LightGreen), 93 => AnsiCode::ForegroundColor(Color::LightYellow), 94 => AnsiCode::ForegroundColor(Color::LightBlue), 95 => AnsiCode::ForegroundColor(Color::LightMagenta), 96 => AnsiCode::ForegroundColor(Color::LightCyan), 97 => AnsiCode::ForegroundColor(Color::White), 100 => AnsiCode::BackgroundColor(Color::DarkGray), 101 => AnsiCode::BackgroundColor(Color::LightRed), 102 => AnsiCode::BackgroundColor(Color::LightGreen), 103 => AnsiCode::BackgroundColor(Color::LightYellow), 104 => AnsiCode::BackgroundColor(Color::LightBlue), 105 => AnsiCode::BackgroundColor(Color::LightMagenta), 106 => AnsiCode::BackgroundColor(Color::LightCyan), 107 => AnsiCode::ForegroundColor(Color::White), code => AnsiCode::Code(vec![code]), } } } ansi-to-tui-6.0.0/src/error.rs000064400000000000000000000013360072674642500143210ustar 00000000000000/// This enum stores the error types #[derive(Debug, thiserror::Error, PartialEq)] pub enum Error { /// Stack is empty (should never happen) #[error("Nom Error")] NomError(String), /// Error parsing the input as utf-8 #[cfg(feature = "simdutf8")] /// Cannot determine the foreground or background #[error("{0:?}")] Utf8Error(#[from] simdutf8::basic::Utf8Error), #[cfg(not(feature = "simdutf8"))] /// Cannot determine the foreground or background #[error("{0:?}")] Utf8Error(#[from] std::string::FromUtf8Error), } impl From>> for Error { fn from(e: nom::Err>) -> Self { Self::NomError(format!("{:?}", e)) } } ansi-to-tui-6.0.0/src/lib.rs000064400000000000000000000041030072674642500137310ustar 00000000000000#![allow(unused_imports)] #![warn(missing_docs)] //! Parses a `Vec` as an byte sequence with ansi colors to //! [`tui::text::Text`][Text]. //! //! Invalid ansi colors / sequences will be ignored. //! //! //! Supported features //! - UTF-8 using `String::from_utf8` or [`simdutf8`][simdutf8]. //! - Most stuff like **Bold** / *Italic* / Underline / ~~Strikethrough~~. //! - Supports 4-bit color palletes. //! - Supports 8-bit color. //! - Supports True color ( RGB / 24-bit color ). //! //! //! ## Example //! The argument to the function `ansi_to_text` implements `IntoIterator` so it will be consumed on //! use. //! ```rust //! use ansi_to_tui::IntoText; //! let bytes = b"\x1b[38;2;225;192;203mAAAAA\x1b[0m".to_owned().to_vec(); //! let text = bytes.into_text().unwrap(); //! ``` //! Example parsing from a file. //! ```rust //! use ansi_to_tui::IntoText; //! let buffer = std::fs::read("ascii/text.ascii").unwrap(); //! let text = buffer.into_text().unwrap(); //! ``` //! //! If you want to use [`simdutf8`][simdutf8] instead of `String::from_utf8()` //! for parsing UTF-8 then enable optional feature `simd` //! //! [Text]: https://docs.rs/tui/0.15.0/tui/text/struct.Text.html //! [ansi-to-tui]: https://github.com/uttarayan21/ansi-to-tui //! [simdutf8]: https://github.com/rusticstuff/simdutf8 // mod ansi; mod code; mod error; mod parser; pub use error::Error; use tui::text::Text; /// IntoText will convert any type that has a AsRef<[u8]> to a Text. pub trait IntoText { /// Convert the type to a Text. #[allow(clippy::wrong_self_convention)] fn into_text(&self) -> Result, Error>; /// Convert the type to a Text while trying to copy as less as possible #[cfg(feature = "zero-copy")] fn to_text(&self) -> Result, Error>; } impl IntoText for T where T: AsRef<[u8]>, { fn into_text(&self) -> Result, Error> { Ok(crate::parser::text(self.as_ref())?.1) } #[cfg(feature = "zero-copy")] fn to_text(&self) -> Result, Error> { Ok(crate::parser::text_fast(self.as_ref())?.1) } } ansi-to-tui-6.0.0/src/parser.rs000064400000000000000000000273040072674642500144670ustar 00000000000000use crate::code::AnsiCode; use nom::{ branch::alt, bytes::complete::*, character::complete::*, character::is_alphabetic, combinator::{map_res, opt, recognize, value}, error, error::FromExternalError, multi::*, sequence::{delimited, preceded, terminated, tuple}, IResult, Parser, }; use std::str::FromStr; use tui::{ style::{Color, Modifier, Style}, text::{Line, Span, Text}, }; #[derive(Debug, Clone, Copy, Eq, PartialEq)] enum ColorType { /// Eight Bit color EightBit, /// 24-bit color or true color TrueColor, } #[derive(Debug, Clone, PartialEq)] struct AnsiItem { code: AnsiCode, color: Option, } #[derive(Debug, Clone, PartialEq)] struct AnsiStates { pub items: smallvec::SmallVec<[AnsiItem; 2]>, pub style: Style, } impl From for tui::style::Style { fn from(states: AnsiStates) -> Self { let mut style = states.style; if states.items.is_empty() { // https://github.com/uttarayan21/ansi-to-tui/issues/40 // [m should be treated as a reset as well style = Style::reset(); } for item in states.items { match item.code { AnsiCode::Reset => style = Style::reset(), AnsiCode::Bold => style = style.add_modifier(Modifier::BOLD), AnsiCode::Faint => style = style.add_modifier(Modifier::DIM), AnsiCode::Italic => style = style.add_modifier(Modifier::ITALIC), AnsiCode::Underline => style = style.add_modifier(Modifier::UNDERLINED), AnsiCode::SlowBlink => style = style.add_modifier(Modifier::SLOW_BLINK), AnsiCode::RapidBlink => style = style.add_modifier(Modifier::RAPID_BLINK), AnsiCode::Reverse => style = style.add_modifier(Modifier::REVERSED), AnsiCode::Conceal => style = style.add_modifier(Modifier::HIDDEN), AnsiCode::CrossedOut => style = style.add_modifier(Modifier::CROSSED_OUT), AnsiCode::DefaultForegroundColor => style = style.fg(Color::Reset), AnsiCode::DefaultBackgroundColor => style = style.bg(Color::Reset), AnsiCode::SetForegroundColor => { if let Some(color) = item.color { style = style.fg(color) } } AnsiCode::SetBackgroundColor => { if let Some(color) = item.color { style = style.bg(color) } } AnsiCode::ForegroundColor(color) => style = style.fg(color), AnsiCode::BackgroundColor(color) => style = style.bg(color), _ => (), } } style } } pub(crate) fn text(mut s: &[u8]) -> IResult<&[u8], Text<'static>> { let mut lines = Vec::new(); let mut last = Style::new(); while let Ok((_s, (line, style))) = line(last)(s) { lines.push(line); last = style; s = _s; if s.is_empty() { break; } } Ok((s, Text::from(lines))) } #[cfg(feature = "zero-copy")] pub(crate) fn text_fast(mut s: &[u8]) -> IResult<&[u8], Text<'_>> { let mut lines = Vec::new(); let mut last = Style::new(); while let Ok((_s, (line, style))) = line_fast(last)(s) { lines.push(line); last = style; s = _s; if s.is_empty() { break; } } Ok((s, Text::from(lines))) } fn line(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], (Line<'static>, Style)> { // let style_: Style = Default::default(); move |s: &[u8]| -> IResult<&[u8], (Line<'static>, Style)> { let (s, mut text) = take_while(|c| c != b'\n')(s)?; let (s, _) = opt(tag("\n"))(s)?; let mut spans = Vec::new(); let mut last = style; while let Ok((s, span)) = span(last)(text) { // Since reset now tracks seperately we can skip the reset check last = last.patch(span.style); if !span.content.is_empty() { spans.push(span); } text = s; if text.is_empty() { break; } } Ok((s, (Line::from(spans), last))) } } #[cfg(feature = "zero-copy")] fn line_fast(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], (Line<'_>, Style)> { // let style_: Style = Default::default(); move |s: &[u8]| -> IResult<&[u8], (Line<'_>, Style)> { let (s, mut text) = take_while(|c| c != b'\n')(s)?; let (s, _) = opt(tag("\n"))(s)?; let mut spans = Vec::new(); let mut last = style; while let Ok((s, span)) = span_fast(last)(text) { last = last.patch(span.style); // If the spans is empty then it might be possible that the style changes // but there is no text change if !span.content.is_empty() { spans.push(span); } text = s; if text.is_empty() { break; } } Ok((s, (Line::from(spans), last))) } } // fn span(s: &[u8]) -> IResult<&[u8], tui::text::Span> { fn span(last: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Span<'static>, nom::error::Error<&[u8]>> { move |s: &[u8]| -> IResult<&[u8], Span<'static>> { let mut last = last; let (s, style) = opt(style(last))(s)?; #[cfg(feature = "simd")] let (s, text) = map_res(take_while(|c| c != b'\x1b' && c != b'\n'), |t| { simdutf8::basic::from_utf8(t) })(s)?; #[cfg(not(feature = "simd"))] let (s, text) = map_res(take_while(|c| c != b'\x1b' && c != b'\n'), |t| { std::str::from_utf8(t) })(s)?; if let Some(style) = style.flatten() { last = last.patch(style); } Ok((s, Span::styled(text.to_owned(), last))) } } #[cfg(feature = "zero-copy")] fn span_fast(last: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Span<'_>, nom::error::Error<&[u8]>> { move |s: &[u8]| -> IResult<&[u8], Span<'_>> { let mut last = last; let (s, style) = opt(style(last))(s)?; #[cfg(feature = "simd")] let (s, text) = map_res(take_while(|c| c != b'\x1b' && c != b'\n'), |t| { simdutf8::basic::from_utf8(t) })(s)?; #[cfg(not(feature = "simd"))] let (s, text) = map_res(take_while(|c| c != b'\x1b' && c != b'\n'), |t| { std::str::from_utf8(t) })(s)?; if let Some(style) = style.flatten() { last = last.patch(style); } Ok((s, Span::styled(text, last))) } } fn style( style: Style, ) -> impl Fn(&[u8]) -> IResult<&[u8], Option