[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: tj"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.yml",
    "content": "## Prerequisites\n\n* [ ] I searched to see if the issue already exists.\n\n## Description\n\nDescribe the bug or feature.\n\n## Steps to Reproduce\n\nDescribe the steps required to reproduce the issue if applicable.\n\n## Slack\n\nJoin us on Slack https://chat.apex.sh/\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "Please open an issue and discuss changes before spending time on them, unless the change is trivial or an issue already exists.\n"
  },
  {
    "path": ".gitignore",
    "content": ".envrc\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License\n\nCopyright (c) 2019 TJ Holowaychuk tj@tjholowaychuk.com\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "Readme.md",
    "content": "# Termd\n\nPackage termd provides terminal markdown rendering, with code block syntax highlighting support.\n\n## Features\n\n- Code block syntax highlighting (JSON-friendly for loading from user config)\n- Word wrapping to the specified column width\n- Terminal specific styling (still looks like markdown but it's cleaned up for the term)\n\n![](https://apex-software.imgix.net/github/tj/termd/screenshot.png)\n\n---\n\n[![GoDoc](https://godoc.org/github.com/tj/go-termd?status.svg)](https://godoc.org/github.com/tj/go-termd)\n![](https://img.shields.io/badge/license-MIT-blue.svg)\n![](https://img.shields.io/badge/status-stable-green.svg)\n\n## Sponsors\n\nThis project is [sponsored](https://github.com/sponsors/tj) by [CTO.ai](https://cto.ai/), making it easy for development teams to create and share workflow automations without leaving the command line.\n\n[![](https://apex-software.imgix.net/github/sponsors/cto.png)](https://cto.ai/)\n\n\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/tj/go-termd\n\ngo 1.13\n\nrequire (\n\tgithub.com/alecthomas/chroma v0.6.8\n\tgithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59\n\tgithub.com/kr/text v0.1.0\n\tgithub.com/mitchellh/go-wordwrap v1.0.0\n\tgithub.com/russross/blackfriday/v2 v2.0.1\n\tgithub.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect\n\tgithub.com/tj/go-css v0.0.0-20191108133013-220a796d1705\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=\ngithub.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=\ngithub.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=\ngithub.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=\ngithub.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=\ngithub.com/alecthomas/chroma v0.6.8 h1:TW4JJaIdbAbMyUtGEd6BukFlOKYvVQz3vVhLBEUNwMU=\ngithub.com/alecthomas/chroma v0.6.8/go.mod h1:o9ohftueRi7H5be3+Q2cQCNa/YnLBFUNx40ZJfGVFKA=\ngithub.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=\ngithub.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=\ngithub.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=\ngithub.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=\ngithub.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA=\ngithub.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=\ngithub.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=\ngithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=\ngithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=\ngithub.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=\ngithub.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=\ngithub.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=\ngithub.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=\ngithub.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=\ngithub.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI=\ngithub.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=\ngithub.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/tj/assert v0.0.0-20190920132354-ee03d75cd160 h1:NSWpaDaurcAJY7PkL8Xt0PhZE7qpvbZl5ljd8r6U0bI=\ngithub.com/tj/assert v0.0.0-20190920132354-ee03d75cd160/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=\ngithub.com/tj/go-css v0.0.0-20191108133013-220a796d1705 h1:+UA89aFRjPMqdccHd9A0HLNCRDXIoElaDoW2C1V3TzA=\ngithub.com/tj/go-css v0.0.0-20191108133013-220a796d1705/go.mod h1:e+JPLQ9wyQCgRnPenX2bo7MJoLphBHz5c1WUqaANSeA=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=\ngolang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=\ngolang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "highlight.go",
    "content": "package termd\n\nimport (\n\t\"strings\"\n\n\t\"github.com/alecthomas/chroma\"\n\t\"github.com/alecthomas/chroma/lexers\"\n)\n\n// SyntaxHighlighter is the interface used to highlight blocks of code.\ntype SyntaxHighlighter interface {\n\tToken(chroma.Token) string\n}\n\n// highlight returns highlighted code, or the input text on error.\nfunc highlight(source, lang string, highlight SyntaxHighlighter) string {\n\tl := lexers.Get(lang)\n\tif l == nil {\n\t\treturn source\n\t}\n\n\tl = chroma.Coalesce(l)\n\n\tit, err := l.Tokenise(nil, source)\n\tif err != nil {\n\t\treturn source\n\t}\n\n\tvar w strings.Builder\n\tfor _, t := range it.Tokens() {\n\t\tw.WriteString(highlight.Token(t))\n\t}\n\treturn w.String()\n}\n"
  },
  {
    "path": "termd.go",
    "content": "// Package termd provides terminal markdown rendering,\n// with code block syntax highlighting support.\npackage termd\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kr/text\"\n\t\"github.com/mitchellh/go-wordwrap\"\n\t\"github.com/russross/blackfriday/v2\"\n)\n\n// Compiler is the markdown to text compiler. The zero value can be used.\ntype Compiler struct {\n\t// Columns is the number of columns to wrap text, defaulting to 90.\n\tColumns int\n\n\t// Markdown is an optional instance of a blackfriday markdown parser,\n\t// defaulting to one with CommonExtensions enabled.\n\tMarkdown *blackfriday.Markdown\n\n\t// SyntaxHighlighter is an optional syntax highlighter for code blocks,\n\t// using the low-level SyntaxHighlighter interface, or SyntaxTheme map.\n\tSyntaxHighlighter\n\n\tinBlockQuote bool\n\tinList       bool\n}\n\n// Compile returns a terminal-styled plain text representation of a markdown string.\nfunc (c *Compiler) Compile(s string) string {\n\tif c.Markdown == nil {\n\t\tc.Markdown = blackfriday.New(blackfriday.WithExtensions(blackfriday.CommonExtensions))\n\t}\n\n\tif c.Columns == 0 {\n\t\tc.Columns = 90\n\t}\n\n\tif c.SyntaxHighlighter == nil {\n\t\tc.SyntaxHighlighter = SyntaxTheme{}\n\t}\n\n\tn := c.Markdown.Parse([]byte(s))\n\treturn c.visit(n)\n}\n\n// visit returns a compiled node string.\nfunc (c *Compiler) visit(n *blackfriday.Node) (s string) {\n\tswitch n.Type {\n\tcase blackfriday.Document:\n\t\ts = c.visit(n.FirstChild)\n\tcase blackfriday.BlockQuote:\n\t\tprev := c.inBlockQuote\n\t\tc.inBlockQuote = true\n\t\ts = c.visit(n.FirstChild)\n\t\ts = fmt.Sprintf(\"\\033[38;5;102m%s\\033[m\", s)\n\t\tc.inBlockQuote = prev\n\tcase blackfriday.List:\n\t\tprev := c.inList\n\t\tc.inList = true\n\t\ts = fmt.Sprintf(\"%s\\n\", c.visit(n.FirstChild))\n\t\tc.inList = prev\n\tcase blackfriday.Item:\n\t\ts = fmt.Sprintf(\"  - %s\", c.visit(n.FirstChild))\n\tcase blackfriday.Paragraph:\n\t\ts = c.visit(n.FirstChild)\n\t\tif c.inList {\n\t\t\ts += \"\\n\"\n\t\t} else {\n\t\t\ts = wordwrap.WrapString(s, uint(c.Columns))\n\t\t\ts = text.Indent(s, \"  \")\n\t\t\ts += \"\\n\\n\"\n\t\t}\n\tcase blackfriday.Heading:\n\t\th := strings.Repeat(\"#\", n.HeadingData.Level)\n\t\tt := c.visit(n.FirstChild)\n\t\ts = fmt.Sprintf(\"\\033[1m%s %s\\033[m\\n\\n\", h, t)\n\tcase blackfriday.HorizontalRule:\n\t\ts = fmt.Sprintf(\"  %s\\n\\n\", strings.Repeat(\"─\", c.Columns))\n\tcase blackfriday.Emph:\n\t\ts = c.visit(n.FirstChild)\n\t\tif !c.inBlockQuote {\n\t\t\ts = fmt.Sprintf(\"\\033[3m%s\\033[m\", s)\n\t\t}\n\tcase blackfriday.Strong:\n\t\ts = c.visit(n.FirstChild)\n\t\tif !c.inBlockQuote {\n\t\t\ts = fmt.Sprintf(\"\\033[1m%s\\033[m\", s)\n\t\t}\n\tcase blackfriday.Link:\n\t\ts = c.visit(n.FirstChild)\n\t\td := string(n.LinkData.Destination)\n\t\tif s != d {\n\t\t\ts = fmt.Sprintf(\"%s (%s)\", s, d)\n\t\t}\n\tcase blackfriday.Image:\n\t\ts = string(n.LinkData.Destination)\n\tcase blackfriday.Text:\n\t\ts = string(n.Literal)\n\tcase blackfriday.CodeBlock:\n\t\tlang := string(n.CodeBlockData.Info)\n\t\ts = string(n.Literal)\n\t\ts = highlight(s, lang, c.SyntaxHighlighter)\n\t\ts = fmt.Sprintf(\"%s\\n\", text.Indent(s, \"    \"))\n\tcase blackfriday.Code:\n\t\ts = fmt.Sprintf(\"\\033[38;5;102m`%s`\\033[m\", string(n.Literal))\n\tcase blackfriday.HTMLSpan:\n\t\t// ignore\n\tdefault:\n\t\ts = fmt.Sprintf(\"<unhandled: %v>\", n)\n\t}\n\n\tif n.Next != nil {\n\t\ts += c.visit(n.Next)\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "termd_test.go",
    "content": "package termd_test\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\n\t\"github.com/tj/go-termd\"\n)\n\nfunc Example() {\n\tvar c termd.Compiler\n\n\tc.SyntaxHighlighter = termd.SyntaxTheme{\n\t\t\"keyword\": termd.Style{},\n\t\t\"comment\": termd.Style{\n\t\t\tColor: \"#323232\",\n\t\t},\n\t\t\"literal\": termd.Style{\n\t\t\tColor: \"#555555\",\n\t\t},\n\t\t\"name\": termd.Style{\n\t\t\tColor: \"#777777\",\n\t\t},\n\t\t\"name.function\": termd.Style{\n\t\t\tColor: \"#444444\",\n\t\t},\n\t\t\"literal.string\": termd.Style{\n\t\t\tColor: \"#333333\",\n\t\t},\n\t}\n\n\tb, _ := ioutil.ReadFile(\"testdata/example.md\")\n\tfmt.Printf(\"%s\\n\", c.Compile(string(b)))\n\t// Output:\n}\n"
  },
  {
    "path": "testdata/example.md",
    "content": "# Heading\n\nLorem ipsum _dolor_ sit amet, consectetur **adipiscing** elit. Curabitur `convallis` aliquet diam ut pharetra. In et sapien dui. Pellentesque nec magna non diam volutpat commodo. Nam non lorem vitae justo facilisis varius eu at lorem.\n\n## Sub Heading\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur convallis aliquet diam ut pharetra. In et sapien dui. Pellentesque nec magna non diam volutpat commodo. Nam non lorem vitae justo facilisis varius eu at lorem. Maecenas auctor, velit consectetur commodo pellentesque, nunc augue tristique justo, ac ullamcorper mi nisi a arcu. Cras lobortis lacus sed erat pellentesque malesuada. \n\nInteger iaculis commodo libero quis scelerisque. Integer tortor tortor, aliquet eu quam sed, viverra pellentesque augue. Sed suscipit nulla magna, at ultrices odio fermentum a. Praesent sodales nulla massa, eu imperdiet magna.\n\nOdio fermentum a. Praesent sodales nulla massa, eu imperdiet magna. Integer iaculis commodo libero quis scelerisque. Integer tortor tortor, aliquet eu quam sed, viverra pellentesque augue. Sed suscipit nulla magna, at ultrices odio fermentum a. Praesent sodales nulla massa, eu imperdiet magna.\n\n--- \n\nInteger iaculis commodo libero quis scelerisque. Integer tortor tortor, aliquet eu quam sed, viverra pellentesque augue. Sed suscipit nulla magna, at ultrices odio fermentum a. Praesent sodales nulla massa, eu imperdiet magna.\n\n## Code Example\n\nHere's an example code block:\n\n```js\nconst foo = /something/\n\n/**\n * isJSON returns true if the string looks like JSON.\n */\n\nfunction isJSON(s = '') {\n  s = s.trim()\n  return s[0] == '{' && s[s.length - 1] == '}'\n}\n\nconsole.log('Testing some stuff')\n```\n"
  },
  {
    "path": "theme.go",
    "content": "package termd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/alecthomas/chroma\"\n\t\"github.com/aybabtme/rgbterm\"\n\t\"github.com/tj/go-css/csshex\"\n)\n\n// Style is the configuration used to style a particular token.\ntype Style struct {\n\tColor      string `json:\"color\"`\n\tBackground string `json:\"background\"`\n\tBold       bool   `json:\"bold\"`\n\tFaint      bool   `json:\"faint\"`\n\tItalic     bool   `json:\"italic\"`\n\tUnderline  bool   `json:\"underline\"`\n}\n\n// apply returns a string with the style applied.\nfunc (s Style) apply(v string) string {\n\tif s.Bold {\n\t\tv = escape(1, v)\n\t}\n\n\tif s.Faint {\n\t\tv = escape(2, v)\n\t}\n\n\tif s.Italic {\n\t\tv = escape(3, v)\n\t}\n\n\tif s.Underline {\n\t\tv = escape(4, v)\n\t}\n\n\tif s.Color != \"\" {\n\t\tv = foreground(v, s.Color)\n\t}\n\n\tif s.Background != \"\" {\n\t\tv = background(v, s.Background)\n\t}\n\n\treturn v\n}\n\n// SyntaxTheme is a map of token names to style configurations.\ntype SyntaxTheme map[string]Style\n\n// Token implementation.\nfunc (c SyntaxTheme) Token(t chroma.Token) string {\n\t// specific\n\tif s, ok := c.mapped(t.Type, t.Value); ok {\n\t\treturn s\n\t}\n\n\t// sub-category\n\tif s, ok := c.mapped(t.Type.SubCategory(), t.Value); ok {\n\t\treturn s\n\t}\n\n\t// category\n\tif s, ok := c.mapped(t.Type.Category(), t.Value); ok {\n\t\treturn s\n\t}\n\n\treturn t.Value\n}\n\n// mapped returns a string mapped to its style, or returns the input string as-is.\nfunc (c SyntaxTheme) mapped(t chroma.TokenType, v string) (string, bool) {\n\t// check if the key is valid\n\tk, ok := themeKeys[t]\n\tif !ok {\n\t\treturn v, false\n\t}\n\n\t// check if the style is mapped\n\ts, ok := c[k]\n\tif !ok {\n\t\treturn v, false\n\t}\n\n\treturn s.apply(v), true\n}\n\n// escape returns an ansi escape sequence with the given code.\nfunc escape(code int, s string) string {\n\treturn fmt.Sprintf(\"\\033[%dm%s\\033[m\", code, s)\n}\n\n// foreground color.\nfunc foreground(s, color string) string {\n\tr, g, b, ok := csshex.Parse(color)\n\tif !ok {\n\t\treturn s\n\t}\n\treturn rgbterm.FgString(s, r, g, b)\n}\n\n// background color.\nfunc background(s, color string) string {\n\tr, g, b, ok := csshex.Parse(color)\n\tif !ok {\n\t\treturn s\n\t}\n\treturn rgbterm.BgString(s, r, g, b)\n}\n\n// themeKeys is the map of token types to names.\nvar themeKeys = map[chroma.TokenType]string{\n\tchroma.Keyword:                  \"keyword\",\n\tchroma.KeywordConstant:          \"keyword.constant\",\n\tchroma.KeywordDeclaration:       \"keyword.declaration\",\n\tchroma.KeywordNamespace:         \"keyword.namespace\",\n\tchroma.KeywordPseudo:            \"keyword.pseudo\",\n\tchroma.KeywordReserved:          \"keyword.reserved\",\n\tchroma.KeywordType:              \"keyword.type\",\n\tchroma.Name:                     \"name\",\n\tchroma.NameAttribute:            \"name.attribute\",\n\tchroma.NameBuiltin:              \"name.builtin\",\n\tchroma.NameBuiltinPseudo:        \"name.builtin.pseudo\",\n\tchroma.NameClass:                \"name.class\",\n\tchroma.NameConstant:             \"name.constant\",\n\tchroma.NameDecorator:            \"name.decorator\",\n\tchroma.NameEntity:               \"name.entity\",\n\tchroma.NameException:            \"name.exception\",\n\tchroma.NameFunction:             \"name.function\",\n\tchroma.NameFunctionMagic:        \"name.function.magic\",\n\tchroma.NameKeyword:              \"name.keyword\",\n\tchroma.NameLabel:                \"name.label\",\n\tchroma.NameNamespace:            \"name.namespace\",\n\tchroma.NameOperator:             \"name.operator\",\n\tchroma.NameOther:                \"name.other\",\n\tchroma.NamePseudo:               \"name.pseudo\",\n\tchroma.NameProperty:             \"name.property\",\n\tchroma.NameTag:                  \"name.tag\",\n\tchroma.NameVariable:             \"name.variable\",\n\tchroma.NameVariableAnonymous:    \"name.variable.anonymous\",\n\tchroma.NameVariableClass:        \"name.variable.class\",\n\tchroma.NameVariableGlobal:       \"name.variable.global\",\n\tchroma.NameVariableInstance:     \"name.variable.instance\",\n\tchroma.NameVariableMagic:        \"name.variable.magic\",\n\tchroma.Literal:                  \"literal\",\n\tchroma.LiteralDate:              \"literal.date\",\n\tchroma.LiteralOther:             \"literal.other\",\n\tchroma.LiteralString:            \"literal.string\",\n\tchroma.LiteralStringAffix:       \"literal.string.affix\",\n\tchroma.LiteralStringAtom:        \"literal.string.atom\",\n\tchroma.LiteralStringBacktick:    \"literal.string.backtick\",\n\tchroma.LiteralStringBoolean:     \"literal.string.boolean\",\n\tchroma.LiteralStringChar:        \"literal.string.char\",\n\tchroma.LiteralStringDelimiter:   \"literal.string.delimiter\",\n\tchroma.LiteralStringDoc:         \"literal.string.doc\",\n\tchroma.LiteralStringDouble:      \"literal.string.double\",\n\tchroma.LiteralStringEscape:      \"literal.string.escape\",\n\tchroma.LiteralStringHeredoc:     \"literal.string.heredoc\",\n\tchroma.LiteralStringInterpol:    \"literal.string.interpol\",\n\tchroma.LiteralStringName:        \"literal.string.name\",\n\tchroma.LiteralStringOther:       \"literal.string.other\",\n\tchroma.LiteralStringRegex:       \"literal.string.regex\",\n\tchroma.LiteralStringSingle:      \"literal.string.single\",\n\tchroma.LiteralStringSymbol:      \"literal.string.symbol\",\n\tchroma.LiteralNumber:            \"literal.number\",\n\tchroma.LiteralNumberBin:         \"literal.number.bin\",\n\tchroma.LiteralNumberFloat:       \"literal.number.float\",\n\tchroma.LiteralNumberHex:         \"literal.number.hex\",\n\tchroma.LiteralNumberInteger:     \"literal.number.integer\",\n\tchroma.LiteralNumberIntegerLong: \"literal.number.integer.long\",\n\tchroma.LiteralNumberOct:         \"literal.number.oct\",\n\tchroma.Operator:                 \"operator\",\n\tchroma.OperatorWord:             \"operator.word\",\n\tchroma.Punctuation:              \"punctuation\",\n\tchroma.Comment:                  \"comment\",\n\tchroma.CommentHashbang:          \"comment.hashbang\",\n\tchroma.CommentMultiline:         \"comment.multiline\",\n\tchroma.CommentSingle:            \"comment.single\",\n\tchroma.CommentSpecial:           \"comment.special\",\n\tchroma.CommentPreproc:           \"comment.preproc\",\n\tchroma.CommentPreprocFile:       \"comment.preproc.file\",\n\tchroma.Generic:                  \"generic\",\n\tchroma.GenericDeleted:           \"generic.deleted\",\n\tchroma.GenericEmph:              \"generic.emph\",\n\tchroma.GenericError:             \"generic.error\",\n\tchroma.GenericHeading:           \"generic.heading\",\n\tchroma.GenericInserted:          \"generic.inserted\",\n\tchroma.GenericOutput:            \"generic.output\",\n\tchroma.GenericPrompt:            \"generic.prompt\",\n\tchroma.GenericStrong:            \"generic.strong\",\n\tchroma.GenericSubheading:        \"generic.subheading\",\n\tchroma.GenericTraceback:         \"generic.traceback\",\n\tchroma.GenericUnderline:         \"generic.underline\",\n\tchroma.Text:                     \"text\",\n\tchroma.TextWhitespace:           \"text.whitespace\",\n\tchroma.TextSymbol:               \"text.symbol\",\n\tchroma.TextPunctuation:          \"text.punctuation\",\n}\n"
  }
]