{"id":32161365,"url":"https://github.com/earthcitizen/escape-artist","last_synced_at":"2025-10-21T13:53:39.653Z","repository":{"id":62435919,"uuid":"67478860","full_name":"EarthCitizen/escape-artist","owner":"EarthCitizen","description":"A Haskell library for text decoration with ANSI escape sequences made easy","archived":false,"fork":false,"pushed_at":"2020-01-28T00:07:06.000Z","size":340,"stargazers_count":10,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-21T13:53:23.620Z","etag":null,"topics":["ansi-colors","ansi-escape-sequences","haskell","haskell-library","text-decoration"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EarthCitizen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-09-06T06:12:23.000Z","updated_at":"2021-10-22T02:21:56.000Z","dependencies_parsed_at":"2022-11-01T21:16:54.485Z","dependency_job_id":null,"html_url":"https://github.com/EarthCitizen/escape-artist","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/EarthCitizen/escape-artist","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EarthCitizen%2Fescape-artist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EarthCitizen%2Fescape-artist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EarthCitizen%2Fescape-artist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EarthCitizen%2Fescape-artist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EarthCitizen","download_url":"https://codeload.github.com/EarthCitizen/escape-artist/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EarthCitizen%2Fescape-artist/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280272338,"owners_count":26302260,"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","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["ansi-colors","ansi-escape-sequences","haskell","haskell-library","text-decoration"],"created_at":"2025-10-21T13:53:36.689Z","updated_at":"2025-10-21T13:53:39.648Z","avatar_url":"https://github.com/EarthCitizen.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# escape-artist\n\n[![Build Status](https://travis-ci.org/EarthCitizen/escape-artist.svg?branch=master)](https://travis-ci.org/EarthCitizen/escape-artist)\n[![Coverage Status](https://coveralls.io/repos/github/EarthCitizen/escape-artist/badge.svg?branch=master)](https://coveralls.io/github/EarthCitizen/escape-artist?branch=master)\n\nA Haskell library for text decoration with ANSI escape sequences made easy. Decorate your terminal text easily and expressively.\n\n## Getting Started\n\n### Building from Source\n#### Prerequisites\n\nTo build this project from source, you will need to install stack. See https://docs.haskellstack.org/en/stable/README/#how-to-install for detailed installation instructions for your operating system.\n\n#### Building\n\n```\ngit clone https://github.com/EarthCitizen/escape-artist\ncd escape-artist\nstack setup\nstack build\n```\n\n### Using\n\nThe data type used to perform text decoration is `Escapable`. This defines the constructors for the decoration. Each constructor takes a single argument. It can be any type that has implemented the `ToEscapable` class. This means that all of the following are perfectly valid:\n\n```haskell\n{-# LANGUAGE ExtendedDefaultRules #-}\n\nFgRed 6\nFgRed \"6\"\nFgRed '6'\nFgRed (6 :: Float)\nFgRed (6 :: Double)\n```\n\nAnd can all dwell in the same list:\n\n```haskell\n{-# LANGUAGE ExtendedDefaultRules #-}\n\nimport Data.List (intersperse)\nimport Text.EscapeArtist\n\nlet redList = [FgRed 6, FgRed \"6\", FgRed '6', FgRed (6 :: Float), FgRed (6 :: Double)]\n\nputEscLn $ mconcat $ intersperse (Inherit \" \") redList\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/six.png)\n\nThe following data types already come with an implementation of `ToEscapable`:\n\n* `Char`\n* `ByteString of Data.ByteString`\n* `ByteString of Data.ByteString.Lazy`\n* `Text of Data.Text`\n* `Text of Data.Text.Lazy`\n* `Double`\n* `Float`\n* `Int`\n* `Integer`\n* `String`\n* `Word`\n* `Word8`\n* `Word16`\n* `Word32`\n* `Word64`\n\nImplementing `ToEscapable` for other data types is fairly simple:\n\n```haskell\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\ndata ABC = A | B deriving (Show, Eq)\n\ninstance ToEscapable ABC where\n   toEscapable (A) = FgRed $ show A\n   toEscapable (B) = FgGreen $ show B\n\ninstance (ToEscapable a) =\u003e ToEscapable (Maybe a) where\n    toEscapable (Just a) = FgGreen \"Just\" \u003c\u003e Inherit \" \" \u003c\u003e FgYellow a\n    toEscapable a = FgRed $ show a\n\nputEscLn A\nputEscLn B\nputEscLn $ Just 15\nputEscLn (Nothing :: Maybe Int)\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/abc_maybe.png)\n\nWhen constructors are combined with the application operator (`$`), the effects accumulate and wrap around the applied value:\n\n```haskell\nimport Text.EscapeArtist\n\nlet combined = FgRed $ Underline $ Blink \"Hello World!\"\n```\n\nwould be equivalent to the following in XML:\n\n```xml\n\u003cred\u003e\n    \u003cunderline\u003e\n        \u003cblink\u003e\n            Hello World!\n        \u003c/blink\u003e\n    \u003c/underline\u003e\n\u003c/red\u003e\n```\n\n**NOTE**: _This library does not produce nor interact with XML. This example is just for the purpose of explanation_.\n\n`Escapable` is an instance of `Monoid`, so a series of `Escapable`s can be appended together into a single value:\n\n```haskell\n{-# LANGUAGE ExtendedDefaultRules #-}\n\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\nlet series = FgYellow 5 \u003c\u003e FgWhite 6\n\nputEscLn series\n```\n\nWhen a constructor is applied to a series of appended `Escapable`s using the `$`, the constructor will be applied to each member of the series.\n\n```haskell\n{-# LANGUAGE ExtendedDefaultRules #-}\n\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\nlet result = Underline $ FgYellow 5 \u003c\u003e FgWhite 6\n\nputEscLn result\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/56yw.png)\n\nXML equivalent:\n```XML\n\u003cunderline\u003e\n    \u003cyellow\u003e5\u003c/yellow\u003e\n\u003c/underline\u003e\n\u003cunderline\u003e\n    \u003cwhite\u003e6\u003c/white\u003e\n\u003c/underline\u003e\n```\n**NOTE**: _The `Underline` is re-applied to each member of the series, and not once for all of them_.\n\n## Constructors\n\n### Foreground Color\n\n`FgBlack FgRed FgGreen FgYellow FgBlue FgMagenta FgCyan FgWhite`\n\n### Background Color\n\n`BgBlack BgRed BgGreen BgYellow BgBlue BgMagenta BgCyan BgWhite`\n\n### Other Types\n\nName           | Effect on Applied Value\n-------------- | -----------------------\n`FgDefault`    | Default foreground color of the terminal.\n`BgDefault`    | Default background color of the terminal.\n`Inherit`      | Applies attributes of parent constructors. Useful for a value interspersed in a series with other `Escapable`s. See examples below.\n`Default`      | Even when other constructors are applied, the contained value will have the default attributes of the terminal.\n`Blink`        | Output blinks in terminal.\n`BlinkOff`     | NOT to end a blinking series, but rather to nest a non-blinking segment inside a series of blinking outputs.\n`Bright`       | Enables bright output for foreground colors.\n`BrightOff`    | NOT to end a bright series, but rather to nest a non-bright segment inside a series of bright outputs.\n`Underline`    | Underlines the output.\n`UnderlineOff` | NOT to end an underlined series, but rather to nest a non-underlined segment inside a series of underlined outputs.\n`Inverse`      | Switches the foreground and background colors.\n`InverseOff`   | NOT to end an inverse series, but rather to nest a non-inverse segment inside a series of inverse outputs.\n\n## Functions\n\nName          | Description\n------------- | -----------\n`escToString` | Renders anything implementing `ToEscapable` to a `String`.\n`putEsc`      | Renders anything implementing `ToEscapable` to a `String`, then writes it to standard out.\n`putEscLn`    | Renders anything implementing `ToEscapable` to a `String`, then writes it to standard out followed by a newline.\n\n## Operators\n\nSymbol | Purpose\n------ | -------\n`^$`   | Same as `$`, but one level of precedence higher than `\u003c\u003e` for avoiding the use of parentheses when needing to use `$` in the same expression as `\u003c\u003e`. See examples below.\n`/\u003c\u003e/` | The same as `\u003c\u003e`, except that any argument that is not of type `Escapable` will be wrapped in `Inherit` before being combined with the other argument via `\u003c\u003e`.\n\n## Examples\n\n### Inherit\n\n```haskell\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\nspacesInherit = FgRed '@' \u003c\u003e Inherit ' ' \u003c\u003e FgYellow '@' \u003c\u003e Inherit ' ' \u003c\u003e FgGreen '@'\n\nputEscLn spacesInherit\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/inherit_none.png)\n\n```haskell\nputEscLn $ Underline spacesInherit\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/inherit_underline.png)\n\n```haskell\nputEscLn $ Inverse spacesInherit\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/inherit_inverse.png)\n\n```haskell\nputEscLn $ BgBlue spacesInherit\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/inherit_bgblue.png)\n\n### UnderlineOff\n\n```haskell\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\nunderlines = Underline $ FgCyan \"I am underlined\" \u003c\u003e UnderlineOff \" but I am not \" \u003c\u003e FgMagenta \"and I am over here\"\n\nputEscLn underlines\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/underline_off.png)\n\nThe same type of functionality applies as well to `BlinkOff`, `BrightOff` and `InverseOff`.\n\n### Operator `^$`\n\nThis operator allows you to avoid parentheses in cases where you need to use `$` and `\u003c\u003e` in he same expression.\n\n```haskell\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\nop1 = Underline $ Bright ^$ FgGreen \"GREEN\" \u003c\u003e Default \" \" \u003c\u003e FgYellow \"YELLOW\"\n\nputEscLn op1\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/high_prec_apply_op.png)\n\nWithout `^$`, this would have to be written as:\n\n```haskell\nUnderline $ (Bright $ FgGreen \"GREEN\") \u003c\u003e Default \" \" \u003c\u003e FgYellow \"YELLOW\"\n```\n\n### Operator `/\u003c\u003e/`\n\nThis operator allows `Inherit` to be omitted.\n\n```haskell\nBgRed $ Inherit 4 \u003c\u003e BgCyan \" \" \u003c\u003e Inherit 5 \u003c\u003e BgGreen \" \" \u003c\u003e Inherit 9\n```\ncan simply be written as:\n\n```haskell\nBgRed $ 4 /\u003c\u003e/ BgCyan \" \" /\u003c\u003e/ 5 /\u003c\u003e/ BgGreen \" \" /\u003c\u003e/ 9\n```\n\n## Advanced Examples\n\n### Fun with Colors\n\n```haskell\nimport Data.Monoid (mempty, (\u003c\u003e))\nimport Text.EscapeArtist\n\nrainbowString :: String -\u003e Escapable\nrainbowString s = fn s (cycle [FgRed, FgWhite, FgGreen, FgBlue, FgYellow, FgCyan])\n    where fn [] _ = mempty\n          fn _ [] = mempty\n          fn (s:ss) ca@(c:cs)\n              | s `elem` \" \\t\\n\\r\" = Inherit s \u003c\u003e fn ss ca\n              | otherwise = c s \u003c\u003e fn ss cs\n\nputEscLn $ rainbowString \"Hello World!\"\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/rainbow_string.png)\n\n### Colorize Sections of a String\n\n```haskell\nimport Text.EscapeArtist\nimport Text.Regex\n\nreplaceNumbers :: String -\u003e String\nreplaceNumbers searchIn = subRegex (mkRegex \"([0-9]+)\") searchIn (escToString $ FgRed \"\\\\1\")\n\nputStrLn $ replaceNumbers \"Line 7 of 23\"\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/highlight_numbers.png)\n\n### Implement `ToEscapable` for Custom and Existing Data Types\n\n```haskell\n{-# LANGUAGE FlexibleInstances #-}\n\nimport Data.Monoid ((\u003c\u003e))\nimport Text.EscapeArtist\n\ntype FileName = String\ntype LineNumber = Integer\ntype ColumnNumber = Integer\ndata ErrorType = SyntaxError FileName LineNumber ColumnNumber deriving (Show)\n\ninstance ToEscapable ErrorType where\n    toEscapable (SyntaxError fn ln cn) = Default \"Syntax error in file \"\n                                       \u003c\u003e FgYellow ^$ Underline fn\n                                       \u003c\u003e Default \" at \"\n                                       \u003c\u003e FgRed (show ln ++ \":\" ++ show cn)\n\ninstance ToEscapable (Either ErrorType String) where\n    toEscapable (Left e) = toEscapable e\n    toEscapable (Right m) = FgGreen m\n\nmkSyntaxError :: FileName -\u003e LineNumber -\u003e ColumnNumber -\u003e Either ErrorType String\nmkSyntaxError fn ln cn = Left $ SyntaxError fn ln cn\n\nmkStatusOK :: Either ErrorType String\nmkStatusOK = Right \"Status OK\"\n\nputEscLn $ mkSyntaxError \"some/File.hs\" 1 23\nputEscLn mkStatusOK\n```\n\n![](https://raw.githubusercontent.com/EarthCitizen/escape-artist/master/images/either_error.png)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fearthcitizen%2Fescape-artist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fearthcitizen%2Fescape-artist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fearthcitizen%2Fescape-artist/lists"}