├── .github ├── FUNDING.yml └── dependabot.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── Readme.md ├── colors.go ├── debug ├── html.go ├── markdown.go └── markdown_set.go ├── fuzzing ├── .gitignore ├── Makefile └── fuzz.go ├── go.mod ├── go.sum ├── html └── html.go ├── markdown.go ├── markdown_test.go ├── misc ├── example.go ├── result.png └── table.png ├── numbering.go ├── numbering_test.go ├── options.go ├── renderer.go ├── renderer_test.go ├── shades.go ├── tables.go ├── tables_test.go ├── testdata_media ├── termui_recording.gif ├── webui1.png └── webui2.png ├── testdata_result ├── Amps and angle encoding.txt ├── Auto links.txt ├── Backslash escapes.txt ├── Blockquotes with code blocks.txt ├── Code Blocks.txt ├── Code Spans.txt ├── Hard-wrapped paragraphs with list-like lines no empty line before block.txt ├── Hard-wrapped paragraphs with list-like lines.txt ├── Horizontal rules.txt ├── Inline HTML (Advanced).txt ├── Inline HTML (Simple).txt ├── Inline HTML comments.txt ├── Links, inline style.text.txt ├── Links, inline style.txt ├── Links, reference style.text.txt ├── Links, reference style.txt ├── Links, shortcut references.txt ├── Literal quotes in titles.txt ├── Markdown Documentation - Basics.txt ├── Markdown Documentation - Syntax.txt ├── Nested blockquotes.txt ├── Ordered and unordered lists.txt ├── README.txt ├── Table.txt ├── Tabs.txt ├── Tidyness.txt ├── break.txt ├── code.txt ├── codeblock.txt ├── emoji.txt ├── emphasis.txt ├── headings.txt ├── horizontal_rule.txt ├── image.txt ├── list.txt └── quote.txt └── testdata_source ├── Amps and angle encoding.md ├── Auto links.md ├── Backslash escapes.md ├── Blockquotes with code blocks.md ├── Code Blocks.md ├── Code Spans.md ├── Hard-wrapped paragraphs with list-like lines no empty line before block.md ├── Hard-wrapped paragraphs with list-like lines.md ├── Horizontal rules.md ├── Inline HTML (Advanced).md ├── Inline HTML (Simple).md ├── Inline HTML comments.md ├── Links, inline style.md ├── Links, reference style.md ├── Links, shortcut references.md ├── Literal quotes in titles.md ├── Markdown Documentation - Basics.md ├── Markdown Documentation - Syntax.md ├── Nested blockquotes.md ├── Ordered and unordered lists.md ├── README.md ├── Table.md ├── Tabs.md ├── Tidyness.md ├── break.md ├── code.md ├── codeblock.md ├── emoji.md ├── emphasis.md ├── headings.md ├── horizontal_rule.md ├── image.md ├── list.md └── quote.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | open_collective: git-bug 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.12.x 5 | - 1.13.x 6 | 7 | env: 8 | - GO111MODULE=on 9 | 10 | script: 11 | - go build 12 | - go test -v -bench=. -race -coverprofile=coverage.txt -covermode=atomic ./... 13 | 14 | after_success: 15 | - bash <(curl -s https://codecov.io/bash) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Michael Muré 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # go-term-markdown 2 | 3 | [![Build Status](https://travis-ci.com/MichaelMure/go-term-markdown.svg?branch=master)](https://travis-ci.com/MichaelMure/go-term-markdown) 4 | [![GoDoc](https://godoc.org/github.com/MichaelMure/go-term-markdown?status.svg)](https://godoc.org/github.com/MichaelMure/go-term-markdown) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/MichaelMure/go-term-markdown)](https://goreportcard.com/report/github.com/MichaelMure/go-term-markdown) 6 | [![codecov](https://codecov.io/gh/MichaelMure/go-term-markdown/branch/master/graph/badge.svg)](https://codecov.io/gh/MichaelMure/go-term-markdown) 7 | [![GitHub license](https://img.shields.io/github/license/MichaelMure/go-term-markdown.svg)](https://github.com/MichaelMure/go-term-markdown/blob/master/LICENSE) 8 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/the-git-bug/Lobby) 9 | 10 | `go-term-markdown` is a go package implementing a Markdown renderer for the terminal. 11 | 12 | Note: Markdown being originally designed to render as HTML, rendering in a terminal is occasionally challenging and some adaptation had to be made. 13 | 14 | Features: 15 | - formatting 16 | - lists 17 | - tables 18 | - images 19 | - code blocks with syntax highlighting 20 | - basic HTML support 21 | 22 | Note: this renderer is packaged as a standalone terminal viewer at https://github.com/MichaelMure/mdr/ 23 | 24 | ## Usage 25 | 26 | ```go 27 | import ( 28 | "fmt" 29 | "io/ioutil" 30 | 31 | markdown "github.com/MichaelMure/go-term-markdown" 32 | ) 33 | 34 | func main() { 35 | path := "Readme.md" 36 | source, err := ioutil.ReadFile(path) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | result := markdown.Render(string(source), 80, 6) 42 | 43 | fmt.Println(result) 44 | } 45 | ``` 46 | 47 | ## Example 48 | 49 | Here is the [Readme](https://github.com/MichaelMure/go-term-text/blob/v0.2.4/Readme.md) of `go-term-text` rendered with `go-term-markdown`: 50 | 51 | ![rendering example](misc/result.png) 52 | 53 | Here is an example of table rendering: 54 | 55 | ![table rendering](misc/table.png) 56 | 57 | ## Origin 58 | 59 | This package has been extracted from the [git-bug](https://github.com/MichaelMure/git-bug) project. As such, its aim is to support this project and not to provide an all-in-one solution. Contributions or full-on takeover as welcome though. 60 | 61 | ## Contribute 62 | 63 | PRs accepted. 64 | 65 | ## License 66 | 67 | MIT 68 | -------------------------------------------------------------------------------- /colors.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import "github.com/fatih/color" 4 | 5 | var ( 6 | // we need a bunch of escape code for manual formatting 7 | boldOn = "\x1b[1m" 8 | // boldOff = "\x1b[21m" --> use resetAll + snapshot with bold off instead 9 | italicOn = "\x1b[3m" 10 | italicOff = "\x1b[23m" 11 | crossedOutOn = "\x1b[9m" 12 | crossedOutOff = "\x1b[29m" 13 | greenOn = "\x1b[32m" 14 | 15 | resetAll = "\x1b[0m" 16 | colorOff = "\x1b[39m" 17 | 18 | Green = color.New(color.FgGreen).SprintFunc() 19 | HiGreen = color.New(color.FgHiGreen).SprintFunc() 20 | GreenBold = color.New(color.FgGreen, color.Bold).SprintFunc() 21 | Blue = color.New(color.FgBlue).SprintFunc() 22 | BlueBgItalic = color.New(color.BgBlue, color.Italic).SprintFunc() 23 | Red = color.New(color.FgRed).SprintFunc() 24 | ) 25 | -------------------------------------------------------------------------------- /debug/html.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "golang.org/x/net/html" 8 | 9 | htmlWalker "github.com/MichaelMure/go-term-markdown/html" 10 | ) 11 | 12 | func HtmlAst2PlantUML(node *html.Node) { 13 | f, err := os.Create("/tmp/html_ast.puml") 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | _, _ = fmt.Fprintln(f, "@startuml") 19 | _, _ = fmt.Fprintln(f, "(*) -> Blah") 20 | 21 | htmlWalker.WalkFunc(node, func(node *html.Node, entering bool) htmlWalker.WalkStatus { 22 | type2str := func(nodeType html.NodeType) string { 23 | return [...]string{"ErrorNode", "TextNode", "DocumentNode", "ElementNode", "CommentNode", "DoctypeNode", "scopeMarkerNode"}[nodeType] 24 | } 25 | if entering { 26 | for child := node.FirstChild; child != nil; child = child.NextSibling { 27 | t := type2str(node.Type) 28 | if node.Type == html.ElementNode { 29 | t = node.Data 30 | } 31 | tc := type2str(child.Type) 32 | if child.Type == html.ElementNode { 33 | tc = child.Data 34 | } 35 | 36 | _, _ = fmt.Fprintf(f, "\"%s-%p\" --> \"%s-%p\"\n", t, node, tc, child) 37 | } 38 | } 39 | return htmlWalker.GoToNext 40 | }) 41 | _, _ = fmt.Fprintln(f, "@enduml") 42 | } 43 | -------------------------------------------------------------------------------- /debug/markdown.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | md "github.com/gomarkdown/markdown" 10 | "github.com/gomarkdown/markdown/ast" 11 | ) 12 | 13 | func MarkdownAst2PlantUML(node ast.Node) { 14 | f, err := os.Create("/tmp/markdown_ast.puml") 15 | if err != nil { 16 | panic(err) 17 | } 18 | 19 | r := &astRenderer{ 20 | f: f, 21 | } 22 | 23 | md.Render(node, r) 24 | } 25 | 26 | var _ md.Renderer = &astRenderer{} 27 | 28 | type astRenderer struct { 29 | f *os.File 30 | } 31 | 32 | func (a *astRenderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.WalkStatus { 33 | if entering { 34 | for _, child := range node.GetChildren() { 35 | str := fmt.Sprintf("%T --> %T\n", node, child) 36 | _, _ = fmt.Fprintf(a.f, strings.Replace(str, "*ast.", "", -1)) 37 | } 38 | } 39 | 40 | return ast.GoToNext 41 | } 42 | 43 | func (a *astRenderer) RenderHeader(w io.Writer, ast ast.Node) { 44 | _, _ = fmt.Fprintln(a.f, "@startuml") 45 | _, _ = fmt.Fprintln(a.f, "(*) --> Document") 46 | } 47 | 48 | func (a *astRenderer) RenderFooter(w io.Writer, ast ast.Node) { 49 | _, _ = fmt.Fprintln(a.f, "@enduml") 50 | _ = a.f.Close() 51 | } 52 | -------------------------------------------------------------------------------- /debug/markdown_set.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | md "github.com/gomarkdown/markdown" 10 | "github.com/gomarkdown/markdown/ast" 11 | ) 12 | 13 | func MarkdownAstSet2PlantUML(node ast.Node) { 14 | f, err := os.Create("/tmp/markdown_ast_set.puml") 15 | if err != nil { 16 | panic(err) 17 | } 18 | 19 | defer f.Close() 20 | 21 | r := &astSetRenderer{ 22 | f: f, 23 | set: make(map[string]struct{}), 24 | } 25 | 26 | _, _ = fmt.Fprintln(f, "@startuml") 27 | _, _ = fmt.Fprintln(f, "(*) --> Document") 28 | 29 | md.Render(node, r) 30 | 31 | _, _ = fmt.Fprintln(f, "@enduml") 32 | } 33 | 34 | var _ md.Renderer = &astSetRenderer{} 35 | 36 | type astSetRenderer struct { 37 | set map[string]struct{} 38 | f *os.File 39 | } 40 | 41 | func (a *astSetRenderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.WalkStatus { 42 | if entering { 43 | for _, child := range node.GetChildren() { 44 | str := fmt.Sprintf("%T --> %T\n", node, child) 45 | if _, has := a.set[str]; !has { 46 | a.set[str] = struct{}{} 47 | _, _ = fmt.Fprintf(a.f, strings.Replace(str, "*ast.", "", -1)) 48 | } 49 | } 50 | } 51 | 52 | return ast.GoToNext 53 | } 54 | 55 | func (a *astSetRenderer) RenderHeader(w io.Writer, ast ast.Node) {} 56 | 57 | func (a *astSetRenderer) RenderFooter(w io.Writer, ast ast.Node) {} 58 | -------------------------------------------------------------------------------- /fuzzing/.gitignore: -------------------------------------------------------------------------------- 1 | corpus/ 2 | crashers/ 3 | suppressions/ 4 | fuzzing-fuzz.zip 5 | -------------------------------------------------------------------------------- /fuzzing/Makefile: -------------------------------------------------------------------------------- 1 | fuzz: 2 | mkdir -p corpus 3 | cp ../testdata_source/* corpus/ 4 | go run github.com/dvyukov/go-fuzz/go-fuzz-build 5 | go run github.com/dvyukov/go-fuzz/go-fuzz -------------------------------------------------------------------------------- /fuzzing/fuzz.go: -------------------------------------------------------------------------------- 1 | package fuzzing 2 | 3 | import markdown "github.com/MichaelMure/go-term-markdown" 4 | 5 | func Fuzz(data []byte) int { 6 | markdown.Render(string(data), 50, 4) 7 | return 1 8 | } 9 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/MichaelMure/go-term-markdown 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/MichaelMure/go-term-text v0.3.1 7 | github.com/alecthomas/chroma v0.10.0 8 | github.com/eliukblau/pixterm v1.3.1 9 | github.com/fatih/color v1.13.0 10 | github.com/gomarkdown/markdown v0.0.0-20220310201231-552c6011c0b8 11 | github.com/kyokomi/emoji/v2 v2.2.9 12 | github.com/stretchr/testify v1.7.1 13 | golang.org/x/net v0.0.0-20220401154927-543a649e0bdd 14 | ) 15 | 16 | require ( 17 | github.com/davecgh/go-spew v1.1.1 // indirect 18 | github.com/disintegration/imaging v1.6.2 // indirect 19 | github.com/dlclark/regexp2 v1.4.0 // indirect 20 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 21 | github.com/mattn/go-colorable v0.1.12 // indirect 22 | github.com/mattn/go-isatty v0.0.14 // indirect 23 | github.com/mattn/go-runewidth v0.0.13 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | github.com/rivo/uniseg v0.2.0 // indirect 26 | golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect 27 | golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect 28 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/MichaelMure/go-term-text v0.3.1 h1:Kw9kZanyZWiCHOYu9v/8pWEgDQ6UVN9/ix2Vd2zzWf0= 2 | github.com/MichaelMure/go-term-text v0.3.1/go.mod h1:QgVjAEDUnRMlzpS6ky5CGblux7ebeiLnuy9dAaFZu8o= 3 | github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= 4 | github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 9 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 10 | github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= 11 | github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 12 | github.com/eliukblau/pixterm v1.3.1 h1:XeouQViH+lmzCa7sMUoK2cd7qlgHYGLIjwRKaOdJbKA= 13 | github.com/eliukblau/pixterm v1.3.1/go.mod h1:on5ueknFt+ZFVvIVVzQ7/JXwPjv5fJd8Q1Ybh7XixfU= 14 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 15 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 16 | github.com/gomarkdown/markdown v0.0.0-20220310201231-552c6011c0b8 h1:YVvt637ygnOO9qjLBVmPOvrUmCz/i8YECSu/8UlOQW0= 17 | github.com/gomarkdown/markdown v0.0.0-20220310201231-552c6011c0b8/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= 18 | github.com/kyokomi/emoji/v2 v2.2.9 h1:UWYkjplPZ4rMPvLxc+/e12/xTqoRcn55oUySkpZ554g= 19 | github.com/kyokomi/emoji/v2 v2.2.9/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= 20 | github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 21 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 22 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 23 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 24 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 25 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 26 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 27 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 28 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 29 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 30 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 31 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 32 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 33 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 34 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 35 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 36 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 37 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 38 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 39 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 40 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 41 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 42 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 43 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 44 | golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 45 | golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg= 46 | golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 47 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 48 | golang.org/x/net v0.0.0-20220401154927-543a649e0bdd h1:zYlwaUHTmxuf6H7hwO2dgwqozQmH7zf4x+/qql4oVWc= 49 | golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 50 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 51 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 52 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 53 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 54 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 55 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 56 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 57 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 58 | golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY= 59 | golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 60 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 61 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 62 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 63 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 64 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 65 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 66 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 67 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 68 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 69 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 70 | -------------------------------------------------------------------------------- /html/html.go: -------------------------------------------------------------------------------- 1 | package html 2 | 3 | import . "golang.org/x/net/html" 4 | 5 | // WalkStatus allows NodeVisitor to have some control over the tree traversal. 6 | // It is returned from NodeVisitor and different values allow Node.Walk to 7 | // decide which node to go to next. 8 | type WalkStatus int 9 | 10 | const ( 11 | // GoToNext is the default traversal of every node. 12 | GoToNext WalkStatus = iota 13 | // SkipChildren tells walker to skip all children of current node. 14 | SkipChildren 15 | // Terminate tells walker to terminate the traversal. 16 | Terminate 17 | ) 18 | 19 | // NodeVisitor is a callback to be called when traversing the syntax tree. 20 | // Called twice for every node: once with entering=true when the branch is 21 | // first visited, then with entering=false after all the children are done. 22 | type NodeVisitor interface { 23 | Visit(node *Node, entering bool) WalkStatus 24 | } 25 | 26 | func Walk(n *Node, visitor NodeVisitor) WalkStatus { 27 | isContainer := n.FirstChild != nil 28 | 29 | // some special case that are container but can be self closing 30 | if n.Type == ElementNode { 31 | switch n.Data { 32 | case "td": 33 | isContainer = true 34 | } 35 | } 36 | 37 | status := visitor.Visit(n, true) 38 | 39 | if status == Terminate { 40 | // even if terminating, close container node 41 | if isContainer { 42 | visitor.Visit(n, false) 43 | } 44 | } 45 | 46 | if isContainer && status != SkipChildren { 47 | child := n.FirstChild 48 | for child != nil { 49 | status = Walk(child, visitor) 50 | if status == Terminate { 51 | return status 52 | } 53 | child = child.NextSibling 54 | } 55 | } 56 | 57 | if isContainer { 58 | status = visitor.Visit(n, false) 59 | if status == Terminate { 60 | return status 61 | } 62 | } 63 | 64 | return GoToNext 65 | } 66 | 67 | // NodeVisitorFunc casts a function to match NodeVisitor interface 68 | type NodeVisitorFunc func(node *Node, entering bool) WalkStatus 69 | 70 | // Visit calls visitor function 71 | func (f NodeVisitorFunc) Visit(node *Node, entering bool) WalkStatus { 72 | return f(node, entering) 73 | } 74 | 75 | // WalkFunc is like Walk but accepts just a callback function 76 | func WalkFunc(n *Node, f NodeVisitorFunc) { 77 | Walk(n, f) 78 | } 79 | -------------------------------------------------------------------------------- /markdown.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | md "github.com/gomarkdown/markdown" 5 | "github.com/gomarkdown/markdown/parser" 6 | ) 7 | 8 | // Extensions returns the bitmask of extensions supported by this renderer. 9 | // The output of this function can be used to instantiate a new markdown 10 | // parser using the `NewWithExtensions` function. 11 | func Extensions() parser.Extensions { 12 | extensions := parser.NoIntraEmphasis // Ignore emphasis markers inside words 13 | extensions |= parser.Tables // Parse tables 14 | extensions |= parser.FencedCode // Parse fenced code blocks 15 | extensions |= parser.Autolink // Detect embedded URLs that are not explicitly marked 16 | extensions |= parser.Strikethrough // Strikethrough text using ~~test~~ 17 | extensions |= parser.SpaceHeadings // Be strict about prefix heading rules 18 | extensions |= parser.HeadingIDs // specify heading IDs with {#id} 19 | extensions |= parser.BackslashLineBreak // Translate trailing backslashes into line breaks 20 | extensions |= parser.DefinitionLists // Parse definition lists 21 | extensions |= parser.LaxHTMLBlocks // more in HTMLBlock, less in HTMLSpan 22 | extensions |= parser.NoEmptyLineBeforeBlock // no need for new line before a list 23 | 24 | return extensions 25 | } 26 | 27 | func Render(source string, lineWidth int, leftPad int, opts ...Options) []byte { 28 | p := parser.NewWithExtensions(Extensions()) 29 | nodes := md.Parse([]byte(source), p) 30 | renderer := NewRenderer(lineWidth, leftPad, opts...) 31 | 32 | return md.Render(nodes, renderer) 33 | } 34 | -------------------------------------------------------------------------------- /markdown_test.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/fatih/color" 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestRender(t *testing.T) { 17 | color.NoColor = false 18 | 19 | sourcepath := "testdata_source/" 20 | resultpath := "testdata_result/" 21 | 22 | err := filepath.Walk(sourcepath, func(fullpath string, info os.FileInfo, err error) error { 23 | require.NoError(t, err) 24 | 25 | if info.IsDir() { 26 | return nil 27 | } 28 | 29 | _, file := filepath.Split(fullpath) 30 | name := strings.TrimRight(file, ".md") 31 | 32 | t.Run(name, func(t *testing.T) { 33 | source, err := ioutil.ReadFile(path.Join(sourcepath, name+".md")) 34 | require.NoError(t, err) 35 | 36 | expected, err := ioutil.ReadFile(path.Join(resultpath, name+".txt")) 37 | require.NoError(t, err) 38 | 39 | output := Render(string(source), 40, 4) 40 | 41 | assert.Equal(t, string(expected), string(output)) 42 | }) 43 | 44 | return nil 45 | }) 46 | 47 | require.NoError(t, err) 48 | } 49 | 50 | func Test__DoRender(t *testing.T) { 51 | // This is not a real test, it's here to create the output testdata. 52 | // uncomment to generate a new test case 53 | t.SkipNow() 54 | 55 | color.NoColor = false 56 | 57 | sourcepath := "testdata_source/" 58 | resultpath := "testdata_result/" 59 | 60 | err := filepath.Walk(sourcepath, func(fullpath string, info os.FileInfo, err error) error { 61 | require.NoError(t, err) 62 | 63 | if info.IsDir() { 64 | return nil 65 | } 66 | 67 | _, file := filepath.Split(fullpath) 68 | name := strings.TrimRight(file, ".md") 69 | 70 | // if name != "Ordered and unordered lists" { 71 | // return nil 72 | // } 73 | 74 | source, err := ioutil.ReadFile(path.Join(sourcepath, name+".md")) 75 | require.NoError(t, err) 76 | 77 | output := Render(string(source), 40, 4) 78 | 79 | err = ioutil.WriteFile(path.Join(resultpath, name+".txt"), output, 0666) 80 | require.NoError(t, err) 81 | 82 | return nil 83 | }) 84 | 85 | require.NoError(t, err) 86 | } 87 | -------------------------------------------------------------------------------- /misc/example.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | 7 | markdown "github.com/MichaelMure/go-term-markdown" 8 | ) 9 | 10 | func main() { 11 | path := "Readme.md" 12 | source, err := ioutil.ReadFile(path) 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | result := markdown.Render(string(source), 80, 6) 18 | 19 | fmt.Println(result) 20 | } 21 | -------------------------------------------------------------------------------- /misc/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/misc/result.png -------------------------------------------------------------------------------- /misc/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/misc/table.png -------------------------------------------------------------------------------- /numbering.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import "strconv" 4 | 5 | type headingNumbering struct { 6 | levels [6]int 7 | } 8 | 9 | // Observe register the event of a new level with the given depth and 10 | // adjust the numbering accordingly 11 | func (hn *headingNumbering) Observe(level int) { 12 | if level <= 0 { 13 | panic("level start at 1, ask blackfriday why") 14 | } 15 | if level > 6 { 16 | panic("Markdown is limited to 6 levels of heading") 17 | } 18 | 19 | hn.levels[level-1]++ 20 | for i := level; i < 6; i++ { 21 | hn.levels[i] = 0 22 | } 23 | } 24 | 25 | // Render render the current headings numbering. 26 | func (hn *headingNumbering) Render() string { 27 | slice := hn.levels[:] 28 | 29 | // pop the last zero levels 30 | for i := 5; i >= 0; i-- { 31 | if hn.levels[i] != 0 { 32 | break 33 | } 34 | slice = slice[:len(slice)-1] 35 | } 36 | 37 | var result string 38 | 39 | for i := range slice { 40 | if i > 0 { 41 | result += "." 42 | } 43 | result += strconv.Itoa(slice[i]) 44 | } 45 | 46 | return result 47 | } 48 | -------------------------------------------------------------------------------- /numbering_test.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_numbering(t *testing.T) { 10 | var n headingNumbering 11 | 12 | assert.Equal(t, "", n.Render()) 13 | n.Observe(1) 14 | assert.Equal(t, "1", n.Render()) 15 | n.Observe(1) 16 | assert.Equal(t, "2", n.Render()) 17 | n.Observe(1) 18 | assert.Equal(t, "3", n.Render()) 19 | n.Observe(2) 20 | assert.Equal(t, "3.1", n.Render()) 21 | n.Observe(2) 22 | assert.Equal(t, "3.2", n.Render()) 23 | n.Observe(2) 24 | assert.Equal(t, "3.3", n.Render()) 25 | n.Observe(4) 26 | assert.Equal(t, "3.3.0.1", n.Render()) 27 | n.Observe(4) 28 | assert.Equal(t, "3.3.0.2", n.Render()) 29 | n.Observe(3) 30 | assert.Equal(t, "3.3.1", n.Render()) 31 | n.Observe(3) 32 | assert.Equal(t, "3.3.2", n.Render()) 33 | n.Observe(1) 34 | assert.Equal(t, "4", n.Render()) 35 | } 36 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import "github.com/eliukblau/pixterm/pkg/ansimage" 4 | 5 | type Options func(r *renderer) 6 | 7 | // DitheringMode type is used for image scale dithering mode constants. 8 | type DitheringMode uint8 9 | 10 | const ( 11 | NoDithering = DitheringMode(iota) 12 | DitheringWithBlocks 13 | DitheringWithChars 14 | ) 15 | 16 | // Dithering mode for ansimage 17 | // Default is fine directly through a terminal 18 | // DitheringWithBlocks is recommended if a terminal UI library is used 19 | func WithImageDithering(mode DitheringMode) Options { 20 | return func(r *renderer) { 21 | r.imageDithering = ansimage.DitheringMode(mode) 22 | } 23 | } 24 | 25 | // Use a custom collection of ANSI colors for the headings 26 | func WithHeadingShades(shades []shadeFmt) Options { 27 | return func(r *renderer) { 28 | r.headingShade = shade(shades) 29 | } 30 | } 31 | 32 | // Use a custom collection of ANSI colors for the blockquotes 33 | func WithBlockquoteShades(shades []shadeFmt) Options { 34 | return func(r *renderer) { 35 | r.blockQuoteShade = shade(shades) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /renderer.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | stdcolor "image/color" 7 | "io" 8 | "math" 9 | "net/http" 10 | "os" 11 | "strings" 12 | "time" 13 | "unicode" 14 | 15 | "github.com/MichaelMure/go-term-text" 16 | "github.com/alecthomas/chroma" 17 | "github.com/alecthomas/chroma/formatters" 18 | "github.com/alecthomas/chroma/lexers" 19 | "github.com/alecthomas/chroma/styles" 20 | "github.com/eliukblau/pixterm/pkg/ansimage" 21 | "github.com/fatih/color" 22 | md "github.com/gomarkdown/markdown" 23 | "github.com/gomarkdown/markdown/ast" 24 | "github.com/kyokomi/emoji/v2" 25 | "golang.org/x/net/html" 26 | 27 | htmlWalker "github.com/MichaelMure/go-term-markdown/html" 28 | ) 29 | 30 | /* 31 | 32 | Here are the possible cases for the AST. You can render it using PlantUML. 33 | 34 | @startuml 35 | 36 | (*) --> Document 37 | BlockQuote --> BlockQuote 38 | BlockQuote --> CodeBlock 39 | BlockQuote --> List 40 | BlockQuote --> Paragraph 41 | Del --> Emph 42 | Del --> Strong 43 | Del --> Text 44 | Document --> BlockQuote 45 | Document --> CodeBlock 46 | Document --> Heading 47 | Document --> HorizontalRule 48 | Document --> HTMLBlock 49 | Document --> List 50 | Document --> Paragraph 51 | Document --> Table 52 | Emph --> Text 53 | Heading --> Code 54 | Heading --> Del 55 | Heading --> Emph 56 | Heading --> HTMLSpan 57 | Heading --> Image 58 | Heading --> Link 59 | Heading --> Strong 60 | Heading --> Text 61 | Image --> Text 62 | Link --> Image 63 | Link --> Text 64 | ListItem --> List 65 | ListItem --> Paragraph 66 | List --> ListItem 67 | Paragraph --> Code 68 | Paragraph --> Del 69 | Paragraph --> Emph 70 | Paragraph --> Hardbreak 71 | Paragraph --> HTMLSpan 72 | Paragraph --> Image 73 | Paragraph --> Link 74 | Paragraph --> Strong 75 | Paragraph --> Text 76 | Strong --> Emph 77 | Strong --> Text 78 | TableBody --> TableRow 79 | TableCell --> Code 80 | TableCell --> Del 81 | TableCell --> Emph 82 | TableCell --> HTMLSpan 83 | TableCell --> Image 84 | TableCell --> Link 85 | TableCell --> Strong 86 | TableCell --> Text 87 | TableHeader --> TableRow 88 | TableRow --> TableCell 89 | Table --> TableBody 90 | Table --> TableHeader 91 | 92 | @enduml 93 | 94 | */ 95 | 96 | var _ md.Renderer = &renderer{} 97 | 98 | type renderer struct { 99 | // maximum line width allowed 100 | lineWidth int 101 | // constant left padding to apply 102 | leftPad int 103 | 104 | // Dithering mode for ansimage 105 | // Default is fine directly through a terminal 106 | // DitheringWithBlocks is recommended if a terminal UI library is used 107 | imageDithering ansimage.DitheringMode 108 | 109 | // all the custom left paddings, without the fixed space from leftPad 110 | padAccumulator []string 111 | 112 | // one-shot indent for the first line of the inline content 113 | indent string 114 | 115 | // for Heading, Paragraph, HTMLBlock and TableCell, accumulate the content of 116 | // the child nodes (Link, Text, Image, formatting ...). The result 117 | // is then rendered appropriately when exiting the node. 118 | inlineAccumulator strings.Builder 119 | 120 | // record and render the heading numbering 121 | headingNumbering headingNumbering 122 | headingShade levelShadeFmt 123 | 124 | blockQuoteLevel int 125 | blockQuoteShade levelShadeFmt 126 | 127 | table *tableRenderer 128 | } 129 | 130 | /// NewRenderer creates a new instance of the console renderer 131 | func NewRenderer(lineWidth int, leftPad int, opts ...Options) *renderer { 132 | r := &renderer{ 133 | lineWidth: lineWidth, 134 | leftPad: leftPad, 135 | padAccumulator: make([]string, 0, 10), 136 | headingShade: shade(defaultHeadingShades), 137 | blockQuoteShade: shade(defaultQuoteShades), 138 | } 139 | for _, opt := range opts { 140 | opt(r) 141 | } 142 | return r 143 | } 144 | 145 | func (r *renderer) pad() string { 146 | return strings.Repeat(" ", r.leftPad) + strings.Join(r.padAccumulator, "") 147 | } 148 | 149 | func (r *renderer) addPad(pad string) { 150 | r.padAccumulator = append(r.padAccumulator, pad) 151 | } 152 | 153 | func (r *renderer) popPad() { 154 | r.padAccumulator = r.padAccumulator[:len(r.padAccumulator)-1] 155 | } 156 | 157 | func (r *renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.WalkStatus { 158 | // TODO: remove 159 | // if node.AsLeaf() != nil { 160 | // fmt.Printf("%T, %v (%s)\n", node, entering, string(node.AsLeaf().Literal)) 161 | // } else { 162 | // fmt.Printf("%T, %v\n", node, entering) 163 | // } 164 | 165 | switch node := node.(type) { 166 | case *ast.Document: 167 | // Nothing to do 168 | 169 | case *ast.BlockQuote: 170 | // set and remove a colored bar on the left 171 | if entering { 172 | r.blockQuoteLevel++ 173 | r.addPad(r.blockQuoteShade(r.blockQuoteLevel)("┃ ")) 174 | } else { 175 | r.blockQuoteLevel-- 176 | r.popPad() 177 | } 178 | 179 | case *ast.List: 180 | // extra new line at the end of a list *if* next is not a list 181 | if next := ast.GetNextNode(node); !entering && next != nil { 182 | _, parentIsListItem := node.GetParent().(*ast.ListItem) 183 | _, nextIsList := next.(*ast.List) 184 | if !nextIsList && !parentIsListItem { 185 | _, _ = fmt.Fprintln(w) 186 | } 187 | } 188 | 189 | case *ast.ListItem: 190 | // write the prefix, add a padding if needed, and let Paragraph handle the rest 191 | if entering { 192 | switch { 193 | // numbered list 194 | case node.ListFlags&ast.ListTypeOrdered != 0: 195 | itemNumber := 1 196 | siblings := node.GetParent().GetChildren() 197 | for _, sibling := range siblings { 198 | if sibling == node { 199 | break 200 | } 201 | itemNumber++ 202 | } 203 | prefix := fmt.Sprintf("%d. ", itemNumber) 204 | r.indent = r.pad() + Green(prefix) 205 | r.addPad(strings.Repeat(" ", text.Len(prefix))) 206 | 207 | // header of a definition 208 | case node.ListFlags&ast.ListTypeTerm != 0: 209 | r.inlineAccumulator.WriteString(greenOn) 210 | 211 | // content of a definition 212 | case node.ListFlags&ast.ListTypeDefinition != 0: 213 | r.addPad(" ") 214 | 215 | // no flags means it's the normal bullet point list 216 | default: 217 | r.indent = r.pad() + Green("• ") 218 | r.addPad(" ") 219 | } 220 | } else { 221 | switch { 222 | // numbered list 223 | case node.ListFlags&ast.ListTypeOrdered != 0: 224 | r.popPad() 225 | 226 | // header of a definition 227 | case node.ListFlags&ast.ListTypeTerm != 0: 228 | r.inlineAccumulator.WriteString(colorOff) 229 | 230 | // content of a definition 231 | case node.ListFlags&ast.ListTypeDefinition != 0: 232 | r.popPad() 233 | _, _ = fmt.Fprintln(w) 234 | 235 | // no flags means it's the normal bullet point list 236 | default: 237 | r.popPad() 238 | } 239 | } 240 | 241 | case *ast.Paragraph: 242 | // on exiting, collect and format the accumulated content 243 | if !entering { 244 | content := r.inlineAccumulator.String() 245 | r.inlineAccumulator.Reset() 246 | 247 | var out string 248 | if r.indent != "" { 249 | out, _ = text.WrapWithPadIndent(content, r.lineWidth, r.indent, r.pad()) 250 | r.indent = "" 251 | } else { 252 | out, _ = text.WrapWithPad(content, r.lineWidth, r.pad()) 253 | } 254 | _, _ = fmt.Fprint(w, out, "\n") 255 | 256 | // extra line break in some cases 257 | if next := ast.GetNextNode(node); next != nil { 258 | switch next.(type) { 259 | case *ast.Paragraph, *ast.Heading, *ast.HorizontalRule, 260 | *ast.CodeBlock, *ast.HTMLBlock: 261 | _, _ = fmt.Fprintln(w) 262 | } 263 | } 264 | } 265 | 266 | case *ast.Heading: 267 | if !entering { 268 | r.renderHeading(w, node.Level) 269 | } 270 | 271 | case *ast.HorizontalRule: 272 | r.renderHorizontalRule(w) 273 | 274 | case *ast.Emph: 275 | if entering { 276 | r.inlineAccumulator.WriteString(italicOn) 277 | } else { 278 | r.inlineAccumulator.WriteString(italicOff) 279 | } 280 | 281 | case *ast.Strong: 282 | if entering { 283 | r.inlineAccumulator.WriteString(boldOn) 284 | } else { 285 | // This is super silly but some terminals, instead of having 286 | // the ANSI code SGR 21 do "bold off" like the logic would guide, 287 | // do "double underline" instead. This is madness. 288 | 289 | // To resolve that problem, we take a snapshot of the escape state, 290 | // remove the bold, then output "reset all" + snapshot 291 | es := text.EscapeState{} 292 | es.Witness(r.inlineAccumulator.String()) 293 | es.Bold = false 294 | r.inlineAccumulator.WriteString(resetAll) 295 | r.inlineAccumulator.WriteString(es.FormatString()) 296 | } 297 | 298 | case *ast.Del: 299 | if entering { 300 | r.inlineAccumulator.WriteString(crossedOutOn) 301 | } else { 302 | r.inlineAccumulator.WriteString(crossedOutOff) 303 | } 304 | 305 | case *ast.Link: 306 | if entering { 307 | r.inlineAccumulator.WriteString("[") 308 | r.inlineAccumulator.WriteString(string(ast.GetFirstChild(node).AsLeaf().Literal)) 309 | r.inlineAccumulator.WriteString("](") 310 | r.inlineAccumulator.WriteString(Blue(string(node.Destination))) 311 | if len(node.Title) > 0 { 312 | r.inlineAccumulator.WriteString(" ") 313 | r.inlineAccumulator.WriteString(string(node.Title)) 314 | } 315 | r.inlineAccumulator.WriteString(")") 316 | return ast.SkipChildren 317 | } 318 | 319 | case *ast.Image: 320 | if entering { 321 | // the alt text/title is weirdly parsed and is actually 322 | // a child text of this node 323 | var title string 324 | if len(node.Children) == 1 { 325 | if t, ok := node.Children[0].(*ast.Text); ok { 326 | title = string(t.Literal) 327 | } 328 | } 329 | 330 | str, rendered := r.renderImage( 331 | string(node.Destination), title, 332 | r.lineWidth-r.leftPad, 333 | ) 334 | 335 | if rendered { 336 | r.inlineAccumulator.WriteString("\n") 337 | r.inlineAccumulator.WriteString(str) 338 | r.inlineAccumulator.WriteString("\n\n") 339 | } else { 340 | r.inlineAccumulator.WriteString(str) 341 | r.inlineAccumulator.WriteString("\n") 342 | } 343 | 344 | return ast.SkipChildren 345 | } 346 | 347 | case *ast.Text: 348 | if string(node.Literal) == "\n" { 349 | break 350 | } 351 | content := string(node.Literal) 352 | if shouldCleanText(node) { 353 | content = removeLineBreak(content) 354 | } 355 | // emoji support ! 356 | emojed := emoji.Sprint(content) 357 | r.inlineAccumulator.WriteString(emojed) 358 | 359 | case *ast.HTMLBlock: 360 | r.renderHTMLBlock(w, node) 361 | 362 | case *ast.CodeBlock: 363 | r.renderCodeBlock(w, node) 364 | 365 | case *ast.Softbreak: 366 | // not actually implemented in gomarkdown 367 | r.inlineAccumulator.WriteString("\n") 368 | 369 | case *ast.Hardbreak: 370 | r.inlineAccumulator.WriteString("\n") 371 | 372 | case *ast.Code: 373 | r.inlineAccumulator.WriteString(BlueBgItalic(string(node.Literal))) 374 | 375 | case *ast.HTMLSpan: 376 | r.inlineAccumulator.WriteString(Red(string(node.Literal))) 377 | 378 | case *ast.Table: 379 | if entering { 380 | r.table = newTableRenderer() 381 | } else { 382 | r.table.Render(w, r.leftPad, r.lineWidth) 383 | r.table = nil 384 | } 385 | 386 | case *ast.TableCell: 387 | if !entering { 388 | content := r.inlineAccumulator.String() 389 | r.inlineAccumulator.Reset() 390 | 391 | align := CellAlignLeft 392 | switch node.Align { 393 | case ast.TableAlignmentRight: 394 | align = CellAlignRight 395 | case ast.TableAlignmentCenter: 396 | align = CellAlignCenter 397 | } 398 | 399 | if node.IsHeader { 400 | r.table.AddHeaderCell(content, align) 401 | } else { 402 | r.table.AddBodyCell(content, CellAlignCopyHeader) 403 | } 404 | } 405 | 406 | case *ast.TableHeader, *ast.TableBody, *ast.TableFooter: 407 | // nothing to do 408 | 409 | case *ast.TableRow: 410 | if _, ok := node.Parent.(*ast.TableBody); ok && entering { 411 | r.table.NextBodyRow() 412 | } 413 | if _, ok := node.Parent.(*ast.TableFooter); ok && entering { 414 | r.table.NextBodyRow() 415 | } 416 | 417 | default: 418 | panic(fmt.Sprintf("Unknown node type %T", node)) 419 | } 420 | 421 | return ast.GoToNext 422 | } 423 | 424 | func (*renderer) RenderHeader(w io.Writer, node ast.Node) {} 425 | 426 | func (*renderer) RenderFooter(w io.Writer, node ast.Node) {} 427 | 428 | func (r *renderer) renderHorizontalRule(w io.Writer) { 429 | _, _ = fmt.Fprintf(w, "%s%s\n\n", r.pad(), strings.Repeat("─", r.lineWidth-r.leftPad)) 430 | } 431 | 432 | func (r *renderer) renderHeading(w io.Writer, level int) { 433 | content := r.inlineAccumulator.String() 434 | r.inlineAccumulator.Reset() 435 | 436 | // render the full line with the headingNumbering 437 | r.headingNumbering.Observe(level) 438 | content = fmt.Sprintf("%s %s", r.headingNumbering.Render(), content) 439 | content = r.headingShade(level)(content) 440 | 441 | // wrap if needed 442 | wrapped, _ := text.WrapWithPad(content, r.lineWidth, r.pad()) 443 | _, _ = fmt.Fprintln(w, wrapped) 444 | 445 | // render the underline, if any 446 | if level == 1 { 447 | _, _ = fmt.Fprintf(w, "%s%s\n", r.pad(), strings.Repeat("─", r.lineWidth-r.leftPad)) 448 | } 449 | 450 | _, _ = fmt.Fprintln(w) 451 | } 452 | 453 | func (r *renderer) renderCodeBlock(w io.Writer, node *ast.CodeBlock) { 454 | code := string(node.Literal) 455 | var lexer chroma.Lexer 456 | // try to get the lexer from the language tag if any 457 | if len(node.Info) > 0 { 458 | lexer = lexers.Get(string(node.Info)) 459 | } 460 | // fallback on detection 461 | if lexer == nil { 462 | lexer = lexers.Analyse(code) 463 | } 464 | // all failed :-( 465 | if lexer == nil { 466 | lexer = lexers.Fallback 467 | } 468 | // simplify the lexer output 469 | lexer = chroma.Coalesce(lexer) 470 | 471 | var formatter chroma.Formatter 472 | if color.NoColor { 473 | formatter = formatters.Fallback 474 | } else { 475 | formatter = formatters.TTY8 476 | } 477 | 478 | iterator, err := lexer.Tokenise(nil, code) 479 | if err != nil { 480 | // Something failed, falling back to no highlight render 481 | r.renderFormattedCodeBlock(w, code) 482 | return 483 | } 484 | 485 | buf := &bytes.Buffer{} 486 | 487 | err = formatter.Format(buf, styles.Pygments, iterator) 488 | if err != nil { 489 | // Something failed, falling back to no highlight render 490 | r.renderFormattedCodeBlock(w, code) 491 | return 492 | } 493 | 494 | r.renderFormattedCodeBlock(w, buf.String()) 495 | } 496 | 497 | func (r *renderer) renderFormattedCodeBlock(w io.Writer, code string) { 498 | // remove the trailing line break 499 | code = strings.TrimRight(code, "\n") 500 | 501 | r.addPad(GreenBold("┃ ")) 502 | output, _ := text.WrapWithPad(code, r.lineWidth, r.pad()) 503 | r.popPad() 504 | 505 | _, _ = fmt.Fprint(w, output) 506 | 507 | _, _ = fmt.Fprintf(w, "\n\n") 508 | } 509 | 510 | func (r *renderer) renderHTMLBlock(w io.Writer, node *ast.HTMLBlock) { 511 | var buf bytes.Buffer 512 | 513 | flushInline := func() { 514 | if r.inlineAccumulator.Len() <= 0 { 515 | return 516 | } 517 | content := r.inlineAccumulator.String() 518 | r.inlineAccumulator.Reset() 519 | out, _ := text.WrapWithPad(content, r.lineWidth, r.pad()) 520 | _, _ = fmt.Fprint(&buf, out, "\n\n") 521 | } 522 | 523 | doc, err := html.Parse(bytes.NewReader(node.Literal)) 524 | if err != nil { 525 | // if there is a parsing error, fallback to a simple render 526 | r.inlineAccumulator.Reset() 527 | content := Red(string(node.Literal)) 528 | out, _ := text.WrapWithPad(content, r.lineWidth, r.pad()) 529 | _, _ = fmt.Fprint(w, out, "\n\n") 530 | return 531 | } 532 | 533 | htmlWalker.WalkFunc(doc, func(node *html.Node, entering bool) htmlWalker.WalkStatus { 534 | // if node.Type != html.TextNode { 535 | // fmt.Println(node.Type, "(", node.Data, ")", entering) 536 | // } 537 | 538 | switch node.Type { 539 | case html.CommentNode, html.DoctypeNode: 540 | // Not rendered 541 | 542 | case html.DocumentNode: 543 | 544 | case html.ElementNode: 545 | switch node.Data { 546 | case "html", "body": 547 | return htmlWalker.GoToNext 548 | 549 | case "head": 550 | return htmlWalker.SkipChildren 551 | 552 | case "div", "p": 553 | if entering { 554 | flushInline() 555 | } else { 556 | content := r.inlineAccumulator.String() 557 | r.inlineAccumulator.Reset() 558 | if len(content) == 0 { 559 | return htmlWalker.GoToNext 560 | } 561 | // remove all line breaks, those are fully managed in HTML 562 | content = strings.Replace(content, "\n", "", -1) 563 | align := getDivHTMLAttr(node.Attr) 564 | content, _ = text.WrapWithPadAlign(content, r.lineWidth, r.pad(), align) 565 | _, _ = fmt.Fprint(&buf, content, "\n\n") 566 | } 567 | 568 | case "h1": 569 | if !entering { 570 | r.renderHeading(&buf, 1) 571 | } 572 | case "h2": 573 | if !entering { 574 | r.renderHeading(&buf, 2) 575 | } 576 | case "h3": 577 | if !entering { 578 | r.renderHeading(&buf, 3) 579 | } 580 | case "h4": 581 | if !entering { 582 | r.renderHeading(&buf, 4) 583 | } 584 | case "h5": 585 | if !entering { 586 | r.renderHeading(&buf, 5) 587 | } 588 | case "h6": 589 | if !entering { 590 | r.renderHeading(&buf, 6) 591 | } 592 | 593 | case "img": 594 | flushInline() 595 | src, title := getImgHTMLAttr(node.Attr) 596 | str, _ := r.renderImage(src, title, r.lineWidth-len(r.pad())) 597 | r.inlineAccumulator.WriteString(str) 598 | 599 | case "hr": 600 | flushInline() 601 | r.renderHorizontalRule(&buf) 602 | 603 | case "ul", "ol": 604 | if !entering { 605 | if node.NextSibling == nil { 606 | _, _ = fmt.Fprint(&buf, "\n") 607 | return htmlWalker.GoToNext 608 | } 609 | switch node.NextSibling.Data { 610 | case "ul", "ol": 611 | default: 612 | _, _ = fmt.Fprint(&buf, "\n") 613 | } 614 | } 615 | 616 | case "li": 617 | if entering { 618 | switch node.Parent.Data { 619 | case "ul": 620 | r.indent = r.pad() + Green("• ") 621 | r.addPad(" ") 622 | 623 | case "ol": 624 | itemNumber := 1 625 | previous := node.PrevSibling 626 | for previous != nil { 627 | itemNumber++ 628 | previous = previous.PrevSibling 629 | } 630 | prefix := fmt.Sprintf("%d. ", itemNumber) 631 | r.indent = r.pad() + Green(prefix) 632 | r.addPad(strings.Repeat(" ", text.Len(prefix))) 633 | 634 | default: 635 | r.inlineAccumulator.WriteString(Red(renderRawHtml(node))) 636 | return htmlWalker.SkipChildren 637 | } 638 | } else { 639 | switch node.Parent.Data { 640 | case "ul", "ol": 641 | content := r.inlineAccumulator.String() 642 | r.inlineAccumulator.Reset() 643 | out, _ := text.WrapWithPadIndent(content, r.lineWidth, r.indent, r.pad()) 644 | r.indent = "" 645 | _, _ = fmt.Fprint(&buf, out, "\n") 646 | r.popPad() 647 | } 648 | } 649 | 650 | case "a": 651 | if entering { 652 | r.inlineAccumulator.WriteString("[") 653 | } else { 654 | href, alt := getAHTMLAttr(node.Attr) 655 | r.inlineAccumulator.WriteString("](") 656 | r.inlineAccumulator.WriteString(Blue(href)) 657 | if len(alt) > 0 { 658 | r.inlineAccumulator.WriteString(" ") 659 | r.inlineAccumulator.WriteString(alt) 660 | } 661 | r.inlineAccumulator.WriteString(")") 662 | } 663 | 664 | case "br": 665 | if entering { 666 | r.inlineAccumulator.WriteString("\n") 667 | } 668 | 669 | case "table": 670 | if entering { 671 | flushInline() 672 | r.table = newTableRenderer() 673 | } else { 674 | r.table.Render(&buf, r.leftPad, r.lineWidth) 675 | r.table = nil 676 | } 677 | 678 | case "thead", "tbody": 679 | // nothing to do 680 | 681 | case "tr": 682 | if entering && node.Parent.Data != "thead" { 683 | r.table.NextBodyRow() 684 | } 685 | 686 | case "th": 687 | if !entering { 688 | content := r.inlineAccumulator.String() 689 | r.inlineAccumulator.Reset() 690 | 691 | align := getTdHTMLAttr(node.Attr) 692 | r.table.AddHeaderCell(content, align) 693 | } 694 | 695 | case "td": 696 | if !entering { 697 | content := r.inlineAccumulator.String() 698 | r.inlineAccumulator.Reset() 699 | 700 | align := getTdHTMLAttr(node.Attr) 701 | r.table.AddBodyCell(content, align) 702 | } 703 | 704 | case "strong", "b": 705 | if entering { 706 | r.inlineAccumulator.WriteString(boldOn) 707 | } else { 708 | // This is super silly but some terminals, instead of having 709 | // the ANSI code SGR 21 do "bold off" like the logic would guide, 710 | // do "double underline" instead. This is madness. 711 | 712 | // To resolve that problem, we take a snapshot of the escape state, 713 | // remove the bold, then output "reset all" + snapshot 714 | es := text.EscapeState{} 715 | es.Witness(r.inlineAccumulator.String()) 716 | es.Bold = false 717 | r.inlineAccumulator.WriteString(resetAll) 718 | r.inlineAccumulator.WriteString(es.FormatString()) 719 | } 720 | 721 | case "i", "em": 722 | if entering { 723 | r.inlineAccumulator.WriteString(italicOn) 724 | } else { 725 | r.inlineAccumulator.WriteString(italicOff) 726 | } 727 | 728 | case "s": 729 | if entering { 730 | r.inlineAccumulator.WriteString(crossedOutOn) 731 | } else { 732 | r.inlineAccumulator.WriteString(crossedOutOff) 733 | } 734 | 735 | default: 736 | r.inlineAccumulator.WriteString(Red(renderRawHtml(node))) 737 | } 738 | 739 | case html.TextNode: 740 | t := strings.TrimSpace(node.Data) 741 | t = strings.ReplaceAll(t, "\n", "") 742 | r.inlineAccumulator.WriteString(t) 743 | 744 | default: 745 | panic("unhandled case") 746 | } 747 | 748 | return htmlWalker.GoToNext 749 | }) 750 | 751 | flushInline() 752 | _, _ = fmt.Fprint(w, buf.String()) 753 | r.inlineAccumulator.Reset() 754 | 755 | // // dl + (dt+dd) 756 | // 757 | // // details 758 | // // summary 759 | // 760 | } 761 | 762 | func getDivHTMLAttr(attrs []html.Attribute) text.Alignment { 763 | for _, attr := range attrs { 764 | switch attr.Key { 765 | case "align": 766 | switch attr.Val { 767 | case "left": 768 | return text.AlignLeft 769 | case "center": 770 | return text.AlignCenter 771 | case "right": 772 | return text.AlignRight 773 | } 774 | } 775 | } 776 | return text.AlignLeft 777 | } 778 | 779 | func getImgHTMLAttr(attrs []html.Attribute) (src, title string) { 780 | for _, attr := range attrs { 781 | switch attr.Key { 782 | case "src": 783 | src = attr.Val 784 | case "alt": 785 | title = attr.Val 786 | } 787 | } 788 | return 789 | } 790 | 791 | func getAHTMLAttr(attrs []html.Attribute) (href, alt string) { 792 | for _, attr := range attrs { 793 | switch attr.Key { 794 | case "href": 795 | href = attr.Val 796 | case "alt": 797 | alt = attr.Val 798 | } 799 | } 800 | return 801 | } 802 | 803 | func getTdHTMLAttr(attrs []html.Attribute) CellAlign { 804 | for _, attr := range attrs { 805 | switch attr.Key { 806 | case "align": 807 | switch attr.Val { 808 | case "right": 809 | return CellAlignRight 810 | case "left": 811 | return CellAlignLeft 812 | case "center": 813 | return CellAlignCenter 814 | } 815 | 816 | case "style": 817 | for _, pair := range strings.Split(attr.Val, " ") { 818 | split := strings.Split(pair, ":") 819 | if split[0] != "text-align" || len(split) != 2 { 820 | continue 821 | } 822 | switch split[1] { 823 | case "right": 824 | return CellAlignRight 825 | case "left": 826 | return CellAlignLeft 827 | case "center": 828 | return CellAlignCenter 829 | } 830 | } 831 | } 832 | } 833 | return CellAlignLeft 834 | } 835 | 836 | func renderRawHtml(node *html.Node) string { 837 | var result strings.Builder 838 | openContent := make([]string, 0, 8) 839 | 840 | openContent = append(openContent, node.Data) 841 | for _, attr := range node.Attr { 842 | openContent = append(openContent, fmt.Sprintf("%s=\"%s\"", attr.Key, attr.Val)) 843 | } 844 | 845 | result.WriteString("<") 846 | result.WriteString(strings.Join(openContent, " ")) 847 | 848 | if node.FirstChild == nil { 849 | result.WriteString("/>") 850 | return result.String() 851 | } 852 | 853 | result.WriteString(">") 854 | 855 | child := node.FirstChild 856 | for child != nil { 857 | if child.Type == html.TextNode { 858 | t := strings.TrimSpace(child.Data) 859 | result.WriteString(t) 860 | child = child.NextSibling 861 | continue 862 | } 863 | 864 | switch node.Data { 865 | case "ul", "p": 866 | result.WriteString("\n ") 867 | } 868 | 869 | result.WriteString(renderRawHtml(child)) 870 | child = child.NextSibling 871 | } 872 | 873 | switch node.Data { 874 | case "ul", "p": 875 | result.WriteString("\n") 876 | } 877 | 878 | result.WriteString("") 881 | 882 | return result.String() 883 | } 884 | 885 | func (r *renderer) renderImage(dest string, title string, lineWidth int) (result string, rendered bool) { 886 | title = strings.ReplaceAll(title, "\n", "") 887 | title = strings.TrimSpace(title) 888 | dest = strings.ReplaceAll(dest, "\n", "") 889 | dest = strings.TrimSpace(dest) 890 | 891 | fallback := func() (string, bool) { 892 | return fmt.Sprintf("![%s](%s)", title, Blue(dest)), false 893 | } 894 | 895 | reader, err := imageFromDestination(dest) 896 | if err != nil { 897 | return fallback() 898 | } 899 | 900 | x := lineWidth 901 | 902 | if r.imageDithering == ansimage.DitheringWithChars || r.imageDithering == ansimage.DitheringWithBlocks { 903 | // not sure why this is needed by ansimage 904 | // x *= 4 905 | } 906 | 907 | img, err := ansimage.NewScaledFromReader(reader, math.MaxInt32, x, 908 | stdcolor.Black, ansimage.ScaleModeFit, r.imageDithering) 909 | 910 | if err != nil { 911 | return fallback() 912 | } 913 | 914 | if title != "" { 915 | return fmt.Sprintf("%s%s: %s", img.Render(), title, Blue(dest)), true 916 | } 917 | return fmt.Sprintf("%s%s", img.Render(), Blue(dest)), true 918 | } 919 | 920 | func imageFromDestination(dest string) (io.ReadCloser, error) { 921 | client := http.Client{ 922 | Timeout: 5 * time.Second, 923 | } 924 | 925 | if strings.HasPrefix(dest, "http://") || strings.HasPrefix(dest, "https://") { 926 | res, err := client.Get(dest) 927 | if err != nil { 928 | return nil, err 929 | } 930 | if res.StatusCode != http.StatusOK { 931 | return nil, fmt.Errorf("http: %v", http.StatusText(res.StatusCode)) 932 | } 933 | 934 | return res.Body, nil 935 | } 936 | 937 | return os.Open(dest) 938 | } 939 | 940 | func removeLineBreak(text string) string { 941 | lines := strings.Split(text, "\n") 942 | 943 | if len(lines) <= 1 { 944 | return text 945 | } 946 | 947 | for i, l := range lines { 948 | switch i { 949 | case 0: 950 | lines[i] = strings.TrimRightFunc(l, unicode.IsSpace) 951 | case len(lines) - 1: 952 | lines[i] = strings.TrimLeftFunc(l, unicode.IsSpace) 953 | default: 954 | lines[i] = strings.TrimFunc(l, unicode.IsSpace) 955 | } 956 | } 957 | return strings.Join(lines, " ") 958 | } 959 | 960 | func shouldCleanText(node ast.Node) bool { 961 | for node != nil { 962 | switch node.(type) { 963 | case *ast.BlockQuote: 964 | return false 965 | 966 | case *ast.Heading, *ast.Image, *ast.Link, 967 | *ast.TableCell, *ast.Document, *ast.ListItem: 968 | return true 969 | } 970 | 971 | node = node.GetParent() 972 | } 973 | 974 | panic("bad markdown document or missing case") 975 | } 976 | -------------------------------------------------------------------------------- /renderer_test.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestRemoveLineBreak(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | want string 13 | }{ 14 | { 15 | "hello\nhello", 16 | "hello hello", 17 | }, 18 | { 19 | "hello \nhello", 20 | "hello hello", 21 | }, 22 | { 23 | "hello\n hello", 24 | "hello hello", 25 | }, 26 | { 27 | " hello hello \n hello hello ", 28 | " hello hello hello hello ", 29 | }, 30 | { 31 | " hello ", 32 | " hello ", 33 | }, 34 | } 35 | for _, tt := range tests { 36 | assert.Equal(t, tt.want, removeLineBreak(tt.input)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shades.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | var defaultHeadingShades = []shadeFmt{ 4 | GreenBold, 5 | GreenBold, 6 | HiGreen, 7 | Green, 8 | } 9 | 10 | var defaultQuoteShades = []shadeFmt{ 11 | GreenBold, 12 | GreenBold, 13 | HiGreen, 14 | Green, 15 | } 16 | 17 | type shadeFmt func(a ...interface{}) string 18 | 19 | type levelShadeFmt func(level int) shadeFmt 20 | 21 | // Return a function giving the color function corresponding to the level. 22 | // Beware, level start counting from 1. 23 | func shade(shades []shadeFmt) levelShadeFmt { 24 | return func(level int) shadeFmt { 25 | if level < 1 { 26 | level = 1 27 | } 28 | if level > len(shades) { 29 | level = len(shades) 30 | } 31 | return shades[level-1] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tables.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | 7 | "github.com/MichaelMure/go-term-text" 8 | ) 9 | 10 | const minColumnCompactedWidth = 5 11 | 12 | type CellAlign int 13 | 14 | const ( 15 | CellAlignLeft CellAlign = iota 16 | CellAlignRight 17 | CellAlignCenter 18 | CellAlignCopyHeader 19 | ) 20 | 21 | type tableCell struct { 22 | content string 23 | alignment CellAlign 24 | } 25 | 26 | type tableRenderer struct { 27 | header []tableCell 28 | body [][]tableCell 29 | } 30 | 31 | func newTableRenderer() *tableRenderer { 32 | return &tableRenderer{} 33 | } 34 | 35 | func (tr *tableRenderer) AddHeaderCell(content string, alignment CellAlign) { 36 | tr.header = append(tr.header, tableCell{ 37 | content: content, 38 | alignment: alignment, 39 | }) 40 | } 41 | 42 | func (tr *tableRenderer) NextBodyRow() { 43 | tr.body = append(tr.body, nil) 44 | } 45 | 46 | func (tr *tableRenderer) AddBodyCell(content string, alignement CellAlign) { 47 | row := tr.body[len(tr.body)-1] 48 | row = append(row, tableCell{ 49 | content: content, 50 | alignment: alignement, 51 | }) 52 | tr.body[len(tr.body)-1] = row 53 | } 54 | 55 | // normalize ensure that the table has the same number of cells 56 | // in each rows, header or not. 57 | func (tr *tableRenderer) normalize() { 58 | width := len(tr.header) 59 | for _, row := range tr.body { 60 | width = max(width, len(row)) 61 | } 62 | 63 | // grow the header if needed 64 | for len(tr.header) < width { 65 | tr.header = append(tr.header, tableCell{}) 66 | } 67 | 68 | // grow lines if needed 69 | for i := range tr.body { 70 | for len(tr.body[i]) < width { 71 | tr.body[i] = append(tr.body[i], tableCell{}) 72 | } 73 | } 74 | } 75 | 76 | func (tr *tableRenderer) copyAlign() { 77 | for i, row := range tr.body { 78 | for j, cell := range row { 79 | if cell.alignment == CellAlignCopyHeader { 80 | tr.body[i][j].alignment = tr.header[j].alignment 81 | } 82 | } 83 | } 84 | } 85 | 86 | func (tr *tableRenderer) Render(w io.Writer, leftPad int, lineWidth int) { 87 | tr.normalize() 88 | tr.copyAlign() 89 | 90 | columnWidths, truncated := tr.columnWidths(lineWidth - leftPad) 91 | pad := strings.Repeat(" ", leftPad) 92 | 93 | drawTopLine(w, pad, columnWidths, truncated) 94 | 95 | drawRow(w, pad, tr.header, columnWidths, truncated) 96 | 97 | drawHeaderUnderline(w, pad, columnWidths, truncated) 98 | 99 | for i, row := range tr.body { 100 | drawRow(w, pad, row, columnWidths, truncated) 101 | if i != len(tr.body)-1 { 102 | drawRowLine(w, pad, columnWidths, truncated) 103 | } 104 | } 105 | 106 | drawBottomLine(w, pad, columnWidths, truncated) 107 | } 108 | 109 | func (tr *tableRenderer) columnWidths(lineWidth int) (widths []int, truncated bool) { 110 | l := len(tr.header) 111 | if len(tr.body) > 0 { 112 | l = max(l, len(tr.body[0])) 113 | } 114 | 115 | maxWidth := make([]int, l) 116 | 117 | for i, cell := range tr.header { 118 | maxWidth[i] = max(maxWidth[i], text.MaxLineLen(cell.content)) 119 | } 120 | 121 | for _, row := range tr.body { 122 | for i, cell := range row { 123 | maxWidth[i] = max(maxWidth[i], text.MaxLineLen(cell.content)) 124 | } 125 | } 126 | 127 | sumWidth := 1 128 | minWidth := 1 129 | for _, width := range maxWidth { 130 | sumWidth += width + 1 131 | minWidth += min(width, minColumnCompactedWidth) + 1 132 | } 133 | 134 | // Strategy 1: the easy case, content is not large enough to overflow 135 | if sumWidth <= lineWidth { 136 | return maxWidth, false 137 | } 138 | 139 | // Strategy 2: overflow, but still enough room 140 | if minWidth < lineWidth { 141 | return tr.overflowColumnWidths(lineWidth, maxWidth), false 142 | } 143 | 144 | // Strategy 3: too much columns, we need to truncate 145 | return tr.truncateColumnWidths(lineWidth, maxWidth), true 146 | } 147 | 148 | func (tr *tableRenderer) overflowColumnWidths(lineWidth int, maxWidth []int) []int { 149 | // We have an overflow. First, we take as is the columns that are thinner 150 | // than the space equally divided. 151 | // Integer division, rounded lower. 152 | available := lineWidth - len(tr.header) - 1 153 | fairSpace := available / len(tr.header) 154 | 155 | result := make([]int, len(tr.header)) 156 | remainingColumn := len(tr.header) 157 | 158 | for i, width := range maxWidth { 159 | if width <= fairSpace { 160 | result[i] = width 161 | available -= width 162 | remainingColumn-- 163 | } else { 164 | // Mark the column as non-allocated yet 165 | result[i] = -1 166 | } 167 | } 168 | 169 | // Now we allocate evenly the remaining space to the remaining columns 170 | for i, width := range result { 171 | if width == -1 { 172 | width = available / remainingColumn 173 | result[i] = width 174 | available -= width 175 | remainingColumn-- 176 | } 177 | } 178 | 179 | return result 180 | } 181 | 182 | func (tr *tableRenderer) truncateColumnWidths(lineWidth int, maxWidth []int) []int { 183 | var result []int 184 | used := 1 185 | 186 | // Pack as much column as possible without compacting them too much 187 | for _, width := range maxWidth { 188 | w := min(width, minColumnCompactedWidth) 189 | 190 | if used+w+1 > lineWidth { 191 | return result 192 | } 193 | 194 | result = append(result, w) 195 | used += w + 1 196 | } 197 | 198 | return result 199 | } 200 | 201 | func drawTopLine(w io.Writer, pad string, columnWidths []int, truncated bool) { 202 | _, _ = w.Write([]byte(pad)) 203 | _, _ = w.Write([]byte("┌")) 204 | for i, width := range columnWidths { 205 | _, _ = w.Write([]byte(strings.Repeat("─", width))) 206 | if i != len(columnWidths)-1 { 207 | _, _ = w.Write([]byte("┬")) 208 | } 209 | } 210 | _, _ = w.Write([]byte("┐")) 211 | if truncated { 212 | _, _ = w.Write([]byte("…")) 213 | } 214 | _, _ = w.Write([]byte("\n")) 215 | } 216 | 217 | func drawHeaderUnderline(w io.Writer, pad string, columnWidths []int, truncated bool) { 218 | _, _ = w.Write([]byte(pad)) 219 | _, _ = w.Write([]byte("╞")) 220 | for i, width := range columnWidths { 221 | _, _ = w.Write([]byte(strings.Repeat("═", width))) 222 | if i != len(columnWidths)-1 { 223 | _, _ = w.Write([]byte("╪")) 224 | } 225 | } 226 | _, _ = w.Write([]byte("╡")) 227 | if truncated { 228 | _, _ = w.Write([]byte("…")) 229 | } 230 | _, _ = w.Write([]byte("\n")) 231 | } 232 | 233 | func drawBottomLine(w io.Writer, pad string, columnWidths []int, truncated bool) { 234 | _, _ = w.Write([]byte(pad)) 235 | _, _ = w.Write([]byte("└")) 236 | for i, width := range columnWidths { 237 | _, _ = w.Write([]byte(strings.Repeat("─", width))) 238 | if i != len(columnWidths)-1 { 239 | _, _ = w.Write([]byte("┴")) 240 | } 241 | } 242 | _, _ = w.Write([]byte("┘")) 243 | if truncated { 244 | _, _ = w.Write([]byte("…")) 245 | } 246 | _, _ = w.Write([]byte("\n")) 247 | } 248 | 249 | func drawRowLine(w io.Writer, pad string, columnWidths []int, truncated bool) { 250 | _, _ = w.Write([]byte(pad)) 251 | _, _ = w.Write([]byte("├")) 252 | for i, width := range columnWidths { 253 | _, _ = w.Write([]byte(strings.Repeat("─", width))) 254 | if i != len(columnWidths)-1 { 255 | _, _ = w.Write([]byte("┼")) 256 | } 257 | } 258 | _, _ = w.Write([]byte("┤")) 259 | if truncated { 260 | _, _ = w.Write([]byte("…")) 261 | } 262 | _, _ = w.Write([]byte("\n")) 263 | } 264 | 265 | func drawRow(w io.Writer, pad string, cells []tableCell, columnWidths []int, truncated bool) { 266 | contents := make([][]string, len(cells)) 267 | 268 | // As we draw the row line by line, we need a way to reset and recover 269 | // the formatting when we alternate between cells. To do that, we witness 270 | // the ongoing series of ANSI escape sequence for each cell into a EscapeState. 271 | // This component will be able to merge them and to give us a snapshot sequence 272 | // that we can use when we start the cell again 273 | formatting := make([]text.EscapeState, len(columnWidths)) 274 | 275 | maxHeight := 0 276 | 277 | // Wrap each cell content into multiple lines, depending on 278 | // how wide each cell is. 279 | for i, cell := range cells[:len(columnWidths)] { 280 | if columnWidths[i] == 0 { 281 | continue 282 | } 283 | wrapped, lines := text.Wrap(cell.content, columnWidths[i]) 284 | contents[i] = strings.Split(wrapped, "\n") 285 | maxHeight = max(maxHeight, lines) 286 | } 287 | 288 | // Draw the row line by line 289 | for i := 0; i < maxHeight; i++ { 290 | _, _ = w.Write([]byte(pad)) 291 | _, _ = w.Write([]byte("│")) 292 | for j, width := range columnWidths { 293 | content := "" 294 | if len(contents[j]) > i { 295 | content = contents[j][i] 296 | trimmed := text.TrimSpace(content) 297 | 298 | switch cells[j].alignment { 299 | case CellAlignLeft: 300 | _, _ = w.Write([]byte(formatting[j].FormatString())) 301 | // accumulate the formatting 302 | formatting[j].Witness(trimmed) 303 | _, _ = w.Write([]byte(trimmed)) 304 | _, _ = w.Write([]byte(formatting[j].ResetString())) 305 | _, _ = w.Write([]byte(strings.Repeat(" ", width-text.Len(trimmed)))) 306 | 307 | case CellAlignCenter: 308 | spaces := width - text.Len(trimmed) 309 | _, _ = w.Write([]byte(strings.Repeat(" ", spaces/2))) 310 | _, _ = w.Write([]byte(formatting[j].FormatString())) 311 | // accumulate the formatting 312 | formatting[j].Witness(trimmed) 313 | _, _ = w.Write([]byte(trimmed)) 314 | _, _ = w.Write([]byte(formatting[j].ResetString())) 315 | _, _ = w.Write([]byte(strings.Repeat(" ", spaces-(spaces/2)))) 316 | 317 | case CellAlignRight: 318 | _, _ = w.Write([]byte(strings.Repeat(" ", width-text.Len(trimmed)))) 319 | _, _ = w.Write([]byte(formatting[j].FormatString())) 320 | // accumulate the formatting 321 | formatting[j].Witness(trimmed) 322 | _, _ = w.Write([]byte(trimmed)) 323 | _, _ = w.Write([]byte(formatting[j].ResetString())) 324 | } 325 | } else { 326 | padding := strings.Repeat(" ", width-text.Len(content)) 327 | _, _ = w.Write([]byte(padding)) 328 | } 329 | _, _ = w.Write([]byte("│")) 330 | } 331 | if truncated { 332 | _, _ = w.Write([]byte("…")) 333 | } 334 | _, _ = w.Write([]byte("\n")) 335 | } 336 | } 337 | 338 | func min(a, b int) int { 339 | if a < b { 340 | return a 341 | } 342 | return b 343 | } 344 | 345 | func max(a, b int) int { 346 | if a > b { 347 | return a 348 | } 349 | return b 350 | } 351 | -------------------------------------------------------------------------------- /tables_test.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestColumnWidths(t *testing.T) { 11 | const lineWidth = 40 12 | 13 | cases := []struct { 14 | cellWidths []int 15 | expected []int 16 | truncated bool 17 | }{ 18 | { 19 | []int{0}, 20 | []int{0}, 21 | false, 22 | }, 23 | { 24 | []int{0, 0, 0, 0, 0}, 25 | []int{0, 0, 0, 0, 0}, 26 | false, 27 | }, 28 | { 29 | []int{1, 2, 3, 4, 5}, 30 | []int{1, 2, 3, 4, 5}, 31 | false, 32 | }, 33 | { 34 | // overflow, one column 35 | []int{60}, 36 | []int{38}, 37 | false, 38 | }, 39 | { 40 | // overflow, multiple columns 41 | []int{30, 30, 30}, 42 | []int{12, 12, 12}, // (40-4)/3 43 | false, 44 | }, 45 | { 46 | // overflow, different columns 47 | []int{30, 60, 30}, 48 | []int{12, 12, 12}, 49 | false, 50 | }, 51 | { 52 | // overflow, different columns with one small enough 53 | []int{10, 30, 30}, 54 | []int{10, 13, 13}, 55 | false, 56 | }, 57 | { 58 | // overflow, different columns with one small enough 59 | []int{10, 60, 30}, 60 | []int{10, 13, 13}, 61 | false, 62 | }, 63 | { 64 | // too much columns 65 | []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, 66 | []int{5, 5, 5, 5, 5, 5}, 67 | true, 68 | }, 69 | } 70 | 71 | for _, tc := range cases { 72 | tr := newTableRenderer() 73 | 74 | for _, w := range tc.cellWidths { 75 | tr.AddHeaderCell(strings.Repeat("a", w), CellAlignLeft) 76 | } 77 | tr.NextBodyRow() 78 | for _, w := range tc.cellWidths { 79 | tr.AddBodyCell(strings.Repeat("a", w), CellAlignCopyHeader) 80 | } 81 | 82 | result, truncated := tr.columnWidths(lineWidth) 83 | 84 | assert.Equal(t, tc.expected, result) 85 | assert.Equal(t, tc.truncated, truncated) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /testdata_media/termui_recording.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/testdata_media/termui_recording.gif -------------------------------------------------------------------------------- /testdata_media/webui1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/testdata_media/webui1.png -------------------------------------------------------------------------------- /testdata_media/webui2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/testdata_media/webui2.png -------------------------------------------------------------------------------- /testdata_result/Amps and angle encoding.txt: -------------------------------------------------------------------------------- 1 | AT&T has an ampersand in their name. 2 | 3 | AT&T is another way to write it. 4 | 5 | This & that. 6 | 7 | 4 < 5. 8 | 9 | 6 > 5. 10 | 11 | Here's a [link](http://example.com/? 12 | foo=1&bar=2) with an ampersand in 13 | the URL. 14 | 15 | Here's a link with an amersand in 16 | the link text: 17 | [AT&T](http://att.com/ AT&T). 18 | 19 | Here's an inline 20 | [link](/script?foo=1&bar=2). 21 | 22 | Here's an inline 23 | [link](/script?foo=1&bar=2). 24 | -------------------------------------------------------------------------------- /testdata_result/Auto links.txt: -------------------------------------------------------------------------------- 1 | Link: [http://example.com/](http://e 2 | xample.com/). 3 | 4 | With an ampersand: [http://example.c 5 | om/?foo=1&bar=2](http://example.com/ 6 | ?foo=1&bar=2) 7 | • In a list? 8 | • [http://example.com/](http://examp 9 | le.com/) 10 | • It should. 11 | 12 | ┃ Blockquoted: [http://example.com/] 13 | ┃ (http://example.com/) 14 | Auto-links should not occur here: 15 |  16 | 17 | ┃ or here: 18 | 19 | -------------------------------------------------------------------------------- /testdata_result/Backslash escapes.txt: -------------------------------------------------------------------------------- 1 | These should all get escaped: 2 | 3 | Backslash: \ 4 | 5 | Backtick: ` 6 | 7 | Asterisk: * 8 | 9 | Underscore: _ 10 | 11 | Left brace: { 12 | 13 | Right brace: } 14 | 15 | Left bracket: [ 16 | 17 | Right bracket: ] 18 | 19 | Left paren: ( 20 | 21 | Right paren: ) 22 | 23 | Greater-than: > 24 | 25 | Hash: # 26 | 27 | Period: . 28 | 29 | Bang: ! 30 | 31 | Plus: + 32 | 33 | Minus: - 34 | 35 | Tilde: ~ 36 | 37 | These should not, because they occur 38 | within a code block: 39 | 40 | ┃ Backslash: \\ 41 | ┃  42 | ┃ Backtick: \` 43 | ┃  44 | ┃ Asterisk: \* 45 | ┃  46 | ┃ Underscore: \_ 47 | ┃  48 | ┃ Left brace: \{ 49 | ┃  50 | ┃ Right brace: \} 51 | ┃  52 | ┃ Left bracket: \[ 53 | ┃  54 | ┃ Right bracket: \] 55 | ┃  56 | ┃ Left paren: \( 57 | ┃  58 | ┃ Right paren: \) 59 | ┃  60 | ┃ Greater-than: \> 61 | ┃  62 | ┃ Hash: \# 63 | ┃  64 | ┃ Period: \. 65 | ┃  66 | ┃ Bang: \! 67 | ┃  68 | ┃ Plus: \+ 69 | ┃  70 | ┃ Minus: \- 71 | ┃  72 | ┃ Tilde: \~ 73 | 74 | Nor should these, which occur in 75 | code spans: 76 | 77 | Backslash: \\ 78 | 79 | Backtick: \` 80 | 81 | Asterisk: \* 82 | 83 | Underscore: \_ 84 | 85 | Left brace: \{ 86 | 87 | Right brace: \} 88 | 89 | Left bracket: \[ 90 | 91 | Right bracket: \] 92 | 93 | Left paren: \( 94 | 95 | Right paren: \) 96 | 97 | Greater-than: \> 98 | 99 | Hash: \# 100 | 101 | Period: \. 102 | 103 | Bang: \! 104 | 105 | Plus: \+ 106 | 107 | Minus: \- 108 | 109 | Tilde: \~ 110 | 111 | These should get escaped, even 112 | though they're matching pairs for 113 | other Markdown constructs: 114 | 115 | *asterisks* 116 | 117 | _underscores_ 118 | 119 | `backticks` 120 | 121 | This is a code span with a literal 122 | backslash-backtick sequence: \` 123 | 124 | This is a tag with unescaped 125 | backticks bar. 127 | 128 | This is a tag with backslashes bar. 130 | -------------------------------------------------------------------------------- /testdata_result/Blockquotes with code blocks.txt: -------------------------------------------------------------------------------- 1 | ┃ Example: 2 | 3 | ┃ ┃ sub status { 4 | ┃ ┃  print "working"; 5 | ┃ ┃ } 6 | 7 | ┃ Or: 8 | 9 | ┃ ┃ sub status { 10 | ┃ ┃  return "working"; 11 | ┃ ┃ } 12 | 13 | ┃ Blockquote 14 | 15 | ┃ ┃  with 16 | 17 | ┃ some 18 | 19 | ┃ ┃  spaces 20 | 21 | -------------------------------------------------------------------------------- /testdata_result/Code Blocks.txt: -------------------------------------------------------------------------------- 1 | ┃ code block on the first line 2 | 3 | Regular text. 4 | 5 | ┃ code block indented by spaces 6 | 7 | Regular text. 8 | 9 | ┃ the lines in this block 10 | ┃ all contain trailing spaces 11 | 12 | Regular Text. 13 | 14 | ┃ code block on the last line 15 | 16 | -------------------------------------------------------------------------------- /testdata_result/Code Spans.txt: -------------------------------------------------------------------------------- 1 |  2 | 3 | Fix for backticks within HTML tag: 4 | like 5 | this 6 | 7 | Here's how you put `backticks` in a 8 | code span. 9 | -------------------------------------------------------------------------------- /testdata_result/Hard-wrapped paragraphs with list-like lines no empty line before block.txt: -------------------------------------------------------------------------------- 1 | In Markdown 1.0.0 and earlier. 2 | Version 3 | 1. This line turns into a list item. 4 | Because a hard-wrapped line in 5 | the middle of a paragraph looked 6 | like a list item. 7 | 8 | Here's one with a bullet. 9 | • criminey. 10 | -------------------------------------------------------------------------------- /testdata_result/Hard-wrapped paragraphs with list-like lines.txt: -------------------------------------------------------------------------------- 1 | In Markdown 1.0.0 and earlier. 2 | Version 3 | 1. This line turns into a list item. 4 | Because a hard-wrapped line in 5 | the middle of a paragraph looked 6 | like a list item. 7 | 8 | Here's one with a bullet. 9 | • criminey. 10 | -------------------------------------------------------------------------------- /testdata_result/Horizontal rules.txt: -------------------------------------------------------------------------------- 1 | Dashes: 2 | 3 | ──────────────────────────────────── 4 | 5 | ──────────────────────────────────── 6 | 7 | ──────────────────────────────────── 8 | 9 | ──────────────────────────────────── 10 | 11 | ┃ --- 12 | 13 | ──────────────────────────────────── 14 | 15 | ──────────────────────────────────── 16 | 17 | ──────────────────────────────────── 18 | 19 | ──────────────────────────────────── 20 | 21 | ┃ - - - 22 | 23 | Asterisks: 24 | 25 | ──────────────────────────────────── 26 | 27 | ──────────────────────────────────── 28 | 29 | ──────────────────────────────────── 30 | 31 | ──────────────────────────────────── 32 | 33 | ┃ *** 34 | 35 | ──────────────────────────────────── 36 | 37 | ──────────────────────────────────── 38 | 39 | ──────────────────────────────────── 40 | 41 | ──────────────────────────────────── 42 | 43 | ┃ * * * 44 | 45 | Underscores: 46 | 47 | ──────────────────────────────────── 48 | 49 | ──────────────────────────────────── 50 | 51 | ──────────────────────────────────── 52 | 53 | ──────────────────────────────────── 54 | 55 | ┃ ___ 56 | 57 | ──────────────────────────────────── 58 | 59 | ──────────────────────────────────── 60 | 61 | ──────────────────────────────────── 62 | 63 | ──────────────────────────────────── 64 | 65 | ┃ _ _ _ 66 | 67 | -------------------------------------------------------------------------------- /testdata_result/Inline HTML (Advanced).txt: -------------------------------------------------------------------------------- 1 | Simple block on one line: 2 | 3 | foo 4 | 5 | And nested without indentation: 6 | 7 | foo 8 | 9 | bar 10 | 11 |  12 | -------------------------------------------------------------------------------- /testdata_result/Inline HTML (Simple).txt: -------------------------------------------------------------------------------- 1 | Here's a simple block: 2 | 3 | foo 4 | 5 | This should be a code block, though: 6 | 7 | ┃ 
8 | ┃  foo 9 | ┃ 
10 | 11 | As should this: 12 | 13 | ┃ 
foo
14 | 15 | Now, nested: 16 | 17 | foo 18 | 19 | ┃  20 | 21 |  22 | 23 | This should just be an HTML comment: 24 | 25 | Multiline: 26 | 27 | Code block: 28 | 29 | ┃  30 | 31 | Just plain comment, with trailing 32 | spaces on the line: 33 | 34 | Code: 35 | 36 | ┃ 
37 | 38 | Hr's: 39 | 40 | ──────────────────────────────────── 41 | 42 | ──────────────────────────────────── 43 | 44 | ──────────────────────────────────── 45 | 46 | ──────────────────────────────────── 47 | 48 | ──────────────────────────────────── 49 | 50 | ──────────────────────────────────── 51 | 52 | ──────────────────────────────────── 53 | 54 | ──────────────────────────────────── 55 | 56 | ──────────────────────────────────── 57 | 58 | -------------------------------------------------------------------------------- /testdata_result/Inline HTML comments.txt: -------------------------------------------------------------------------------- 1 | Paragraph one. 2 | 3 | Paragraph two. 4 | 5 | The end. 6 | -------------------------------------------------------------------------------- /testdata_result/Links, inline style.text.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/testdata_result/Links, inline style.text.txt -------------------------------------------------------------------------------- /testdata_result/Links, inline style.txt: -------------------------------------------------------------------------------- 1 | Just a [URL](/url/). 2 | 3 | [URL and title](/url/ title). 4 | 5 | [URL and title](/url/ title preceded 6 | by two spaces). 7 | 8 | [URL and title](/url/ title preceded 9 | by a tab). 10 | 11 | [URL and title](/url/ title has 12 | spaces afterward). 13 | 14 | [Empty](). 15 | -------------------------------------------------------------------------------- /testdata_result/Links, reference style.text.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMure/go-term-markdown/d84f2b9b34f9b8beadd9307bff0592620f7b9578/testdata_result/Links, reference style.text.txt -------------------------------------------------------------------------------- /testdata_result/Links, reference style.txt: -------------------------------------------------------------------------------- 1 | Foo [bar](/url/ Title). 2 | 3 | Foo [bar](/url/ Title). 4 | 5 | Foo [bar](/url/ Title). 6 | 7 | With [embedded [brackets]](/url/). 8 | 9 | Indented [once](/url). 10 | 11 | Indented [twice](/url). 12 | 13 | Indented [thrice](/url). 14 | 15 | Indented [four][] times. 16 | 17 | ┃ [four]: /url 18 | 19 | ──────────────────────────────────── 20 | 21 | [this](foo) should work 22 | 23 | So should [this](foo). 24 | 25 | And [this](foo). 26 | 27 | And [this](foo). 28 | 29 | And [this](foo). 30 | 31 | But not [that] []. 32 | 33 | Nor [that][]. 34 | 35 | Nor [that]. 36 | 37 | [Something in brackets like 38 | [this](foo) should work] 39 | 40 | [Same with [this](foo).] 41 | 42 | In this case, 43 | [this](/somethingelse/) points to 44 | something else. 45 | 46 | Backslashing should suppress [this] 47 | and [this]. 48 | 49 | ──────────────────────────────────── 50 | 51 | Here's one where the [link 52 | breaks](/url/) across lines. 53 | 54 | Here's another where the 55 | [link](/url/) across lines, but with 56 | a line-ending space. 57 | -------------------------------------------------------------------------------- /testdata_result/Links, shortcut references.txt: -------------------------------------------------------------------------------- 1 | This is the [simple case](/simple). 2 | 3 | This one has a [line 4 | break](/foo). 5 | 6 | This one has a [line](/foo) with a 7 | line-ending space. 8 | 9 | [this](/that) and the 10 | [other](/other) 11 | -------------------------------------------------------------------------------- /testdata_result/Literal quotes in titles.txt: -------------------------------------------------------------------------------- 1 | Foo [bar](/url/ Title with "quotes" 2 | inside). 3 | 4 | Foo [bar](/url/ Title with "quotes" 5 | inside). 6 | -------------------------------------------------------------------------------- /testdata_result/Markdown Documentation - Basics.txt: -------------------------------------------------------------------------------- 1 | 1 Markdown: Basics 2 | ──────────────────────────────────── 3 | 4 | • [Main](/projects/markdown/) 5 | • [Basics]() 6 | • [Syntax](/projects/markdown/syntax 7 | ) 8 | • [License](/projects/markdown/licen 9 | se) 10 | • [Dingus](/projects/markdown/dingus 11 | ) 12 | 13 | 1.1 Getting the Gist of Markdown's 14 | Formatting Syntax 15 | 16 | This page offers a brief overview of 17 | what it's like to use Markdown. The 18 | [syntax 19 | page](/projects/markdown/syntax 20 | Markdown Syntax) provides complete, 21 | detailed documentation for every 22 | feature, but Markdown should be very 23 | easy to pick up simply by looking 24 | at a few examples of it in action. 25 | The examples on this page are 26 | written in a before/after style, 27 | showing example syntax and the HTML 28 | output produced by Markdown. 29 | 30 | It's also helpful to simply try 31 | Markdown out; the 32 | [Dingus](/projects/markdown/dingus 33 | Markdown Dingus) is a web 34 | application that allows you type 35 | your own Markdown-formatted text and 36 | translate it to XHTML. 37 | 38 | Note: This document is itself 39 | written using Markdown; you can [see 40 | the source for it by adding '.text' 41 | to the URL](/projects/markdown/basi 42 | cs.text). 43 | 44 | 1.2 Paragraphs, Headers, Blockquotes 45 | 46 | A paragraph is simply one or more 47 | consecutive lines of text, separated 48 | by one or more blank lines. (A 49 | blank line is any line that looks 50 | like a blank line -- a line 51 | containing nothing spaces or tabs is 52 | considered blank.) Normal 53 | paragraphs should not be intended 54 | with spaces or tabs. 55 | 56 | Markdown offers two styles of 57 | headers: Setext and atx. 58 | Setext-style headers for 

 and 59 | 

 are created by "underlining" 60 | with equal signs (=) and hyphens 61 | (-), respectively. To create an 62 | atx-style header, you put 1-6 hash 63 | marks (#) at the beginning of the 64 | line -- the number of hashes equals 65 | the resulting HTML header level. 66 | 67 | Blockquotes are indicated using 68 | email-style '>' angle brackets. 69 | 70 | Markdown: 71 | 72 | ┃ A First Level Header 73 | ┃ ==================== 74 | ┃  75 | ┃ A Second Level Header 76 | ┃ --------------------- 77 | ┃  78 | ┃ Now is the time for all good men 79 | ┃ to come to 80 | ┃ the aid of their country. This is 81 | ┃ just a 82 | ┃ regular paragraph. 83 | ┃  84 | ┃ The quick brown fox jumped over 85 | ┃ the lazy 86 | ┃ dog's back. 87 | ┃  88 | ┃ ### Header 3 89 | ┃  90 | ┃ > This is a blockquote. 91 | ┃ > 92 | ┃ > This is the second paragraph in 93 | ┃ the blockquote. 94 | ┃ > 95 | ┃ > ## This is an H2 in a blockquote 96 | 97 | Output: 98 | 99 | ┃ 

A First Level Header

100 | ┃  101 | ┃ 

A Second Level Header

102 | ┃  103 | ┃ 

Now is the time for all good 104 | ┃ men to come to 105 | ┃ the aid of their country. This is 106 | ┃ just a 107 | ┃ regular paragraph.

108 | ┃  109 | ┃ 

The quick brown fox jumped over 110 | ┃ the lazy 111 | ┃ dog's back.

112 | ┃  113 | ┃ 

Header 3

114 | ┃  115 | ┃ 
116 | ┃ 

This is a blockquote.

117 | ┃  118 | ┃ 

This is the second 119 | ┃ paragraph in the blockquote.

120 | ┃  121 | ┃ 

This is an H2 in a 122 | ┃ blockquote

123 | ┃ 
124 | 125 | 1.2.1 Phrase Emphasis 126 | 127 | Markdown uses asterisks and 128 | underscores to indicate spans of 129 | emphasis. 130 | 131 | Markdown: 132 | 133 | ┃ Some of these words *are 134 | ┃ emphasized*. 135 | ┃ Some of these words _are 136 | ┃ emphasized also_. 137 | ┃  138 | ┃ Use two asterisks for **strong 139 | ┃ emphasis**. 140 | ┃ Or, if you prefer, __use two 141 | ┃ underscores instead__. 142 | 143 | Output: 144 | 145 | ┃ 

Some of these words are 146 | ┃ emphasized. 147 | ┃ Some of these words are 148 | ┃ emphasized also.

149 | ┃  150 | ┃ 

Use two asterisks for 151 | ┃ strong emphasis. 152 | ┃ Or, if you prefer, use two 153 | ┃ underscores instead.

154 | 155 | 1.3 Lists 156 | 157 | Unordered (bulleted) lists use 158 | asterisks, pluses, and hyphens (*, 159 | +, and -) as list markers. These 160 | three markers are interchangable; 161 | this: 162 | 163 | ┃ * Candy. 164 | ┃ * Gum. 165 | ┃ * Booze. 166 | 167 | this: 168 | 169 | ┃ + Candy. 170 | ┃ + Gum. 171 | ┃ + Booze. 172 | 173 | and this: 174 | 175 | ┃ - Candy. 176 | ┃ - Gum. 177 | ┃ - Booze. 178 | 179 | all produce the same output: 180 | 181 | ┃ 
    182 | ┃ 
  • Candy.
  • 183 | ┃ 
  • Gum.
  • 184 | ┃ 
  • Booze.
  • 185 | ┃ 
186 | 187 | Ordered (numbered) lists use regular 188 | numbers, followed by periods, as 189 | list markers: 190 | 191 | ┃ 1. Red 192 | ┃ 2. Green 193 | ┃ 3. Blue 194 | 195 | Output: 196 | 197 | ┃ 
    198 | ┃ 
  1. Red
  2. 199 | ┃ 
  3. Green
  4. 200 | ┃ 
  5. Blue
  6. 201 | ┃ 
202 | 203 | If you put blank lines between 204 | items, you'll get 

 tags for the 205 | list item text. You can create 206 | multi-paragraph list items by 207 | indenting the paragraphs by 4 spaces 208 | or 1 tab: 209 | 210 | ┃ * A list item. 211 | ┃  212 | ┃  With multiple paragraphs. 213 | ┃  214 | ┃ * Another item in the list. 215 | 216 | Output: 217 | 218 | ┃ 

    219 | ┃ 
  • A list item.

    220 | ┃ 

    With multiple 221 | ┃ paragraphs.

  • 222 | ┃ 
  • Another item in the 223 | ┃ list.

  • 224 | ┃ 
225 | 226 | 1.3.1 Links 227 | 228 | Markdown supports two styles for 229 | creating links: inline and 230 | reference. With both styles, you use 231 | square brackets to delimit the text 232 | you want to turn into a link. 233 | 234 | Inline-style links use parentheses 235 | immediately after the link text. For 236 | example: 237 | 238 | ┃ This is an [example 239 | ┃ link](http://example.com/). 240 | 241 | Output: 242 | 243 | ┃ 

This is an 245 | ┃ example link.

246 | 247 | Optionally, you may include a title 248 | attribute in the parentheses: 249 | 250 | ┃ This is an [example 251 | ┃ link](http://example.com/ "With a 252 | ┃ Title"). 253 | 254 | Output: 255 | 256 | ┃ 

This is an 259 | ┃ example link.

260 | 261 | Reference-style links allow you to 262 | refer to your links by names, which 263 | you define elsewhere in your 264 | document: 265 | 266 | ┃ I get 10 times more traffic from 267 | ┃ [Google][1] than from 268 | ┃ [Yahoo][2] or [MSN][3]. 269 | ┃  270 | ┃ [1]: http://google.com/ 271 | ┃ "Google" 272 | ┃ [2]: http://search.yahoo.com/ 273 | ┃ "Yahoo Search" 274 | ┃ [3]: http://search.msn.com/ 275 | ┃ "MSN Search" 276 | 277 | Output: 278 | 279 | ┃ 

I get 10 times more traffic 280 | ┃ from Google than 282 | ┃ from Yahoo or 285 | ┃ MSN.

287 | 288 | The title attribute is optional. 289 | Link names may contain letters, 290 | numbers and spaces, but are not case 291 | sensitive: 292 | 293 | ┃ I start my morning with a cup of 294 | ┃ coffee and 295 | ┃ [The New York Times][NY Times]. 296 | ┃  297 | ┃ [ny times]: 298 | ┃ http://www.nytimes.com/ 299 | 300 | Output: 301 | 302 | ┃ 

I start my morning with a cup 303 | ┃ of coffee and 304 | ┃ The 306 | ┃ New York Times.

307 | 308 | 1.3.2 Images 309 | 310 | Image syntax is very much like link 311 | syntax. 312 | 313 | Inline (titles are optional): 314 | 315 | ┃ ![alt text](/path/to/img.jpg 316 | ┃ "Title") 317 | 318 | Reference-style: 319 | 320 | ┃ ![alt text][id] 321 | ┃  322 | ┃ [id]: /path/to/img.jpg "Title" 323 | 324 | Both of the above examples produce 325 | the same output: 326 | 327 | ┃  329 | 330 | 1.3.3 Code 331 | 332 | In a regular paragraph, you can 333 | create code span by wrapping text in 334 | backtick quotes. Any ampersands (&) 335 | and angle brackets (< or >) will 336 | automatically be translated into 337 | HTML entities. This makes it easy to 338 | use Markdown to write about HTML 339 | example code: 340 | 341 | ┃ I strongly recommend against using 342 | ┃ any `` tags. 343 | ┃  344 | ┃ I wish SmartyPants used named 345 | ┃ entities like `—` 346 | ┃ instead of decimal-encoded entites 347 | ┃ like `—`. 348 | 349 | Output: 350 | 351 | ┃ 

I strongly recommend against 352 | ┃ using any 353 | ┃ <blink> 354 | ┃ tags.

355 | ┃  356 | ┃ 

I wish SmartyPants used named 357 | ┃ entities like 358 | ┃ &mdash; instead 359 | ┃ of decimal-encoded 360 | ┃ entites like 361 | ┃ &#8212;.

362 | 363 | To specify an entire block of 364 | pre-formatted code, indent every 365 | line of the block by 4 spaces or 1 366 | tab. Just like with code spans, &, 367 | <, and > characters will be escaped 368 | automatically. 369 | 370 | Markdown: 371 | 372 | ┃ If you want your page to validate 373 | ┃ under XHTML 1.0 Strict, 374 | ┃ you've got to put paragraph tags 375 | ┃ in your blockquotes: 376 | ┃  377 | ┃ 
378 | ┃ 

For example.

379 | ┃ 
380 | 381 | Output: 382 | 383 | ┃ 

If you want your page to 384 | ┃ validate under XHTML 1.0 Strict, 385 | ┃ you've got to put paragraph tags 386 | ┃ in your blockquotes:

387 | ┃  388 | ┃ 
<blockquote>
389 |     ┃     <p>For
390 |     ┃ example.</p>
391 |     ┃ </blockquote>
392 |     ┃ 
393 | 394 | -------------------------------------------------------------------------------- /testdata_result/Markdown Documentation - Syntax.txt: -------------------------------------------------------------------------------- 1 | 1 Markdown: Syntax 2 | ──────────────────────────────────── 3 | 4 | • [Main](/projects/markdown/) 5 | • [Basics](/projects/markdown/basics 6 | ) 7 | • [Syntax]() 8 | • [License](/projects/markdown/licen 9 | se) 10 | • [Dingus](/projects/markdown/dingus 11 | ) 12 | 13 | • [Overview](#overview) 14 | • [Philosophy](#philosophy) 15 | • [Inline HTML](#html) 16 | • [Automatic Escaping for Special 17 | Characters](#autoescape) 18 | • [Block Elements](#block) 19 | • [Paragraphs and Line Breaks](#p) 20 | • [Headers](#header) 21 | • [Blockquotes](#blockquote) 22 | • [Lists](#list) 23 | • [Code Blocks](#precode) 24 | • [Horizontal Rules](#hr) 25 | • [Span Elements](#span) 26 | • [Links](#link) 27 | • [Emphasis](#em) 28 | • [Code](#code) 29 | • [Images](#img) 30 | • [Miscellaneous](#misc) 31 | • [Backslash Escapes](#backslash) 32 | • [Automatic Links](#autolink) 33 | 34 | Note: This document is itself 35 | written using Markdown; you can [see 36 | the source for it by adding '.text' 37 | to the URL](/projects/markdown/synt 38 | ax.text). 39 | 40 | ──────────────────────────────────── 41 | 42 | 1.1 Overview 43 | 44 | 1.1.1 Philosophy 45 | 46 | Markdown is intended to be as 47 | easy-to-read and easy-to-write as is 48 | feasible. 49 | 50 | Readability, however, is emphasized 51 | above all else. A Markdown-formatted 52 | document should be publishable 53 | as-is, as plain text, without 54 | looking like it's been marked up 55 | with tags or formatting 56 | instructions. While Markdown's 57 | syntax has been influenced by 58 | several existing text-to-HTML 59 | filters -- including [Setext](http:/ 60 | /docutils.sourceforge.net/mirror/set 61 | ext.html), [atx](http://www.aaronsw. 62 | com/2002/atx/), [Textile](http://tex 63 | tism.com/tools/textile/), [reStructu 64 | redText](http://docutils.sourceforge 65 | .net/rst.html), [Grutatext](http://w 66 | ww.triptico.com/software/grutatxt.ht 67 | ml), and [EtText](http://ettext.tain 68 | t.org/doc/) -- the single biggest 69 | source of inspiration for Markdown's 70 | syntax is the format of plain text 71 | email. 72 | 73 | To this end, Markdown's syntax is 74 | comprised entirely of punctuation 75 | characters, which punctuation 76 | characters have been carefully 77 | chosen so as to look like what they 78 | mean. E.g., asterisks around a word 79 | actually look like *emphasis*. 80 | Markdown lists look like, well, 81 | lists. Even blockquotes look like 82 | quoted passages of text, assuming 83 | you've ever used email. 84 | 85 | 1.1.2 Inline HTML 86 | 87 | Markdown's syntax is intended for 88 | one purpose: to be used as a format 89 | for writing for the web. 90 | 91 | Markdown is not a replacement for 92 | HTML, or even close to it. Its 93 | syntax is very small, corresponding 94 | only to a very small subset of HTML 95 | tags. The idea is not to create a 96 | syntax that makes it easier to 97 | insert HTML tags. In my opinion, 98 | HTML tags are already easy to 99 | insert. The idea for Markdown is to 100 | make it easy to read, write, and 101 | edit prose. HTML is a publishing 102 | format; Markdown is a writing 103 | format. Thus, Markdown's formatting 104 | syntax only addresses issues that 105 | can be conveyed in plain text. 106 | 107 | For any markup that is not covered 108 | by Markdown's syntax, you simply use 109 | HTML itself. There's no need to 110 | preface it or delimit it to indicate 111 | that you're switching from Markdown 112 | to HTML; you just use the tags. 113 | 114 | The only restrictions are that 115 | block-level HTML elements -- e.g. 116 | 
, , 
, 

, etc. -- 117 | must be separated from surrounding 118 | content by blank lines, and the 119 | start and end tags of the block 120 | should not be indented with tabs or 121 | spaces. Markdown is smart enough not 122 | to add extra (unwanted) 

 tags 123 | around HTML block-level tags. 124 | 125 | For example, to add an HTML table to 126 | a Markdown article: 127 | 128 | ┃ This is a regular paragraph. 129 | ┃  130 | ┃ 

131 | ┃  132 | ┃  133 | ┃  134 | ┃ 
Foo
135 | ┃  136 | ┃ This is another regular paragraph. 137 | 138 | Note that Markdown formatting syntax 139 | is not processed within block-level 140 | HTML tags. E.g., you can't use 141 | Markdown-style *emphasis* inside an 142 | HTML block. 143 | 144 | Span-level HTML tags -- e.g. , 145 | , or  -- can be used 146 | anywhere in a Markdown paragraph, 147 | list item, or header. If you want, 148 | you can even use HTML tags instead 149 | of Markdown formatting; e.g. if 150 | you'd prefer to use HTML  or 151 |  tags instead of Markdown's 152 | link or image syntax, go right 153 | ahead. 154 | 155 | Unlike block-level HTML tags, 156 | Markdown syntax is processed within 157 | span-level tags. 158 | 159 | 1.1.3 Automatic Escaping for Special 160 | Characters 161 | 162 | In HTML, there are two characters 163 | that demand special treatment: < and 164 | &. Left angle brackets are used to 165 | start tags; ampersands are used to 166 | denote HTML entities. If you want to 167 | use them as literal characters, you 168 | must escape them as entities, e.g. 169 | <, and &. 170 | 171 | Ampersands in particular are 172 | bedeviling for web writers. If you 173 | want to write about 'AT&T', you need 174 | to write 'AT&T'. You even need 175 | to escape ampersands within URLs. 176 | Thus, if you want to link to: 177 | 178 | ┃ http://images.google.com/images?nu 179 | ┃ m=30&q=larry+bird 180 | 181 | you need to encode the URL as: 182 | 183 | ┃ http://images.google.com/images?nu 184 | ┃ m=30&q=larry+bird 185 | 186 | in your anchor tag href attribute. 187 | Needless to say, this is easy to 188 | forget, and is probably the single 189 | most common source of HTML 190 | validation errors in otherwise 191 | well-marked-up web sites. 192 | 193 | Markdown allows you to use these 194 | characters naturally, taking care of 195 | all the necessary escaping for you. 196 | If you use an ampersand as part of 197 | an HTML entity, it remains 198 | unchanged; otherwise it will be 199 | translated into &. 200 | 201 | So, if you want to include a 202 | copyright symbol in your article, 203 | you can write: 204 | 205 | ┃ © 206 | 207 | and Markdown will leave it alone. 208 | But if you write: 209 | 210 | ┃ AT&T 211 | 212 | Markdown will translate it to: 213 | 214 | ┃ AT&T 215 | 216 | Similarly, because Markdown supports 217 | [inline HTML](#html), if you use 218 | angle brackets as delimiters for 219 | HTML tags, Markdown will treat them 220 | as such. But if you write: 221 | 222 | ┃ 4 < 5 223 | 224 | Markdown will translate it to: 225 | 226 | ┃ 4 < 5 227 | 228 | However, inside Markdown code spans 229 | and blocks, angle brackets and 230 | ampersands are always encoded 231 | automatically. This makes it easy to 232 | use Markdown to write about HTML 233 | code. (As opposed to raw HTML, which 234 | is a terrible format for writing 235 | about HTML syntax, because every 236 | single < and & in your example code 237 | needs to be escaped.) 238 | 239 | ──────────────────────────────────── 240 | 241 | 1.2 Block Elements 242 | 243 | 1.2.1 Paragraphs and Line Breaks 244 | 245 | A paragraph is simply one or more 246 | consecutive lines of text, separated 247 | by one or more blank lines. (A 248 | blank line is any line that looks 249 | like a blank line -- a line 250 | containing nothing but spaces or 251 | tabs is considered blank.) Normal 252 | paragraphs should not be intended 253 | with spaces or tabs. 254 | 255 | The implication of the "one or more 256 | consecutive lines of text" rule is 257 | that Markdown supports 258 | "hard-wrapped" text paragraphs. This 259 | differs significantly from most 260 | other text-to-HTML formatters 261 | (including Movable Type's "Convert 262 | Line Breaks" option) which translate 263 | every line break character in a 264 | paragraph into a 
 tag. 265 | 266 | When you do want to insert a 
 267 | break tag using Markdown, you end a 268 | line with two or more spaces, then 269 | type return. 270 | 271 | Yes, this takes a tad more effort to 272 | create a 
, but a simplistic 273 | "every line break is a 
" rule 274 | wouldn't work for Markdown. 275 | Markdown's email-style 276 | [blockquoting](#blockquote) and 277 | multi-paragraph [list items](#list) 278 | work best -- and look better -- when 279 | you format them with hard breaks. 280 | 281 | 1.2.2 Headers 282 | 283 | Markdown supports two styles of 284 | headers, [Setext](http://docutils.so 285 | urceforge.net/mirror/setext.html) 286 | and [atx](http://www.aaronsw.com/200 287 | 2/atx/). 288 | 289 | Setext-style headers are 290 | "underlined" using equal signs (for 291 | first-level headers) and dashes (for 292 | second-level headers). For example: 293 | 294 | ┃ This is an H1 295 | ┃ ============= 296 | ┃  297 | ┃ This is an H2 298 | ┃ ------------- 299 | 300 | Any number of underlining ='s or -'s 301 | will work. 302 | 303 | Atx-style headers use 1-6 hash 304 | characters at the start of the line, 305 | corresponding to header levels 1-6. 306 | For example: 307 | 308 | ┃ # This is an H1 309 | ┃  310 | ┃ ## This is an H2 311 | ┃  312 | ┃ ###### This is an H6 313 | 314 | Optionally, you may "close" 315 | atx-style headers. This is purely 316 | cosmetic -- you can use this if you 317 | think it looks better. The closing 318 | hashes don't even need to match the 319 | number of hashes used to open the 320 | header. (The number of opening 321 | hashes determines the header level.) 322 | : 323 | 324 | ┃ # This is an H1 # 325 | ┃  326 | ┃ ## This is an H2 ## 327 | ┃  328 | ┃ ### This is an H3 ###### 329 | 330 | 1.2.3 Blockquotes 331 | 332 | Markdown uses email-style > 333 | characters for blockquoting. If 334 | you're familiar with quoting 335 | passages of text in an email 336 | message, then you know how to create 337 | a blockquote in Markdown. It looks 338 | best if you hard wrap the text and 339 | put a > before every line: 340 | 341 | ┃ > This is a blockquote with two 342 | ┃ paragraphs. Lorem ipsum dolor sit 343 | ┃ amet, 344 | ┃ > consectetuer adipiscing elit. 345 | ┃ Aliquam hendrerit mi posuere 346 | ┃ lectus. 347 | ┃ > Vestibulum enim wisi, viverra 348 | ┃ nec, fringilla in, laoreet vitae, 349 | ┃ risus. 350 | ┃ > 351 | ┃ > Donec sit amet nisl. Aliquam 352 | ┃ semper ipsum sit amet velit. 353 | ┃ Suspendisse 354 | ┃ > id sem consectetuer libero 355 | ┃ luctus adipiscing. 356 | 357 | Markdown allows you to be lazy and 358 | only put the > before the first line 359 | of a hard-wrapped paragraph: 360 | 361 | ┃ > This is a blockquote with two 362 | ┃ paragraphs. Lorem ipsum dolor sit 363 | ┃ amet, 364 | ┃ consectetuer adipiscing elit. 365 | ┃ Aliquam hendrerit mi posuere 366 | ┃ lectus. 367 | ┃ Vestibulum enim wisi, viverra nec, 368 | ┃ fringilla in, laoreet vitae, 369 | ┃ risus. 370 | ┃  371 | ┃ > Donec sit amet nisl. Aliquam 372 | ┃ semper ipsum sit amet velit. 373 | ┃ Suspendisse 374 | ┃ id sem consectetuer libero luctus 375 | ┃ adipiscing. 376 | 377 | Blockquotes can be nested (i.e. a 378 | blockquote-in-a-blockquote) by 379 | adding additional levels of >: 380 | 381 | ┃ > This is the first level of 382 | ┃ quoting. 383 | ┃ > 384 | ┃ > > This is nested blockquote. 385 | ┃ > 386 | ┃ > Back to the first level. 387 | 388 | Blockquotes can contain other 389 | Markdown elements, including 390 | headers, lists, and code blocks: 391 | 392 | ┃ > ## This is a header. 393 | ┃ > 394 | ┃ > 1. This is the first list 395 | ┃ item. 396 | ┃ > 2. This is the second list 397 | ┃ item. 398 | ┃ > 399 | ┃ > Here's some example code: 400 | ┃ > 401 | ┃ > return shell_exec("echo 402 | ┃ $input | $markdown_script"); 403 | 404 | Any decent text editor should make 405 | email-style quoting easy. For 406 | example, with BBEdit, you can make a 407 | selection and choose Increase Quote 408 | Level from the Text menu. 409 | 410 | 1.2.4 Lists 411 | 412 | Markdown supports ordered (numbered) 413 | and unordered (bulleted) lists. 414 | 415 | Unordered lists use asterisks, 416 | pluses, and hyphens -- 417 | interchangably -- as list markers: 418 | 419 | ┃ * Red 420 | ┃ * Green 421 | ┃ * Blue 422 | 423 | is equivalent to: 424 | 425 | ┃ + Red 426 | ┃ + Green 427 | ┃ + Blue 428 | 429 | and: 430 | 431 | ┃ - Red 432 | ┃ - Green 433 | ┃ - Blue 434 | 435 | Ordered lists use numbers followed 436 | by periods: 437 | 438 | ┃ 1. Bird 439 | ┃ 2. McHale 440 | ┃ 3. Parish 441 | 442 | It's important to note that the 443 | actual numbers you use to mark the 444 | list have no effect on the HTML 445 | output Markdown produces. The HTML 446 | Markdown produces from the above 447 | list is: 448 | 449 | ┃ 
    450 | ┃ 
  1. Bird
  2. 451 | ┃ 
  3. McHale
  4. 452 | ┃ 
  5. Parish
  6. 453 | ┃ 
454 | 455 | If you instead wrote the list in 456 | Markdown like this: 457 | 458 | ┃ 1. Bird 459 | ┃ 1. McHale 460 | ┃ 1. Parish 461 | 462 | or even: 463 | 464 | ┃ 3. Bird 465 | ┃ 1. McHale 466 | ┃ 8. Parish 467 | 468 | you'd get the exact same HTML 469 | output. The point is, if you want 470 | to, you can use ordinal numbers in 471 | your ordered Markdown lists, so that 472 | the numbers in your source match 473 | the numbers in your published HTML. 474 | But if you want to be lazy, you 475 | don't have to. 476 | 477 | If you do use lazy list numbering, 478 | however, you should still start the 479 | list with the number 1. At some 480 | point in the future, Markdown may 481 | support starting ordered lists at an 482 | arbitrary number. 483 | 484 | List markers typically start at the 485 | left margin, but may be indented by 486 | up to three spaces. List markers 487 | must be followed by one or more 488 | spaces or a tab. 489 | 490 | To make lists look nice, you can 491 | wrap items with hanging indents: 492 | 493 | ┃ * Lorem ipsum dolor sit amet, 494 | ┃ consectetuer adipiscing elit. 495 | ┃  Aliquam hendrerit mi posuere 496 | ┃ lectus. Vestibulum enim wisi, 497 | ┃  viverra nec, fringilla in, 498 | ┃ laoreet vitae, risus. 499 | ┃ * Donec sit amet nisl. Aliquam 500 | ┃ semper ipsum sit amet velit. 501 | ┃  Suspendisse id sem 502 | ┃ consectetuer libero luctus 503 | ┃ adipiscing. 504 | 505 | But if you want to be lazy, you 506 | don't have to: 507 | 508 | ┃ * Lorem ipsum dolor sit amet, 509 | ┃ consectetuer adipiscing elit. 510 | ┃ Aliquam hendrerit mi posuere 511 | ┃ lectus. Vestibulum enim wisi, 512 | ┃ viverra nec, fringilla in, laoreet 513 | ┃ vitae, risus. 514 | ┃ * Donec sit amet nisl. Aliquam 515 | ┃ semper ipsum sit amet velit. 516 | ┃ Suspendisse id sem consectetuer 517 | ┃ libero luctus adipiscing. 518 | 519 | If list items are separated by blank 520 | lines, Markdown will wrap the items 521 | in 

 tags in the HTML output. For 522 | example, this input: 523 | 524 | ┃ * Bird 525 | ┃ * Magic 526 | 527 | will turn into: 528 | 529 | ┃ 

    530 | ┃ 
  • Bird
  • 531 | ┃ 
  • Magic
  • 532 | ┃ 
533 | 534 | But this: 535 | 536 | ┃ * Bird 537 | ┃  538 | ┃ * Magic 539 | 540 | will turn into: 541 | 542 | ┃ 
    543 | ┃ 
  • Bird

  • 544 | ┃ 
  • Magic

  • 545 | ┃ 
546 | 547 | List items may consist of multiple 548 | paragraphs. Each subsequent 549 | paragraph in a list item must be 550 | intended by either 4 spaces or one 551 | tab: 552 | 553 | ┃ 1. This is a list item with two 554 | ┃ paragraphs. Lorem ipsum dolor 555 | ┃  sit amet, consectetuer 556 | ┃ adipiscing elit. Aliquam hendrerit 557 | ┃  mi posuere lectus. 558 | ┃  559 | ┃  Vestibulum enim wisi, viverra 560 | ┃ nec, fringilla in, laoreet 561 | ┃  vitae, risus. Donec sit amet 562 | ┃ nisl. Aliquam semper ipsum 563 | ┃  sit amet velit. 564 | ┃  565 | ┃ 2. Suspendisse id sem 566 | ┃ consectetuer libero luctus 567 | ┃ adipiscing. 568 | 569 | It looks nice if you indent every 570 | line of the subsequent paragraphs, 571 | but here again, Markdown will allow 572 | you to be lazy: 573 | 574 | ┃ * This is a list item with two 575 | ┃ paragraphs. 576 | ┃  577 | ┃  This is the second paragraph 578 | ┃ in the list item. You're 579 | ┃ only required to indent the first 580 | ┃ line. Lorem ipsum dolor 581 | ┃ sit amet, consectetuer adipiscing 582 | ┃ elit. 583 | ┃  584 | ┃ * Another item in the same list. 585 | 586 | To put a blockquote within a list 587 | item, the blockquote's > delimiters 588 | need to be indented: 589 | 590 | ┃ * A list item with a blockquote: 591 | ┃  592 | ┃  > This is a blockquote 593 | ┃  > inside a list item. 594 | 595 | To put a code block within a list 596 | item, the code block needs to be 597 | indented twice -- 8 spaces or two 598 | tabs: 599 | 600 | ┃ * A list item with a code block: 601 | ┃  602 | ┃  603 | 604 | It's worth noting that it's possible 605 | to trigger an ordered list by 606 | accident, by writing something like 607 | this: 608 | 609 | ┃ 1986. What a great season. 610 | 611 | In other words, a 612 | number-period-space sequence at the 613 | beginning of a line. To avoid this, 614 | you can backslash-escape the period: 615 | 616 | ┃ 1986\. What a great season. 617 | 618 | 1.2.5 Code Blocks 619 | 620 | Pre-formatted code blocks are used 621 | for writing about programming or 622 | markup source code. Rather than 623 | forming normal paragraphs, the lines 624 | of a code block are interpreted 625 | literally. Markdown wraps a code 626 | block in both 
 and  tags.
 627 | 
 628 |     To produce a code block in Markdown,
 629 |     simply indent every line of the
 630 |     block by at least 4 spaces or 1 tab.
 631 |     For example, given this input:
 632 | 
 633 |     ┃ This is a normal paragraph:
 634 |     ┃ 
 635 |     ┃     This is a code block.
 636 | 
 637 |     Markdown will generate:
 638 | 
 639 |     ┃ 

This is a normal paragraph:

640 | ┃  641 | ┃ 
This is a code block.
 642 |     ┃ 
643 | 644 | One level of indentation -- 4 spaces 645 | or 1 tab -- is removed from each 646 | line of the code block. For example, 647 | this: 648 | 649 | ┃ Here is an example of AppleScript: 650 | ┃  651 | ┃  tell application "Foo" 652 | ┃  beep 653 | ┃  end tell 654 | 655 | will turn into: 656 | 657 | ┃ 

Here is an example of 658 | ┃ AppleScript:

659 | ┃  660 | ┃ 
tell application "Foo"
 661 |     ┃     beep
 662 |     ┃ end tell
 663 |     ┃ 
664 | 665 | A code block continues until it 666 | reaches a line that is not indented 667 | (or the end of the article). 668 | 669 | Within a code block, ampersands (&) 670 | and angle brackets (< and >) are 671 | automatically converted into HTML 672 | entities. This makes it very easy to 673 | include example HTML source code 674 | using Markdown -- just paste it and 675 | indent it, and Markdown will handle 676 | the hassle of encoding the 677 | ampersands and angle brackets. For 678 | example, this: 679 | 680 | ┃  684 | 685 | will turn into: 686 | 687 | ┃ 
<div
 688 |     ┃ class="footer">
 689 |     ┃     &copy; 2004 Foo
 690 |     ┃ Corporation
 691 |     ┃ </div>
 692 |     ┃ 
693 | 694 | Regular Markdown syntax is not 695 | processed within code blocks. E.g., 696 | asterisks are just literal asterisks 697 | within a code block. This means 698 | it's also easy to use Markdown to 699 | write about Markdown's own syntax. 700 | 701 | 1.2.6 Horizontal Rules 702 | 703 | You can produce a horizontal rule 704 | tag (
) by placing three or 705 | more hyphens, asterisks, or 706 | underscores on a line by themselves. 707 | If you wish, you may use spaces 708 | between the hyphens or asterisks. 709 | Each of the following lines will 710 | produce a horizontal rule: 711 | 712 | ┃ * * * 713 | ┃  714 | ┃ *** 715 | ┃  716 | ┃ ***** 717 | ┃  718 | ┃ - - - 719 | ┃  720 | ┃ ---------------------------------- 721 | ┃ ----- 722 | ┃  723 | ┃ _ _ _ 724 | 725 | ──────────────────────────────────── 726 | 727 | 1.3 Span Elements 728 | 729 | 1.3.1 Links 730 | 731 | Markdown supports two style of 732 | links: inline and reference. 733 | 734 | In both styles, the link text is 735 | delimited by [square brackets]. 736 | 737 | To create an inline link, use a set 738 | of regular parentheses immediately 739 | after the link text's closing square 740 | bracket. Inside the parentheses, 741 | put the URL where you want the link 742 | to point, along with an optional 743 | title for the link, surrounded in 744 | quotes. For example: 745 | 746 | ┃ This is [an 747 | ┃ example](http://example.com/ 748 | ┃ "Title") inline link. 749 | ┃  750 | ┃ [This link](http://example.net/) 751 | ┃ has no title attribute. 752 | 753 | Will produce: 754 | 755 | ┃ 

This is 758 | ┃ an example inline link.

759 | ┃  760 | ┃ 

This 762 | ┃ link has no 763 | ┃ title attribute.

764 | 765 | If you're referring to a local 766 | resource on the same server, you can 767 | use relative paths: 768 | 769 | ┃ See my [About](/about/) page for 770 | ┃ details. 771 | 772 | Reference-style links use a second 773 | set of square brackets, inside which 774 | you place a label of your choosing 775 | to identify the link: 776 | 777 | ┃ This is [an example][id] 778 | ┃ reference-style link. 779 | 780 | You can optionally use a space to 781 | separate the sets of brackets: 782 | 783 | ┃ This is [an example] [id] 784 | ┃ reference-style link. 785 | 786 | Then, anywhere in the document, you 787 | define your link label like this, on 788 | a line by itself: 789 | 790 | ┃ [id]: http://example.com/ 791 | ┃ "Optional Title Here" 792 | 793 | That is: 794 | • Square brackets containing the 795 | link identifier (optionally 796 | indented from the left margin 797 | using up to three spaces); 798 | • followed by a colon; 799 | • followed by one or more spaces (or 800 | tabs); 801 | • followed by the URL for the link; 802 | • optionally followed by a title 803 | attribute for the link, enclosed 804 | in double or single quotes. 805 | 806 | The link URL may, optionally, be 807 | surrounded by angle brackets: 808 | 809 | ┃ [id]: 810 | ┃ "Optional Title Here" 811 | 812 | You can put the title attribute on 813 | the next line and use extra spaces 814 | or tabs for padding, which tends to 815 | look better with longer URLs: 816 | 817 | ┃ [id]: http://example.com/longish/p 818 | ┃ ath/to/resource/here 819 | ┃  "Optional Title Here" 820 | 821 | Link definitions are only used for 822 | creating links during Markdown 823 | processing, and are stripped from 824 | your document in the HTML output. 825 | 826 | Link definition names may constist 827 | of letters, numbers, spaces, and 828 | punctuation -- but they are not case 829 | sensitive. E.g. these two links: 830 | 831 | ┃ [link text][a] 832 | ┃ [link text][A] 833 | 834 | are equivalent. 835 | 836 | The implicit link name shortcut 837 | allows you to omit the name of the 838 | link, in which case the link text 839 | itself is used as the name. Just use 840 | an empty set of square brackets -- 841 | e.g., to link the word "Google" to 842 | the google.com web site, you could 843 | simply write: 844 | 845 | ┃ [Google][] 846 | 847 | And then define the link: 848 | 849 | ┃ [Google]: http://google.com/ 850 | 851 | Because link names may contain 852 | spaces, this shortcut even works for 853 | multiple words in the link text: 854 | 855 | ┃ Visit [Daring Fireball][] for more 856 | ┃ information. 857 | 858 | And then define the link: 859 | 860 | ┃ [Daring Fireball]: 861 | ┃ http://daringfireball.net/ 862 | 863 | Link definitions can be placed 864 | anywhere in your Markdown document. 865 | I tend to put them immediately after 866 | each paragraph in which they're 867 | used, but if you want, you can put 868 | them all at the end of your 869 | document, sort of like footnotes. 870 | 871 | Here's an example of reference links 872 | in action: 873 | 874 | ┃ I get 10 times more traffic from 875 | ┃ [Google] [1] than from 876 | ┃ [Yahoo] [2] or [MSN] [3]. 877 | ┃  878 | ┃  [1]: http://google.com/ 879 | ┃ "Google" 880 | ┃  [2]: http://search.yahoo.com/ 881 | ┃ "Yahoo Search" 882 | ┃  [3]: http://search.msn.com/ 883 | ┃ "MSN Search" 884 | 885 | Using the implicit link name 886 | shortcut, you could instead write: 887 | 888 | ┃ I get 10 times more traffic from 889 | ┃ [Google][] than from 890 | ┃ [Yahoo][] or [MSN][]. 891 | ┃  892 | ┃  [google]: http://google.com/ 893 | ┃ "Google" 894 | ┃  [yahoo]: 895 | ┃ http://search.yahoo.com/ "Yahoo 896 | ┃ Search" 897 | ┃  [msn]: http://search.msn.com/ 898 | ┃ "MSN Search" 899 | 900 | Both of the above examples will 901 | produce the following HTML output: 902 | 903 | ┃ 

I get 10 times more traffic 904 | ┃ from Google than 906 | ┃ from 907 | ┃ Yahoo 909 | ┃ or MSN.

912 | 913 | For comparison, here is the same 914 | paragraph written using Markdown's 915 | inline link style: 916 | 917 | ┃ I get 10 times more traffic from 918 | ┃ [Google](http://google.com/ 919 | ┃ "Google") 920 | ┃ than from 921 | ┃ [Yahoo](http://search.yahoo.com/ 922 | ┃ "Yahoo Search") or 923 | ┃ [MSN](http://search.msn.com/ "MSN 924 | ┃ Search"). 925 | 926 | The point of reference-style links 927 | is not that they're easier to write. 928 | The point is that with 929 | reference-style links, your document 930 | source is vastly more readable. 931 | Compare the above examples: using 932 | reference-style links, the paragraph 933 | itself is only 81 characters long; 934 | with inline-style links, it's 176 935 | characters; and as raw HTML, it's 936 | 234 characters. In the raw HTML, 937 | there's more markup than there is 938 | text. 939 | 940 | With Markdown's reference-style 941 | links, a source document much more 942 | closely resembles the final output, 943 | as rendered in a browser. By 944 | allowing you to move the 945 | markup-related metadata out of the 946 | paragraph, you can add links without 947 | interrupting the narrative flow of 948 | your prose. 949 | 950 | 1.3.2 Emphasis 951 | 952 | Markdown treats asterisks (*) and 953 | underscores (_) as indicators of 954 | emphasis. Text wrapped with one * or 955 | _ will be wrapped with an HTML  956 |  tag; double *'s or _'s will be 957 | wrapped with an HTML  tag. 958 | E.g., this input: 959 | 960 | ┃ *single asterisks* 961 | ┃  962 | ┃ _single underscores_ 963 | ┃  964 | ┃ **double asterisks** 965 | ┃  966 | ┃ __double underscores__ 967 | 968 | will produce: 969 | 970 | ┃ single asterisks 971 | ┃  972 | ┃ single underscores 973 | ┃  974 | ┃ double asterisks 975 | ┃  976 | ┃ double 977 | ┃ underscores 978 | 979 | You can use whichever style you 980 | prefer; the lone restriction is that 981 | the same character must be used to 982 | open and close an emphasis span. 983 | 984 | Emphasis can be used in the middle 985 | of a word: 986 | 987 | ┃ un*fucking*believable 988 | 989 | But if you surround an * or _ with 990 | spaces, it'll be treated as a 991 | literal asterisk or underscore. 992 | 993 | To produce a literal asterisk or 994 | underscore at a position where it 995 | would otherwise be used as an 996 | emphasis delimiter, you can 997 | backslash escape it: 998 | 999 | ┃ \*this text is surrounded by 1000 | ┃ literal asterisks\* 1001 | 1002 | 1.3.3 Code 1003 | 1004 | To indicate a span of code, wrap it 1005 | with backtick quotes (`). Unlike a 1006 | pre-formatted code block, a code 1007 | span indicates code within a normal 1008 | paragraph. For example: 1009 | 1010 | ┃ Use the `printf()` function. 1011 | 1012 | will produce: 1013 | 1014 | ┃ 

Use the printf() 1015 | ┃ function.

1016 | 1017 | To include a literal backtick 1018 | character within a code span, you 1019 | can use multiple backticks as the 1020 | opening and closing delimiters: 1021 | 1022 | ┃ ``There is a literal backtick (`) 1023 | ┃ here.`` 1024 | 1025 | which will produce this: 1026 | 1027 | ┃ 

There is a literal 1028 | ┃ backtick (`) here.

1029 | 1030 | The backtick delimiters surrounding 1031 | a code span may include spaces -- 1032 | one after the opening, one before 1033 | the closing. This allows you to 1034 | place literal backtick characters at 1035 | the beginning or end of a code 1036 | span: 1037 | 1038 | ┃ A single backtick in a code span: 1039 | ┃ `` ` `` 1040 | ┃  1041 | ┃ A backtick-delimited string in a 1042 | ┃ code span: `` `foo` `` 1043 | 1044 | will produce: 1045 | 1046 | ┃ 

A single backtick in a code 1047 | ┃ span: `

1048 | ┃  1049 | ┃ 

A backtick-delimited string in 1050 | ┃ a code span: 1051 | ┃ `foo`

1052 | 1053 | With a code span, ampersands and 1054 | angle brackets are encoded as HTML 1055 | entities automatically, which makes 1056 | it easy to include example HTML 1057 | tags. Markdown will turn this: 1058 | 1059 | ┃ Please don't use any `` 1060 | ┃ tags. 1061 | 1062 | into: 1063 | 1064 | ┃ 

Please don't use any 1065 | ┃ <blink> 1066 | ┃ tags.

1067 | 1068 | You can write this: 1069 | 1070 | ┃ `—` is the decimal-encoded 1071 | ┃ equivalent of `—`. 1072 | 1073 | to produce: 1074 | 1075 | ┃ 

&#8212; is the 1076 | ┃ decimal-encoded 1077 | ┃ equivalent of 1078 | ┃ &mdash;.

1079 | 1080 | 1.3.4 Images 1081 | 1082 | Admittedly, it's fairly difficult to 1083 | devise a "natural" syntax for 1084 | placing images into a plain text 1085 | document format. 1086 | 1087 | Markdown uses an image syntax that 1088 | is intended to resemble the syntax 1089 | for links, allowing for two styles: 1090 | inline and reference. 1091 | 1092 | Inline image syntax looks like this: 1093 | 1094 | ┃ ![Alt text](/path/to/img.jpg) 1095 | ┃  1096 | ┃ ![Alt text](/path/to/img.jpg 1097 | ┃ "Optional title") 1098 | 1099 | That is: 1100 | • An exclamation mark: !; 1101 | • followed by a set of square 1102 | brackets, containing the alt 1103 | attribute text for the image; 1104 | • followed by a set of parentheses, 1105 | containing the URL or path to the 1106 | image, and an optional title 1107 | attribute enclosed in double or 1108 | single quotes. 1109 | 1110 | Reference-style image syntax looks 1111 | like this: 1112 | 1113 | ┃ ![Alt text][id] 1114 | 1115 | Where "id" is the name of a defined 1116 | image reference. Image references 1117 | are defined using syntax identical 1118 | to link references: 1119 | 1120 | ┃ [id]: url/to/image "Optional 1121 | ┃ title attribute" 1122 | 1123 | As of this writing, Markdown has no 1124 | syntax for specifying the dimensions 1125 | of an image; if this is important 1126 | to you, you can simply use regular 1127 | HTML  tags. 1128 | 1129 | ──────────────────────────────────── 1130 | 1131 | 1.4 Miscellaneous 1132 | 1133 | 1.4.1 Automatic Links 1134 | 1135 | Markdown supports a shortcut style 1136 | for creating "automatic" links for 1137 | URLs and email addresses: simply 1138 | surround the URL or email address 1139 | with angle brackets. What this means 1140 | is that if you want to show the 1141 | actual text of a URL or email 1142 | address, and also have it be a 1143 | clickable link, you can do this: 1144 | 1145 | ┃  1146 | 1147 | Markdown will turn this into: 1148 | 1149 | ┃ http 1150 | ┃ ://example.com/ 1151 | 1152 | Automatic links for email addresses 1153 | work similarly, except that Markdown 1154 | will also perform a bit of 1155 | randomized decimal and hex 1156 | entity-encoding to help obscure your 1157 | address from address-harvesting 1158 | spambots. For example, Markdown will 1159 | turn this: 1160 | 1161 | ┃  1162 | 1163 | into something like this: 1164 | 1165 | ┃ addr&# 1172 | ┃ x65;ss@ex& 1173 | ┃ #x61; 1174 | ┃ mple.c 1175 | ┃ 1;m 1176 | 1177 | which will render in a browser as a 1178 | clickable link to 1179 | "address@example.com". 1180 | 1181 | (This sort of entity-encoding trick 1182 | will indeed fool many, if not most, 1183 | address-harvesting bots, but it 1184 | definitely won't fool all of them. 1185 | It's better than nothing, but an 1186 | address published in this way will 1187 | probably eventually start receiving 1188 | spam.) 1189 | 1190 | 1.4.2 Backslash Escapes 1191 | 1192 | Markdown allows you to use backslash 1193 | escapes to generate literal 1194 | characters which would otherwise 1195 | have special meaning in Markdown's 1196 | formatting syntax. For example, if 1197 | you wanted to surround a word with 1198 | literal asterisks (instead of an 1199 | HTML  tag), you can backslashes 1200 | before the asterisks, like this: 1201 | 1202 | ┃ \*literal asterisks\* 1203 | 1204 | Markdown provides backslash escapes 1205 | for the following characters: 1206 | 1207 | ┃ \ backslash 1208 | ┃ ` backtick 1209 | ┃ * asterisk 1210 | ┃ _ underscore 1211 | ┃ {} curly braces 1212 | ┃ [] square brackets 1213 | ┃ () parentheses 1214 | ┃ # hash mark 1215 | ┃ + plus sign 1216 | ┃ - minus sign (hyphen) 1217 | ┃ . dot 1218 | ┃ ! exclamation mark 1219 | 1220 | -------------------------------------------------------------------------------- /testdata_result/Nested blockquotes.txt: -------------------------------------------------------------------------------- 1 | ┃ foo 2 | ┃ ┃ bar 3 | ┃ foo 4 | -------------------------------------------------------------------------------- /testdata_result/Ordered and unordered lists.txt: -------------------------------------------------------------------------------- 1 | 0.1 Unordered 2 | 3 | Asterisks tight: 4 | • asterisk 1 5 | • asterisk 2 6 | • asterisk 3 7 | 8 | Asterisks loose: 9 | • asterisk 1 10 | • asterisk 2 11 | • asterisk 3 12 | 13 | ──────────────────────────────────── 14 | 15 | Pluses tight: 16 | • Plus 1 17 | • Plus 2 18 | • Plus 3 19 | 20 | Pluses loose: 21 | • Plus 1 22 | • Plus 2 23 | • Plus 3 24 | 25 | ──────────────────────────────────── 26 | 27 | Minuses tight: 28 | • Minus 1 29 | • Minus 2 30 | • Minus 3 31 | 32 | Minuses loose: 33 | • Minus 1 34 | • Minus 2 35 | • Minus 3 36 | 37 | 0.2 Ordered 38 | 39 | Tight: 40 | 1. First 41 | 2. Second 42 | 3. Third 43 | 44 | and: 45 | 1. One 46 | 2. Two 47 | 3. Three 48 | 49 | Loose using tabs: 50 | 1. First 51 | 2. Second 52 | 3. Third 53 | 54 | and using spaces: 55 | 1. One 56 | 2. Two 57 | 3. Three 58 | 59 | Multiple paragraphs: 60 | 1. Item 1, graf one. 61 | 62 | Item 2. graf two. The quick brown 63 | fox jumped over the lazy dog's 64 | back. 65 | 2. Item 2. 66 | 3. Item 3. 67 | 68 | 0.3 Nested 69 | 70 | • Tab 71 | • Tab 72 | • Tab 73 | 74 | Here's another: 75 | 1. First 76 | 2. Second: 77 | • Fee 78 | • Fie 79 | • Foe 80 | 3. Third 81 | 82 | Same thing but with paragraphs: 83 | 1. First 84 | 2. Second: 85 | • Fee 86 | • Fie 87 | • Foe 88 | 3. Third 89 | 90 | This was an error in Markdown 1.0.1: 91 | • this 92 | • sub 93 | that 94 | -------------------------------------------------------------------------------- /testdata_result/Table.txt: -------------------------------------------------------------------------------- 1 | ┌────────┬───────┬──────┐ 2 | │Markdown│Less │Pretty│ 3 | ╞════════╪═══════╪══════╡ 4 | │Still │renders│nicely│ 5 | ├────────┼───────┼──────┤ 6 | │1 │2 │3 │ 7 | └────────┴───────┴──────┘ 8 | Colons can be used to align columns. 9 | ┌──────────┬───────────┬───────────┐ 10 | │Tables │ Are │ Cool│ 11 | ╞══════════╪═══════════╪═══════════╡ 12 | │col 3 is │right-align│ $1600│ 13 | │ │ ed │ │ 14 | ├──────────┼───────────┼───────────┤ 15 | │col 2 is │ centered │ $12│ 16 | ├──────────┼───────────┼───────────┤ 17 | │zebra │ are neat │ $1│ 18 | │stripes │ │ │ 19 | ├──────────┼───────────┼───────────┤ 20 | │
    
  • it│ See the │ from the│ 21 | │em1
  • item2
 │ │ │ 24 | ├──────────┼───────────┼───────────┤ 25 | │[URL and t│ │ │ 26 | │itle](/url│ │ │ 27 | │/ title). │ │ │ 28 | ├──────────┼───────────┼───────────┤ 29 | │Emphasis, │ Strong │Strikethrou│ 30 | │aka │ emphasis, │gh uses two│ 31 | │italics, │ aka bold, │ tildes.│ 32 | │with │ with │ Scratch│ 33 | │asterisks │ asterisks │ this.│ 34 | │or undersc│or undersco│ │ 35 | │ores. │ res. │ │ 36 | ├──────────┼───────────┼───────────┤ 37 | │![GitHub L│ │ │ 38 | │ogo](/imag│ │ │ 39 | │es/logo.pn│ │ │ 40 | │g) │ │ │ 41 | │ │ │ │ 42 | └──────────┴───────────┴───────────┘ 43 | There must be at least 3 dashes 44 | separating each header cell. The 45 | outer pipes (|) are optional, and 46 | you don't need to make the raw 47 | Markdown line up prettily. You can 48 | also use inline Markdown. 49 | 50 | ┌──────────┬───────────┬───────────┐ 51 | │Tables │ Are │ Cool│ 52 | ╞══════════╪═══════════╪═══════════╡ 53 | │col 3 is │right-align│ $1600│ 54 | │ │ ed │ │ 55 | ├──────────┼───────────┼───────────┤ 56 | │col 2 is │ centered │ $12│ 57 | ├──────────┼───────────┼───────────┤ 58 | │zebra │ are neat │ $1│ 59 | │stripes │ │ │ 60 | ├──────────┼───────────┼───────────┤ 61 | │
  • it│ See the │ from the│ 62 | │em1
  • item2
│ │ │ 65 | ├──────────┼───────────┼───────────┤ 66 | │[URL and t│ │ │ 67 | │itle](/url│ │ │ 68 | │/). │ │ │ 69 | ├──────────┼───────────┼───────────┤ 70 | │Emphasis, │ Strong │Strikethrou│ 71 | │aka │ emphasis, │gh uses two│ 72 | │italics, w│aka bold, w│tildes.Scra│ 73 | │ithasteris│ithasterisk│ tch this.│ 74 | │ksorunders│sorundersco│ │ 75 | │cores. │ res. │ │ 76 | ├──────────┼───────────┼───────────┤ 77 | │![GitHub L│ │ │ 78 | │ogo](/imag│ │ │ 79 | │es/logo.pn│ │ │ 80 | │g) │ │ │ 81 | └──────────┴───────────┴───────────┘ 82 | -------------------------------------------------------------------------------- /testdata_result/Tabs.txt: -------------------------------------------------------------------------------- 1 | • this is a list item indented with 2 | tabs 3 | • this is a list item indented with 4 | spaces 5 | 6 | Code: 7 | 8 | ┃ this code block is indented by one 9 | ┃ tab 10 | 11 | And: 12 | 13 | ┃  this code block is indented by 14 | ┃ two tabs 15 | 16 | And: 17 | 18 | ┃ + this is an example list item 19 | ┃  indented with tabs 20 | ┃  21 | ┃ + this is an example list item 22 | ┃  indented with spaces 23 | 24 | -------------------------------------------------------------------------------- /testdata_result/Tidyness.txt: -------------------------------------------------------------------------------- 1 | ┃ A list within a blockquote: 2 | ┃ • asterisk 1 3 | ┃ • asterisk 2 4 | ┃ • asterisk 3 5 | -------------------------------------------------------------------------------- /testdata_result/break.txt: -------------------------------------------------------------------------------- 1 | some content 2 | more content 3 | -------------------------------------------------------------------------------- /testdata_result/code.txt: -------------------------------------------------------------------------------- 1 |  2 | 3 | Fix for backticks within HTML tag: 4 | like 5 | this 6 | 7 | Here's how you put `backticks` in a 8 | code span. 9 | -------------------------------------------------------------------------------- /testdata_result/codeblock.txt: -------------------------------------------------------------------------------- 1 | Here is a piece of code: 2 | 3 | ┃ package main 4 | ┃  5 | ┃ import "fmt" 6 | ┃  7 | ┃ func main() { 8 | ┃  fmt.Println("Hello world") 9 | ┃ } 10 | 11 | Another one, tagged as well with the 12 | language name 13 | 14 | ┃ #include  15 | ┃ int main() 16 | ┃ { 17 | ┃  printf("Hello, World!"); 18 | ┃  return 0; 19 | ┃ } 20 | 21 | This one is not: 22 | 23 | ┃ #include 24 | ┃ int main() 25 | ┃ { 26 | ┃  printf("Hello, World!"); 27 | ┃  return 0; 28 | ┃ } 29 | 30 | -------------------------------------------------------------------------------- /testdata_result/emoji.txt: -------------------------------------------------------------------------------- 1 | Emoji support ! 2 | 3 | :bowtie:😄 :simple_smile:😆 😊 😃 ☺️ 4 | 😏 😍 😘 😚 😳 😌 😆 😁 😉 😜 😝 😀 5 | 😗 😙 😛 😴 😟 😦 😧 😮 😬 😕 😯 😑 6 | 😒 😅 😓 😥 😩 😔 😞 😖 😨 😰 😣 😢 7 | 😭 😂 😲 😱 :neckbeard:😫 8 | 9 | 😏 😍 😘 😚 😳 😌 😆 😄 😉 😜 😝 😀 10 | 😗 😙 😛 😴 😟 😦 😧 😮 😬 😕 😯 😑 11 | 😒 😅 😓 😥 😩 😔 😞 😖 😨 😰 😣 😢 12 | -------------------------------------------------------------------------------- /testdata_result/emphasis.txt: -------------------------------------------------------------------------------- 1 | Emphasis, aka italics, with 2 | asterisks or underscores. 3 | 4 | Strong emphasis, aka bold, with 5 | asterisks or underscores. 6 | 7 | Bold and italic with asterisks or 8 | underscores. 9 | 10 | Strikethrough uses two tildes. 11 | Scratch this. 12 | 13 | striked italic 14 | 15 | striked bold 16 | 17 | striked bold and italic 18 | -------------------------------------------------------------------------------- /testdata_result/headings.txt: -------------------------------------------------------------------------------- 1 | 1 heading 1 2 | ──────────────────────────────────── 3 | 4 | 1.1 heading 2 5 | 6 | 1.2 heading 2 7 | 8 | 1.3 heading 2 9 | 10 | 1.3.1 heading 3 11 | 12 | 1.3.1.1 heading 4 13 | 14 | 1.3.1.1.1 heading 5 15 | 16 | 1.3.1.1.1.1 heading 6 17 | 18 | 1.3.2 heading 3 19 | 20 | 1.3.3 link [my 21 | link](http://example.com) 22 | 23 | 1.3.4 image 24 | ![img](http://example.com/img.png) 25 |  26 | 27 | 1.3.5 link + img 28 | [](http://example.com) 29 | 30 | 1.3.6 code ìnt x = 7 31 | 32 | 1.3.7 emphasis emphasis 33 | 34 | 1.3.8 strikethrough 35 | 36 | 1.3.9 bold bold 37 | 38 | 1.3.10 
foo
 39 | 40 | 1.3.11  comment html 42 | 43 | -------------------------------------------------------------------------------- /testdata_result/horizontal_rule.txt: -------------------------------------------------------------------------------- 1 | Three or more... 2 | 3 | ──────────────────────────────────── 4 | 5 | Hyphens 6 | 7 | ──────────────────────────────────── 8 | 9 | Asterisks 10 | 11 | ──────────────────────────────────── 12 | 13 | Underscores 14 | -------------------------------------------------------------------------------- /testdata_result/list.txt: -------------------------------------------------------------------------------- 1 | 1. First ordered list item 2 | 2. Another item 3 | • Unordered sub-list. 4 | 3. Actual numbers don't matter, just 5 | that it's a number 6 | 1. Ordered sub-list 7 | 4. And another item. 8 | 9 | A definition list: 10 | Markdown 11 | Text-to-HTML conversion tool 12 | 13 | With a link 14 | [url](http://example.com) 15 | That's a definition list with an 16 | url in the header 17 | 18 | • [x] Write the press release 19 | • [ ] Update the website 20 | • [ ] Contact the media 21 | 1. [x] Write the press release 22 | 2. [ ] Update the website 23 | 3. [ ] Contact the media 24 | -------------------------------------------------------------------------------- /testdata_result/quote.txt: -------------------------------------------------------------------------------- 1 | A paragraph. 2 | ┃ Quoted text. 3 | ┃ Quoted text. 4 | ┃ Quoted text 5 | ┃ ┃ Quoted quote. 6 | ┃ ┃ Quoted quote. 7 | ┃ Quoted text. 8 | ┃ Quoted text. 9 | A paragraph. 10 | ┃ • Quoted 11 | ┃ • List 12 | A paragraph. 13 | -------------------------------------------------------------------------------- /testdata_source/Amps and angle encoding.md: -------------------------------------------------------------------------------- 1 | AT&T has an ampersand in their name. 2 | 3 | AT&T is another way to write it. 4 | 5 | This & that. 6 | 7 | 4 < 5. 8 | 9 | 6 > 5. 10 | 11 | Here's a [link] [1] with an ampersand in the URL. 12 | 13 | Here's a link with an amersand in the link text: [AT&T] [2]. 14 | 15 | Here's an inline [link](/script?foo=1&bar=2). 16 | 17 | Here's an inline [link](). 18 | 19 | 20 | [1]: http://example.com/?foo=1&bar=2 21 | [2]: http://att.com/ "AT&T" -------------------------------------------------------------------------------- /testdata_source/Auto links.md: -------------------------------------------------------------------------------- 1 | Link: . 2 | 3 | With an ampersand: 4 | 5 | * In a list? 6 | * 7 | * It should. 8 | 9 | > Blockquoted: 10 | 11 | Auto-links should not occur here: `` 12 | 13 | or here: -------------------------------------------------------------------------------- /testdata_source/Backslash escapes.md: -------------------------------------------------------------------------------- 1 | These should all get escaped: 2 | 3 | Backslash: \\ 4 | 5 | Backtick: \` 6 | 7 | Asterisk: \* 8 | 9 | Underscore: \_ 10 | 11 | Left brace: \{ 12 | 13 | Right brace: \} 14 | 15 | Left bracket: \[ 16 | 17 | Right bracket: \] 18 | 19 | Left paren: \( 20 | 21 | Right paren: \) 22 | 23 | Greater-than: \> 24 | 25 | Hash: \# 26 | 27 | Period: \. 28 | 29 | Bang: \! 30 | 31 | Plus: \+ 32 | 33 | Minus: \- 34 | 35 | Tilde: \~ 36 | 37 | 38 | 39 | These should not, because they occur within a code block: 40 | 41 | Backslash: \\ 42 | 43 | Backtick: \` 44 | 45 | Asterisk: \* 46 | 47 | Underscore: \_ 48 | 49 | Left brace: \{ 50 | 51 | Right brace: \} 52 | 53 | Left bracket: \[ 54 | 55 | Right bracket: \] 56 | 57 | Left paren: \( 58 | 59 | Right paren: \) 60 | 61 | Greater-than: \> 62 | 63 | Hash: \# 64 | 65 | Period: \. 66 | 67 | Bang: \! 68 | 69 | Plus: \+ 70 | 71 | Minus: \- 72 | 73 | Tilde: \~ 74 | 75 | 76 | Nor should these, which occur in code spans: 77 | 78 | Backslash: `\\` 79 | 80 | Backtick: `` \` `` 81 | 82 | Asterisk: `\*` 83 | 84 | Underscore: `\_` 85 | 86 | Left brace: `\{` 87 | 88 | Right brace: `\}` 89 | 90 | Left bracket: `\[` 91 | 92 | Right bracket: `\]` 93 | 94 | Left paren: `\(` 95 | 96 | Right paren: `\)` 97 | 98 | Greater-than: `\>` 99 | 100 | Hash: `\#` 101 | 102 | Period: `\.` 103 | 104 | Bang: `\!` 105 | 106 | Plus: `\+` 107 | 108 | Minus: `\-` 109 | 110 | Tilde: `\~` 111 | 112 | 113 | These should get escaped, even though they're matching pairs for 114 | other Markdown constructs: 115 | 116 | \*asterisks\* 117 | 118 | \_underscores\_ 119 | 120 | \`backticks\` 121 | 122 | This is a code span with a literal backslash-backtick sequence: `` \` `` 123 | 124 | This is a tag with unescaped backticks bar. 125 | 126 | This is a tag with backslashes bar. 127 | -------------------------------------------------------------------------------- /testdata_source/Blockquotes with code blocks.md: -------------------------------------------------------------------------------- 1 | > Example: 2 | > 3 | > sub status { 4 | > print "working"; 5 | > } 6 | > 7 | > Or: 8 | > 9 | > sub status { 10 | > return "working"; 11 | > } 12 | 13 | 14 | > Blockquote 15 | > with 16 | > some 17 | > spaces 18 | -------------------------------------------------------------------------------- /testdata_source/Code Blocks.md: -------------------------------------------------------------------------------- 1 | code block on the first line 2 | 3 | Regular text. 4 | 5 | code block indented by spaces 6 | 7 | Regular text. 8 | 9 | the lines in this block 10 | all contain trailing spaces 11 | 12 | Regular Text. 13 | 14 | code block on the last line -------------------------------------------------------------------------------- /testdata_source/Code Spans.md: -------------------------------------------------------------------------------- 1 | `` 2 | 3 | Fix for backticks within HTML tag: like this 4 | 5 | Here's how you put `` `backticks` `` in a code span. 6 | 7 | -------------------------------------------------------------------------------- /testdata_source/Hard-wrapped paragraphs with list-like lines no empty line before block.md: -------------------------------------------------------------------------------- 1 | In Markdown 1.0.0 and earlier. Version 2 | 8. This line turns into a list item. 3 | Because a hard-wrapped line in the 4 | middle of a paragraph looked like a 5 | list item. 6 | 7 | Here's one with a bullet. 8 | * criminey. 9 | -------------------------------------------------------------------------------- /testdata_source/Hard-wrapped paragraphs with list-like lines.md: -------------------------------------------------------------------------------- 1 | In Markdown 1.0.0 and earlier. Version 2 | 8. This line turns into a list item. 3 | Because a hard-wrapped line in the 4 | middle of a paragraph looked like a 5 | list item. 6 | 7 | Here's one with a bullet. 8 | * criminey. 9 | -------------------------------------------------------------------------------- /testdata_source/Horizontal rules.md: -------------------------------------------------------------------------------- 1 | Dashes: 2 | 3 | --- 4 | 5 | --- 6 | 7 | --- 8 | 9 | --- 10 | 11 | --- 12 | 13 | - - - 14 | 15 | - - - 16 | 17 | - - - 18 | 19 | - - - 20 | 21 | - - - 22 | 23 | 24 | Asterisks: 25 | 26 | *** 27 | 28 | *** 29 | 30 | *** 31 | 32 | *** 33 | 34 | *** 35 | 36 | * * * 37 | 38 | * * * 39 | 40 | * * * 41 | 42 | * * * 43 | 44 | * * * 45 | 46 | 47 | Underscores: 48 | 49 | ___ 50 | 51 | ___ 52 | 53 | ___ 54 | 55 | ___ 56 | 57 | ___ 58 | 59 | _ _ _ 60 | 61 | _ _ _ 62 | 63 | _ _ _ 64 | 65 | _ _ _ 66 | 67 | _ _ _ 68 | -------------------------------------------------------------------------------- /testdata_source/Inline HTML (Advanced).md: -------------------------------------------------------------------------------- 1 | Simple block on one line: 2 | 3 |
foo
4 | 5 | And nested without indentation: 6 | 7 |
8 |
9 |
10 | foo 11 |
12 |
13 |
14 |
bar
15 |
16 | -------------------------------------------------------------------------------- /testdata_source/Inline HTML (Simple).md: -------------------------------------------------------------------------------- 1 | Here's a simple block: 2 | 3 |
4 | foo 5 |
6 | 7 | This should be a code block, though: 8 | 9 |
10 | foo 11 |
12 | 13 | As should this: 14 | 15 |
foo
16 | 17 | Now, nested: 18 | 19 |
20 |
21 |
22 | foo 23 |
24 |
25 |
26 | 27 | This should just be an HTML comment: 28 | 29 | 30 | 31 | Multiline: 32 | 33 | 37 | 38 | Code block: 39 | 40 | 41 | 42 | Just plain comment, with trailing spaces on the line: 43 | 44 | 45 | 46 | Code: 47 | 48 |
49 | 50 | Hr's: 51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 |
59 | 60 |
61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 |
69 | 70 | -------------------------------------------------------------------------------- /testdata_source/Inline HTML comments.md: -------------------------------------------------------------------------------- 1 | Paragraph one. 2 | 3 | 4 | 5 | 8 | 9 | Paragraph two. 10 | 11 | 12 | 13 | The end. 14 | -------------------------------------------------------------------------------- /testdata_source/Links, inline style.md: -------------------------------------------------------------------------------- 1 | Just a [URL](/url/). 2 | 3 | [URL and title](/url/ "title"). 4 | 5 | [URL and title](/url/ "title preceded by two spaces"). 6 | 7 | [URL and title](/url/ "title preceded by a tab"). 8 | 9 | [URL and title](/url/ "title has spaces afterward" ). 10 | 11 | 12 | [Empty](). 13 | -------------------------------------------------------------------------------- /testdata_source/Links, reference style.md: -------------------------------------------------------------------------------- 1 | Foo [bar] [1]. 2 | 3 | Foo [bar][1]. 4 | 5 | Foo [bar] 6 | [1]. 7 | 8 | [1]: /url/ "Title" 9 | 10 | 11 | With [embedded [brackets]] [b]. 12 | 13 | 14 | Indented [once][]. 15 | 16 | Indented [twice][]. 17 | 18 | Indented [thrice][]. 19 | 20 | Indented [four][] times. 21 | 22 | [once]: /url 23 | 24 | [twice]: /url 25 | 26 | [thrice]: /url 27 | 28 | [four]: /url 29 | 30 | 31 | [b]: /url/ 32 | 33 | * * * 34 | 35 | [this] [this] should work 36 | 37 | So should [this][this]. 38 | 39 | And [this] []. 40 | 41 | And [this][]. 42 | 43 | And [this]. 44 | 45 | But not [that] []. 46 | 47 | Nor [that][]. 48 | 49 | Nor [that]. 50 | 51 | [Something in brackets like [this][] should work] 52 | 53 | [Same with [this].] 54 | 55 | In this case, [this](/somethingelse/) points to something else. 56 | 57 | Backslashing should suppress \[this] and [this\]. 58 | 59 | [this]: foo 60 | 61 | 62 | * * * 63 | 64 | Here's one where the [link 65 | breaks] across lines. 66 | 67 | Here's another where the [link 68 | breaks] across lines, but with a line-ending space. 69 | 70 | 71 | [link breaks]: /url/ 72 | -------------------------------------------------------------------------------- /testdata_source/Links, shortcut references.md: -------------------------------------------------------------------------------- 1 | This is the [simple case]. 2 | 3 | [simple case]: /simple 4 | 5 | 6 | 7 | This one has a [line 8 | break]. 9 | 10 | This one has a [line 11 | break] with a line-ending space. 12 | 13 | [line break]: /foo 14 | 15 | 16 | [this] [that] and the [other] 17 | 18 | [this]: /this 19 | [that]: /that 20 | [other]: /other 21 | -------------------------------------------------------------------------------- /testdata_source/Literal quotes in titles.md: -------------------------------------------------------------------------------- 1 | Foo [bar][]. 2 | 3 | Foo [bar](/url/ "Title with "quotes" inside"). 4 | 5 | 6 | [bar]: /url/ "Title with "quotes" inside" 7 | 8 | -------------------------------------------------------------------------------- /testdata_source/Markdown Documentation - Basics.md: -------------------------------------------------------------------------------- 1 | Markdown: Basics 2 | ================ 3 | 4 | 11 | 12 | 13 | Getting the Gist of Markdown's Formatting Syntax 14 | ------------------------------------------------ 15 | 16 | This page offers a brief overview of what it's like to use Markdown. 17 | The [syntax page] [s] provides complete, detailed documentation for 18 | every feature, but Markdown should be very easy to pick up simply by 19 | looking at a few examples of it in action. The examples on this page 20 | are written in a before/after style, showing example syntax and the 21 | HTML output produced by Markdown. 22 | 23 | It's also helpful to simply try Markdown out; the [Dingus] [d] is a 24 | web application that allows you type your own Markdown-formatted text 25 | and translate it to XHTML. 26 | 27 | **Note:** This document is itself written using Markdown; you 28 | can [see the source for it by adding '.text' to the URL] [src]. 29 | 30 | [s]: /projects/markdown/syntax "Markdown Syntax" 31 | [d]: /projects/markdown/dingus "Markdown Dingus" 32 | [src]: /projects/markdown/basics.text 33 | 34 | 35 | ## Paragraphs, Headers, Blockquotes ## 36 | 37 | A paragraph is simply one or more consecutive lines of text, separated 38 | by one or more blank lines. (A blank line is any line that looks like a 39 | blank line -- a line containing nothing spaces or tabs is considered 40 | blank.) Normal paragraphs should not be intended with spaces or tabs. 41 | 42 | Markdown offers two styles of headers: *Setext* and *atx*. 43 | Setext-style headers for `

` and `

` are created by 44 | "underlining" with equal signs (`=`) and hyphens (`-`), respectively. 45 | To create an atx-style header, you put 1-6 hash marks (`#`) at the 46 | beginning of the line -- the number of hashes equals the resulting 47 | HTML header level. 48 | 49 | Blockquotes are indicated using email-style '`>`' angle brackets. 50 | 51 | Markdown: 52 | 53 | A First Level Header 54 | ==================== 55 | 56 | A Second Level Header 57 | --------------------- 58 | 59 | Now is the time for all good men to come to 60 | the aid of their country. This is just a 61 | regular paragraph. 62 | 63 | The quick brown fox jumped over the lazy 64 | dog's back. 65 | 66 | ### Header 3 67 | 68 | > This is a blockquote. 69 | > 70 | > This is the second paragraph in the blockquote. 71 | > 72 | > ## This is an H2 in a blockquote 73 | 74 | 75 | Output: 76 | 77 |

A First Level Header

78 | 79 |

A Second Level Header

80 | 81 |

Now is the time for all good men to come to 82 | the aid of their country. This is just a 83 | regular paragraph.

84 | 85 |

The quick brown fox jumped over the lazy 86 | dog's back.

87 | 88 |

Header 3

89 | 90 |
91 |

This is a blockquote.

92 | 93 |

This is the second paragraph in the blockquote.

94 | 95 |

This is an H2 in a blockquote

96 |
97 | 98 | 99 | 100 | ### Phrase Emphasis ### 101 | 102 | Markdown uses asterisks and underscores to indicate spans of emphasis. 103 | 104 | Markdown: 105 | 106 | Some of these words *are emphasized*. 107 | Some of these words _are emphasized also_. 108 | 109 | Use two asterisks for **strong emphasis**. 110 | Or, if you prefer, __use two underscores instead__. 111 | 112 | Output: 113 | 114 |

Some of these words are emphasized. 115 | Some of these words are emphasized also.

116 | 117 |

Use two asterisks for strong emphasis. 118 | Or, if you prefer, use two underscores instead.

119 | 120 | 121 | 122 | ## Lists ## 123 | 124 | Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`, 125 | `+`, and `-`) as list markers. These three markers are 126 | interchangable; this: 127 | 128 | * Candy. 129 | * Gum. 130 | * Booze. 131 | 132 | this: 133 | 134 | + Candy. 135 | + Gum. 136 | + Booze. 137 | 138 | and this: 139 | 140 | - Candy. 141 | - Gum. 142 | - Booze. 143 | 144 | all produce the same output: 145 | 146 |
    147 |
  • Candy.
  • 148 |
  • Gum.
  • 149 |
  • Booze.
  • 150 |
151 | 152 | Ordered (numbered) lists use regular numbers, followed by periods, as 153 | list markers: 154 | 155 | 1. Red 156 | 2. Green 157 | 3. Blue 158 | 159 | Output: 160 | 161 |
    162 |
  1. Red
  2. 163 |
  3. Green
  4. 164 |
  5. Blue
  6. 165 |
166 | 167 | If you put blank lines between items, you'll get `

` tags for the 168 | list item text. You can create multi-paragraph list items by indenting 169 | the paragraphs by 4 spaces or 1 tab: 170 | 171 | * A list item. 172 | 173 | With multiple paragraphs. 174 | 175 | * Another item in the list. 176 | 177 | Output: 178 | 179 |

    180 |
  • A list item.

    181 |

    With multiple paragraphs.

  • 182 |
  • Another item in the list.

  • 183 |
184 | 185 | 186 | 187 | ### Links ### 188 | 189 | Markdown supports two styles for creating links: *inline* and 190 | *reference*. With both styles, you use square brackets to delimit the 191 | text you want to turn into a link. 192 | 193 | Inline-style links use parentheses immediately after the link text. 194 | For example: 195 | 196 | This is an [example link](http://example.com/). 197 | 198 | Output: 199 | 200 |

This is an 201 | example link.

202 | 203 | Optionally, you may include a title attribute in the parentheses: 204 | 205 | This is an [example link](http://example.com/ "With a Title"). 206 | 207 | Output: 208 | 209 |

This is an 210 | example link.

211 | 212 | Reference-style links allow you to refer to your links by names, which 213 | you define elsewhere in your document: 214 | 215 | I get 10 times more traffic from [Google][1] than from 216 | [Yahoo][2] or [MSN][3]. 217 | 218 | [1]: http://google.com/ "Google" 219 | [2]: http://search.yahoo.com/ "Yahoo Search" 220 | [3]: http://search.msn.com/ "MSN Search" 221 | 222 | Output: 223 | 224 |

I get 10 times more traffic from Google than from Yahoo or MSN.

228 | 229 | The title attribute is optional. Link names may contain letters, 230 | numbers and spaces, but are *not* case sensitive: 231 | 232 | I start my morning with a cup of coffee and 233 | [The New York Times][NY Times]. 234 | 235 | [ny times]: http://www.nytimes.com/ 236 | 237 | Output: 238 | 239 |

I start my morning with a cup of coffee and 240 | The New York Times.

241 | 242 | 243 | ### Images ### 244 | 245 | Image syntax is very much like link syntax. 246 | 247 | Inline (titles are optional): 248 | 249 | ![alt text](/path/to/img.jpg "Title") 250 | 251 | Reference-style: 252 | 253 | ![alt text][id] 254 | 255 | [id]: /path/to/img.jpg "Title" 256 | 257 | Both of the above examples produce the same output: 258 | 259 | alt text 260 | 261 | 262 | 263 | ### Code ### 264 | 265 | In a regular paragraph, you can create code span by wrapping text in 266 | backtick quotes. Any ampersands (`&`) and angle brackets (`<` or 267 | `>`) will automatically be translated into HTML entities. This makes 268 | it easy to use Markdown to write about HTML example code: 269 | 270 | I strongly recommend against using any `` tags. 271 | 272 | I wish SmartyPants used named entities like `—` 273 | instead of decimal-encoded entites like `—`. 274 | 275 | Output: 276 | 277 |

I strongly recommend against using any 278 | <blink> tags.

279 | 280 |

I wish SmartyPants used named entities like 281 | &mdash; instead of decimal-encoded 282 | entites like &#8212;.

283 | 284 | 285 | To specify an entire block of pre-formatted code, indent every line of 286 | the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`, 287 | and `>` characters will be escaped automatically. 288 | 289 | Markdown: 290 | 291 | If you want your page to validate under XHTML 1.0 Strict, 292 | you've got to put paragraph tags in your blockquotes: 293 | 294 |
295 |

For example.

296 |
297 | 298 | Output: 299 | 300 |

If you want your page to validate under XHTML 1.0 Strict, 301 | you've got to put paragraph tags in your blockquotes:

302 | 303 |
<blockquote>
304 |         <p>For example.</p>
305 |     </blockquote>
306 |     
307 | -------------------------------------------------------------------------------- /testdata_source/Markdown Documentation - Syntax.md: -------------------------------------------------------------------------------- 1 | Markdown: Syntax 2 | ================ 3 | 4 | 11 | 12 | 13 | * [Overview](#overview) 14 | * [Philosophy](#philosophy) 15 | * [Inline HTML](#html) 16 | * [Automatic Escaping for Special Characters](#autoescape) 17 | * [Block Elements](#block) 18 | * [Paragraphs and Line Breaks](#p) 19 | * [Headers](#header) 20 | * [Blockquotes](#blockquote) 21 | * [Lists](#list) 22 | * [Code Blocks](#precode) 23 | * [Horizontal Rules](#hr) 24 | * [Span Elements](#span) 25 | * [Links](#link) 26 | * [Emphasis](#em) 27 | * [Code](#code) 28 | * [Images](#img) 29 | * [Miscellaneous](#misc) 30 | * [Backslash Escapes](#backslash) 31 | * [Automatic Links](#autolink) 32 | 33 | 34 | **Note:** This document is itself written using Markdown; you 35 | can [see the source for it by adding '.text' to the URL][src]. 36 | 37 | [src]: /projects/markdown/syntax.text 38 | 39 | * * * 40 | 41 |

Overview

42 | 43 |

Philosophy

44 | 45 | Markdown is intended to be as easy-to-read and easy-to-write as is feasible. 46 | 47 | Readability, however, is emphasized above all else. A Markdown-formatted 48 | document should be publishable as-is, as plain text, without looking 49 | like it's been marked up with tags or formatting instructions. While 50 | Markdown's syntax has been influenced by several existing text-to-HTML 51 | filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4], 52 | [Grutatext] [5], and [EtText] [6] -- the single biggest source of 53 | inspiration for Markdown's syntax is the format of plain text email. 54 | 55 | [1]: http://docutils.sourceforge.net/mirror/setext.html 56 | [2]: http://www.aaronsw.com/2002/atx/ 57 | [3]: http://textism.com/tools/textile/ 58 | [4]: http://docutils.sourceforge.net/rst.html 59 | [5]: http://www.triptico.com/software/grutatxt.html 60 | [6]: http://ettext.taint.org/doc/ 61 | 62 | To this end, Markdown's syntax is comprised entirely of punctuation 63 | characters, which punctuation characters have been carefully chosen so 64 | as to look like what they mean. E.g., asterisks around a word actually 65 | look like \*emphasis\*. Markdown lists look like, well, lists. Even 66 | blockquotes look like quoted passages of text, assuming you've ever 67 | used email. 68 | 69 | 70 | 71 |

Inline HTML

72 | 73 | Markdown's syntax is intended for one purpose: to be used as a 74 | format for *writing* for the web. 75 | 76 | Markdown is not a replacement for HTML, or even close to it. Its 77 | syntax is very small, corresponding only to a very small subset of 78 | HTML tags. The idea is *not* to create a syntax that makes it easier 79 | to insert HTML tags. In my opinion, HTML tags are already easy to 80 | insert. The idea for Markdown is to make it easy to read, write, and 81 | edit prose. HTML is a *publishing* format; Markdown is a *writing* 82 | format. Thus, Markdown's formatting syntax only addresses issues that 83 | can be conveyed in plain text. 84 | 85 | For any markup that is not covered by Markdown's syntax, you simply 86 | use HTML itself. There's no need to preface it or delimit it to 87 | indicate that you're switching from Markdown to HTML; you just use 88 | the tags. 89 | 90 | The only restrictions are that block-level HTML elements -- e.g. `
`, 91 | ``, `
`, `

`, etc. -- must be separated from surrounding 92 | content by blank lines, and the start and end tags of the block should 93 | not be indented with tabs or spaces. Markdown is smart enough not 94 | to add extra (unwanted) `

` tags around HTML block-level tags. 95 | 96 | For example, to add an HTML table to a Markdown article: 97 | 98 | This is a regular paragraph. 99 | 100 |

101 | 102 | 103 | 104 |
Foo
105 | 106 | This is another regular paragraph. 107 | 108 | Note that Markdown formatting syntax is not processed within block-level 109 | HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an 110 | HTML block. 111 | 112 | Span-level HTML tags -- e.g. ``, ``, or `` -- can be 113 | used anywhere in a Markdown paragraph, list item, or header. If you 114 | want, you can even use HTML tags instead of Markdown formatting; e.g. if 115 | you'd prefer to use HTML `` or `` tags instead of Markdown's 116 | link or image syntax, go right ahead. 117 | 118 | Unlike block-level HTML tags, Markdown syntax *is* processed within 119 | span-level tags. 120 | 121 | 122 |

Automatic Escaping for Special Characters

123 | 124 | In HTML, there are two characters that demand special treatment: `<` 125 | and `&`. Left angle brackets are used to start tags; ampersands are 126 | used to denote HTML entities. If you want to use them as literal 127 | characters, you must escape them as entities, e.g. `<`, and 128 | `&`. 129 | 130 | Ampersands in particular are bedeviling for web writers. If you want to 131 | write about 'AT&T', you need to write '`AT&T`'. You even need to 132 | escape ampersands within URLs. Thus, if you want to link to: 133 | 134 | http://images.google.com/images?num=30&q=larry+bird 135 | 136 | you need to encode the URL as: 137 | 138 | http://images.google.com/images?num=30&q=larry+bird 139 | 140 | in your anchor tag `href` attribute. Needless to say, this is easy to 141 | forget, and is probably the single most common source of HTML validation 142 | errors in otherwise well-marked-up web sites. 143 | 144 | Markdown allows you to use these characters naturally, taking care of 145 | all the necessary escaping for you. If you use an ampersand as part of 146 | an HTML entity, it remains unchanged; otherwise it will be translated 147 | into `&`. 148 | 149 | So, if you want to include a copyright symbol in your article, you can write: 150 | 151 | © 152 | 153 | and Markdown will leave it alone. But if you write: 154 | 155 | AT&T 156 | 157 | Markdown will translate it to: 158 | 159 | AT&T 160 | 161 | Similarly, because Markdown supports [inline HTML](#html), if you use 162 | angle brackets as delimiters for HTML tags, Markdown will treat them as 163 | such. But if you write: 164 | 165 | 4 < 5 166 | 167 | Markdown will translate it to: 168 | 169 | 4 < 5 170 | 171 | However, inside Markdown code spans and blocks, angle brackets and 172 | ampersands are *always* encoded automatically. This makes it easy to use 173 | Markdown to write about HTML code. (As opposed to raw HTML, which is a 174 | terrible format for writing about HTML syntax, because every single `<` 175 | and `&` in your example code needs to be escaped.) 176 | 177 | 178 | * * * 179 | 180 | 181 |

Block Elements

182 | 183 | 184 |

Paragraphs and Line Breaks

185 | 186 | A paragraph is simply one or more consecutive lines of text, separated 187 | by one or more blank lines. (A blank line is any line that looks like a 188 | blank line -- a line containing nothing but spaces or tabs is considered 189 | blank.) Normal paragraphs should not be intended with spaces or tabs. 190 | 191 | The implication of the "one or more consecutive lines of text" rule is 192 | that Markdown supports "hard-wrapped" text paragraphs. This differs 193 | significantly from most other text-to-HTML formatters (including Movable 194 | Type's "Convert Line Breaks" option) which translate every line break 195 | character in a paragraph into a `
` tag. 196 | 197 | When you *do* want to insert a `
` break tag using Markdown, you 198 | end a line with two or more spaces, then type return. 199 | 200 | Yes, this takes a tad more effort to create a `
`, but a simplistic 201 | "every line break is a `
`" rule wouldn't work for Markdown. 202 | Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] 203 | work best -- and look better -- when you format them with hard breaks. 204 | 205 | [bq]: #blockquote 206 | [l]: #list 207 | 208 | 209 | 210 | 211 | 212 | Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. 213 | 214 | Setext-style headers are "underlined" using equal signs (for first-level 215 | headers) and dashes (for second-level headers). For example: 216 | 217 | This is an H1 218 | ============= 219 | 220 | This is an H2 221 | ------------- 222 | 223 | Any number of underlining `=`'s or `-`'s will work. 224 | 225 | Atx-style headers use 1-6 hash characters at the start of the line, 226 | corresponding to header levels 1-6. For example: 227 | 228 | # This is an H1 229 | 230 | ## This is an H2 231 | 232 | ###### This is an H6 233 | 234 | Optionally, you may "close" atx-style headers. This is purely 235 | cosmetic -- you can use this if you think it looks better. The 236 | closing hashes don't even need to match the number of hashes 237 | used to open the header. (The number of opening hashes 238 | determines the header level.) : 239 | 240 | # This is an H1 # 241 | 242 | ## This is an H2 ## 243 | 244 | ### This is an H3 ###### 245 | 246 | 247 |

Blockquotes

248 | 249 | Markdown uses email-style `>` characters for blockquoting. If you're 250 | familiar with quoting passages of text in an email message, then you 251 | know how to create a blockquote in Markdown. It looks best if you hard 252 | wrap the text and put a `>` before every line: 253 | 254 | > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, 255 | > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. 256 | > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. 257 | > 258 | > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse 259 | > id sem consectetuer libero luctus adipiscing. 260 | 261 | Markdown allows you to be lazy and only put the `>` before the first 262 | line of a hard-wrapped paragraph: 263 | 264 | > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, 265 | consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. 266 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. 267 | 268 | > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse 269 | id sem consectetuer libero luctus adipiscing. 270 | 271 | Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by 272 | adding additional levels of `>`: 273 | 274 | > This is the first level of quoting. 275 | > 276 | > > This is nested blockquote. 277 | > 278 | > Back to the first level. 279 | 280 | Blockquotes can contain other Markdown elements, including headers, lists, 281 | and code blocks: 282 | 283 | > ## This is a header. 284 | > 285 | > 1. This is the first list item. 286 | > 2. This is the second list item. 287 | > 288 | > Here's some example code: 289 | > 290 | > return shell_exec("echo $input | $markdown_script"); 291 | 292 | Any decent text editor should make email-style quoting easy. For 293 | example, with BBEdit, you can make a selection and choose Increase 294 | Quote Level from the Text menu. 295 | 296 | 297 |

Lists

298 | 299 | Markdown supports ordered (numbered) and unordered (bulleted) lists. 300 | 301 | Unordered lists use asterisks, pluses, and hyphens -- interchangably 302 | -- as list markers: 303 | 304 | * Red 305 | * Green 306 | * Blue 307 | 308 | is equivalent to: 309 | 310 | + Red 311 | + Green 312 | + Blue 313 | 314 | and: 315 | 316 | - Red 317 | - Green 318 | - Blue 319 | 320 | Ordered lists use numbers followed by periods: 321 | 322 | 1. Bird 323 | 2. McHale 324 | 3. Parish 325 | 326 | It's important to note that the actual numbers you use to mark the 327 | list have no effect on the HTML output Markdown produces. The HTML 328 | Markdown produces from the above list is: 329 | 330 |
    331 |
  1. Bird
  2. 332 |
  3. McHale
  4. 333 |
  5. Parish
  6. 334 |
335 | 336 | If you instead wrote the list in Markdown like this: 337 | 338 | 1. Bird 339 | 1. McHale 340 | 1. Parish 341 | 342 | or even: 343 | 344 | 3. Bird 345 | 1. McHale 346 | 8. Parish 347 | 348 | you'd get the exact same HTML output. The point is, if you want to, 349 | you can use ordinal numbers in your ordered Markdown lists, so that 350 | the numbers in your source match the numbers in your published HTML. 351 | But if you want to be lazy, you don't have to. 352 | 353 | If you do use lazy list numbering, however, you should still start the 354 | list with the number 1. At some point in the future, Markdown may support 355 | starting ordered lists at an arbitrary number. 356 | 357 | List markers typically start at the left margin, but may be indented by 358 | up to three spaces. List markers must be followed by one or more spaces 359 | or a tab. 360 | 361 | To make lists look nice, you can wrap items with hanging indents: 362 | 363 | * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 364 | Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, 365 | viverra nec, fringilla in, laoreet vitae, risus. 366 | * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 367 | Suspendisse id sem consectetuer libero luctus adipiscing. 368 | 369 | But if you want to be lazy, you don't have to: 370 | 371 | * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 372 | Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, 373 | viverra nec, fringilla in, laoreet vitae, risus. 374 | * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 375 | Suspendisse id sem consectetuer libero luctus adipiscing. 376 | 377 | If list items are separated by blank lines, Markdown will wrap the 378 | items in `

` tags in the HTML output. For example, this input: 379 | 380 | * Bird 381 | * Magic 382 | 383 | will turn into: 384 | 385 |

    386 |
  • Bird
  • 387 |
  • Magic
  • 388 |
389 | 390 | But this: 391 | 392 | * Bird 393 | 394 | * Magic 395 | 396 | will turn into: 397 | 398 |
    399 |
  • Bird

  • 400 |
  • Magic

  • 401 |
402 | 403 | List items may consist of multiple paragraphs. Each subsequent 404 | paragraph in a list item must be intended by either 4 spaces 405 | or one tab: 406 | 407 | 1. This is a list item with two paragraphs. Lorem ipsum dolor 408 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit 409 | mi posuere lectus. 410 | 411 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet 412 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum 413 | sit amet velit. 414 | 415 | 2. Suspendisse id sem consectetuer libero luctus adipiscing. 416 | 417 | It looks nice if you indent every line of the subsequent 418 | paragraphs, but here again, Markdown will allow you to be 419 | lazy: 420 | 421 | * This is a list item with two paragraphs. 422 | 423 | This is the second paragraph in the list item. You're 424 | only required to indent the first line. Lorem ipsum dolor 425 | sit amet, consectetuer adipiscing elit. 426 | 427 | * Another item in the same list. 428 | 429 | To put a blockquote within a list item, the blockquote's `>` 430 | delimiters need to be indented: 431 | 432 | * A list item with a blockquote: 433 | 434 | > This is a blockquote 435 | > inside a list item. 436 | 437 | To put a code block within a list item, the code block needs 438 | to be indented *twice* -- 8 spaces or two tabs: 439 | 440 | * A list item with a code block: 441 | 442 | 443 | 444 | 445 | It's worth noting that it's possible to trigger an ordered list by 446 | accident, by writing something like this: 447 | 448 | 1986. What a great season. 449 | 450 | In other words, a *number-period-space* sequence at the beginning of a 451 | line. To avoid this, you can backslash-escape the period: 452 | 453 | 1986\. What a great season. 454 | 455 | 456 | 457 |

Code Blocks

458 | 459 | Pre-formatted code blocks are used for writing about programming or 460 | markup source code. Rather than forming normal paragraphs, the lines 461 | of a code block are interpreted literally. Markdown wraps a code block 462 | in both `
` and `` tags.
463 | 
464 | To produce a code block in Markdown, simply indent every line of the
465 | block by at least 4 spaces or 1 tab. For example, given this input:
466 | 
467 |     This is a normal paragraph:
468 | 
469 |         This is a code block.
470 | 
471 | Markdown will generate:
472 | 
473 |     

This is a normal paragraph:

474 | 475 |
This is a code block.
476 |     
477 | 478 | One level of indentation -- 4 spaces or 1 tab -- is removed from each 479 | line of the code block. For example, this: 480 | 481 | Here is an example of AppleScript: 482 | 483 | tell application "Foo" 484 | beep 485 | end tell 486 | 487 | will turn into: 488 | 489 |

Here is an example of AppleScript:

490 | 491 |
tell application "Foo"
492 |         beep
493 |     end tell
494 |     
495 | 496 | A code block continues until it reaches a line that is not indented 497 | (or the end of the article). 498 | 499 | Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) 500 | are automatically converted into HTML entities. This makes it very 501 | easy to include example HTML source code using Markdown -- just paste 502 | it and indent it, and Markdown will handle the hassle of encoding the 503 | ampersands and angle brackets. For example, this: 504 | 505 | 508 | 509 | will turn into: 510 | 511 |
<div class="footer">
512 |         &copy; 2004 Foo Corporation
513 |     </div>
514 |     
515 | 516 | Regular Markdown syntax is not processed within code blocks. E.g., 517 | asterisks are just literal asterisks within a code block. This means 518 | it's also easy to use Markdown to write about Markdown's own syntax. 519 | 520 | 521 | 522 |

Horizontal Rules

523 | 524 | You can produce a horizontal rule tag (`
`) by placing three or 525 | more hyphens, asterisks, or underscores on a line by themselves. If you 526 | wish, you may use spaces between the hyphens or asterisks. Each of the 527 | following lines will produce a horizontal rule: 528 | 529 | * * * 530 | 531 | *** 532 | 533 | ***** 534 | 535 | - - - 536 | 537 | --------------------------------------- 538 | 539 | _ _ _ 540 | 541 | 542 | * * * 543 | 544 |

Span Elements

545 | 546 | 547 | 548 | Markdown supports two style of links: *inline* and *reference*. 549 | 550 | In both styles, the link text is delimited by [square brackets]. 551 | 552 | To create an inline link, use a set of regular parentheses immediately 553 | after the link text's closing square bracket. Inside the parentheses, 554 | put the URL where you want the link to point, along with an *optional* 555 | title for the link, surrounded in quotes. For example: 556 | 557 | This is [an example](http://example.com/ "Title") inline link. 558 | 559 | [This link](http://example.net/) has no title attribute. 560 | 561 | Will produce: 562 | 563 |

This is 564 | an example inline link.

565 | 566 |

This link has no 567 | title attribute.

568 | 569 | If you're referring to a local resource on the same server, you can 570 | use relative paths: 571 | 572 | See my [About](/about/) page for details. 573 | 574 | Reference-style links use a second set of square brackets, inside 575 | which you place a label of your choosing to identify the link: 576 | 577 | This is [an example][id] reference-style link. 578 | 579 | You can optionally use a space to separate the sets of brackets: 580 | 581 | This is [an example] [id] reference-style link. 582 | 583 | Then, anywhere in the document, you define your link label like this, 584 | on a line by itself: 585 | 586 | [id]: http://example.com/ "Optional Title Here" 587 | 588 | That is: 589 | 590 | * Square brackets containing the link identifier (optionally 591 | indented from the left margin using up to three spaces); 592 | * followed by a colon; 593 | * followed by one or more spaces (or tabs); 594 | * followed by the URL for the link; 595 | * optionally followed by a title attribute for the link, enclosed 596 | in double or single quotes. 597 | 598 | The link URL may, optionally, be surrounded by angle brackets: 599 | 600 | [id]: "Optional Title Here" 601 | 602 | You can put the title attribute on the next line and use extra spaces 603 | or tabs for padding, which tends to look better with longer URLs: 604 | 605 | [id]: http://example.com/longish/path/to/resource/here 606 | "Optional Title Here" 607 | 608 | Link definitions are only used for creating links during Markdown 609 | processing, and are stripped from your document in the HTML output. 610 | 611 | Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links: 612 | 613 | [link text][a] 614 | [link text][A] 615 | 616 | are equivalent. 617 | 618 | The *implicit link name* shortcut allows you to omit the name of the 619 | link, in which case the link text itself is used as the name. 620 | Just use an empty set of square brackets -- e.g., to link the word 621 | "Google" to the google.com web site, you could simply write: 622 | 623 | [Google][] 624 | 625 | And then define the link: 626 | 627 | [Google]: http://google.com/ 628 | 629 | Because link names may contain spaces, this shortcut even works for 630 | multiple words in the link text: 631 | 632 | Visit [Daring Fireball][] for more information. 633 | 634 | And then define the link: 635 | 636 | [Daring Fireball]: http://daringfireball.net/ 637 | 638 | Link definitions can be placed anywhere in your Markdown document. I 639 | tend to put them immediately after each paragraph in which they're 640 | used, but if you want, you can put them all at the end of your 641 | document, sort of like footnotes. 642 | 643 | Here's an example of reference links in action: 644 | 645 | I get 10 times more traffic from [Google] [1] than from 646 | [Yahoo] [2] or [MSN] [3]. 647 | 648 | [1]: http://google.com/ "Google" 649 | [2]: http://search.yahoo.com/ "Yahoo Search" 650 | [3]: http://search.msn.com/ "MSN Search" 651 | 652 | Using the implicit link name shortcut, you could instead write: 653 | 654 | I get 10 times more traffic from [Google][] than from 655 | [Yahoo][] or [MSN][]. 656 | 657 | [google]: http://google.com/ "Google" 658 | [yahoo]: http://search.yahoo.com/ "Yahoo Search" 659 | [msn]: http://search.msn.com/ "MSN Search" 660 | 661 | Both of the above examples will produce the following HTML output: 662 | 663 |

I get 10 times more traffic from Google than from 665 | Yahoo 666 | or MSN.

667 | 668 | For comparison, here is the same paragraph written using 669 | Markdown's inline link style: 670 | 671 | I get 10 times more traffic from [Google](http://google.com/ "Google") 672 | than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or 673 | [MSN](http://search.msn.com/ "MSN Search"). 674 | 675 | The point of reference-style links is not that they're easier to 676 | write. The point is that with reference-style links, your document 677 | source is vastly more readable. Compare the above examples: using 678 | reference-style links, the paragraph itself is only 81 characters 679 | long; with inline-style links, it's 176 characters; and as raw HTML, 680 | it's 234 characters. In the raw HTML, there's more markup than there 681 | is text. 682 | 683 | With Markdown's reference-style links, a source document much more 684 | closely resembles the final output, as rendered in a browser. By 685 | allowing you to move the markup-related metadata out of the paragraph, 686 | you can add links without interrupting the narrative flow of your 687 | prose. 688 | 689 | 690 |

Emphasis

691 | 692 | Markdown treats asterisks (`*`) and underscores (`_`) as indicators of 693 | emphasis. Text wrapped with one `*` or `_` will be wrapped with an 694 | HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML 695 | `` tag. E.g., this input: 696 | 697 | *single asterisks* 698 | 699 | _single underscores_ 700 | 701 | **double asterisks** 702 | 703 | __double underscores__ 704 | 705 | will produce: 706 | 707 | single asterisks 708 | 709 | single underscores 710 | 711 | double asterisks 712 | 713 | double underscores 714 | 715 | You can use whichever style you prefer; the lone restriction is that 716 | the same character must be used to open and close an emphasis span. 717 | 718 | Emphasis can be used in the middle of a word: 719 | 720 | un*fucking*believable 721 | 722 | But if you surround an `*` or `_` with spaces, it'll be treated as a 723 | literal asterisk or underscore. 724 | 725 | To produce a literal asterisk or underscore at a position where it 726 | would otherwise be used as an emphasis delimiter, you can backslash 727 | escape it: 728 | 729 | \*this text is surrounded by literal asterisks\* 730 | 731 | 732 | 733 |

Code

734 | 735 | To indicate a span of code, wrap it with backtick quotes (`` ` ``). 736 | Unlike a pre-formatted code block, a code span indicates code within a 737 | normal paragraph. For example: 738 | 739 | Use the `printf()` function. 740 | 741 | will produce: 742 | 743 |

Use the printf() function.

744 | 745 | To include a literal backtick character within a code span, you can use 746 | multiple backticks as the opening and closing delimiters: 747 | 748 | ``There is a literal backtick (`) here.`` 749 | 750 | which will produce this: 751 | 752 |

There is a literal backtick (`) here.

753 | 754 | The backtick delimiters surrounding a code span may include spaces -- 755 | one after the opening, one before the closing. This allows you to place 756 | literal backtick characters at the beginning or end of a code span: 757 | 758 | A single backtick in a code span: `` ` `` 759 | 760 | A backtick-delimited string in a code span: `` `foo` `` 761 | 762 | will produce: 763 | 764 |

A single backtick in a code span: `

765 | 766 |

A backtick-delimited string in a code span: `foo`

767 | 768 | With a code span, ampersands and angle brackets are encoded as HTML 769 | entities automatically, which makes it easy to include example HTML 770 | tags. Markdown will turn this: 771 | 772 | Please don't use any `` tags. 773 | 774 | into: 775 | 776 |

Please don't use any <blink> tags.

777 | 778 | You can write this: 779 | 780 | `—` is the decimal-encoded equivalent of `—`. 781 | 782 | to produce: 783 | 784 |

&#8212; is the decimal-encoded 785 | equivalent of &mdash;.

786 | 787 | 788 | 789 |

Images

790 | 791 | Admittedly, it's fairly difficult to devise a "natural" syntax for 792 | placing images into a plain text document format. 793 | 794 | Markdown uses an image syntax that is intended to resemble the syntax 795 | for links, allowing for two styles: *inline* and *reference*. 796 | 797 | Inline image syntax looks like this: 798 | 799 | ![Alt text](/path/to/img.jpg) 800 | 801 | ![Alt text](/path/to/img.jpg "Optional title") 802 | 803 | That is: 804 | 805 | * An exclamation mark: `!`; 806 | * followed by a set of square brackets, containing the `alt` 807 | attribute text for the image; 808 | * followed by a set of parentheses, containing the URL or path to 809 | the image, and an optional `title` attribute enclosed in double 810 | or single quotes. 811 | 812 | Reference-style image syntax looks like this: 813 | 814 | ![Alt text][id] 815 | 816 | Where "id" is the name of a defined image reference. Image references 817 | are defined using syntax identical to link references: 818 | 819 | [id]: url/to/image "Optional title attribute" 820 | 821 | As of this writing, Markdown has no syntax for specifying the 822 | dimensions of an image; if this is important to you, you can simply 823 | use regular HTML `` tags. 824 | 825 | 826 | * * * 827 | 828 | 829 |

Miscellaneous

830 | 831 | 832 | 833 | Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: 834 | 835 | 836 | 837 | Markdown will turn this into: 838 | 839 | http://example.com/ 840 | 841 | Automatic links for email addresses work similarly, except that 842 | Markdown will also perform a bit of randomized decimal and hex 843 | entity-encoding to help obscure your address from address-harvesting 844 | spambots. For example, Markdown will turn this: 845 | 846 | 847 | 848 | into something like this: 849 | 850 | address@exa 853 | mple.com 854 | 855 | which will render in a browser as a clickable link to "address@example.com". 856 | 857 | (This sort of entity-encoding trick will indeed fool many, if not 858 | most, address-harvesting bots, but it definitely won't fool all of 859 | them. It's better than nothing, but an address published in this way 860 | will probably eventually start receiving spam.) 861 | 862 | 863 | 864 |

Backslash Escapes

865 | 866 | Markdown allows you to use backslash escapes to generate literal 867 | characters which would otherwise have special meaning in Markdown's 868 | formatting syntax. For example, if you wanted to surround a word with 869 | literal asterisks (instead of an HTML `` tag), you can backslashes 870 | before the asterisks, like this: 871 | 872 | \*literal asterisks\* 873 | 874 | Markdown provides backslash escapes for the following characters: 875 | 876 | \ backslash 877 | ` backtick 878 | * asterisk 879 | _ underscore 880 | {} curly braces 881 | [] square brackets 882 | () parentheses 883 | # hash mark 884 | + plus sign 885 | - minus sign (hyphen) 886 | . dot 887 | ! exclamation mark 888 | 889 | -------------------------------------------------------------------------------- /testdata_source/Nested blockquotes.md: -------------------------------------------------------------------------------- 1 | > foo 2 | > 3 | > > bar 4 | > 5 | > foo 6 | -------------------------------------------------------------------------------- /testdata_source/Ordered and unordered lists.md: -------------------------------------------------------------------------------- 1 | ## Unordered 2 | 3 | Asterisks tight: 4 | 5 | * asterisk 1 6 | * asterisk 2 7 | * asterisk 3 8 | 9 | 10 | Asterisks loose: 11 | 12 | * asterisk 1 13 | 14 | * asterisk 2 15 | 16 | * asterisk 3 17 | 18 | * * * 19 | 20 | Pluses tight: 21 | 22 | + Plus 1 23 | + Plus 2 24 | + Plus 3 25 | 26 | 27 | Pluses loose: 28 | 29 | + Plus 1 30 | 31 | + Plus 2 32 | 33 | + Plus 3 34 | 35 | * * * 36 | 37 | 38 | Minuses tight: 39 | 40 | - Minus 1 41 | - Minus 2 42 | - Minus 3 43 | 44 | 45 | Minuses loose: 46 | 47 | - Minus 1 48 | 49 | - Minus 2 50 | 51 | - Minus 3 52 | 53 | 54 | ## Ordered 55 | 56 | Tight: 57 | 58 | 1. First 59 | 2. Second 60 | 3. Third 61 | 62 | and: 63 | 64 | 1. One 65 | 2. Two 66 | 3. Three 67 | 68 | 69 | Loose using tabs: 70 | 71 | 1. First 72 | 73 | 2. Second 74 | 75 | 3. Third 76 | 77 | and using spaces: 78 | 79 | 1. One 80 | 81 | 2. Two 82 | 83 | 3. Three 84 | 85 | Multiple paragraphs: 86 | 87 | 1. Item 1, graf one. 88 | 89 | Item 2. graf two. The quick brown fox jumped over the lazy dog's 90 | back. 91 | 92 | 2. Item 2. 93 | 94 | 3. Item 3. 95 | 96 | 97 | 98 | ## Nested 99 | 100 | * Tab 101 | * Tab 102 | * Tab 103 | 104 | Here's another: 105 | 106 | 1. First 107 | 2. Second: 108 | * Fee 109 | * Fie 110 | * Foe 111 | 3. Third 112 | 113 | Same thing but with paragraphs: 114 | 115 | 1. First 116 | 117 | 2. Second: 118 | * Fee 119 | * Fie 120 | * Foe 121 | 122 | 3. Third 123 | 124 | 125 | This was an error in Markdown 1.0.1: 126 | 127 | * this 128 | 129 | * sub 130 | 131 | that 132 | -------------------------------------------------------------------------------- /testdata_source/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

git-bug

5 | 6 |
7 | 8 | [![Build Status](https://travis-ci.org/MichaelMure/git-bug.svg?branch=master)](https://travis-ci.org/MichaelMure/git-bug) 9 | [![Backers on Open Collective](https://opencollective.com/git-bug/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/git-bug/sponsors/badge.svg)](#sponsors) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3+-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) 10 | [![GoDoc](https://godoc.org/github.com/MichaelMure/git-bug?status.svg)](https://godoc.org/github.com/MichaelMure/git-bug) 11 | [![Go Report Card](https://goreportcard.com/badge/github.com/MichaelMure/git-bug)](https://goreportcard.com/report/github.com/MichaelMure/git-bug) 12 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/the-git-bug/Lobby) 13 | 14 |
15 | 16 | `git-bug` is a bug tracker that: 17 | 18 | - **fully embed in git**: you only need your git repository to have a bug tracker 19 | - **is distributed**: use your normal git remote to collaborate, push and pull your bugs ! 20 | - **works offline**: in a plane or under the sea ? keep reading and writing bugs 21 | - **prevent vendor locking**: your usual service is down or went bad ? you already have a full backup 22 | - **is fast**: listing bugs or opening them is a matter of milliseconds 23 | - **doesn't pollute your project**: no files are added in your project 24 | - **integrate with your tooling**: use the UI you like (CLI, terminal, web) or integrate with your existing tools through the CLI or the GraphQL API 25 | - **bridge with other bug trackers**: [bridges](#bridges) exist to import and soon export to other trackers. 26 | 27 | :construction: This is now more than a proof of concept, but still not fully stable. Expect dragons and unfinished business. :construction: 28 | 29 | ## Install 30 | 31 |
go get 32 | 33 | ```shell 34 | go get -u github.com/MichaelMure/git-bug 35 | ``` 36 | 37 | If it's not done already, add golang binary directory in your PATH: 38 | 39 | ```bash 40 | export PATH=$PATH:$(go env GOROOT)/bin:$(go env GOPATH)/bin 41 | ``` 42 | 43 |
44 | 45 |
Pre-compiled binaries 46 | 47 | 1. Go to the [release page](https://github.com/MichaelMure/git-bug/releases/latest) and download the appropriate binary for your system. 48 | 2. Copy the binary anywhere in your PATH 49 | 3. Rename the binary to `git-bug` (or `git-bug.exe` on windows) 50 | 51 | That's all ! 52 | 53 |
54 | 55 |
Linux packages 56 | 57 | * [Archlinux (AUR)](https://aur.archlinux.org/packages/?K=git-bug) 58 | 59 |
60 | 61 | ## CLI usage 62 | 63 | Create a new identity: 64 | 65 | ``` 66 | git bug user create 67 | ``` 68 | 69 | Create a new bug: 70 | 71 | ``` 72 | git bug add 73 | ``` 74 | 75 | Your favorite editor will open to write a title and a message. 76 | 77 | You can push your new entry to a remote: 78 | ``` 79 | git bug push [] 80 | ``` 81 | 82 | And pull for updates: 83 | ``` 84 | git bug pull [] 85 | ``` 86 | 87 | List existing bugs: 88 | ``` 89 | git bug ls 90 | ``` 91 | 92 | Filter and sort bugs using a [query](doc/queries.md): 93 | ``` 94 | git bug ls "status:open sort:edit" 95 | ``` 96 | 97 | You can now use commands like `show`, `comment`, `open` or `close` to display and modify bugs. For more details about each command, you can run `git bug --help` or read the [command's documentation](doc/md/git-bug.md). 98 | 99 | ## Interactive terminal UI 100 | 101 | An interactive terminal UI is available using the command `git bug termui` to browse and edit bugs. 102 | 103 | ![Termui recording](testdata_media/termui_recording.gif) 104 | 105 | ## Web UI (status: WIP) 106 | 107 | You can launch a rich Web UI with `git bug webui`. 108 | 109 | ![Web UI screenshot 1](testdata_media/webui1.png) 110 | ![Web UI screenshot 2](testdata_media/webui2.png) 111 | 112 | This web UI is entirely packed inside the same go binary and serve static content through a localhost http server. 113 | 114 | The web UI interact with the backend through a GraphQL API. The schema is available [here](graphql/). 115 | 116 | ## Bridges 117 | 118 | ### Importer implementations 119 | 120 | | | Github | Launchpad | 121 | | ----------------------------------------------- | ---------------- | :----------------: | 122 | | **incremental**
(can import more than once) | :heavy_check_mark: | :x: | 123 | | **with resume**
(download only new data) | :x: | :x: | 124 | | **identities** | :heavy_check_mark: | :heavy_check_mark: | 125 | | identities update | :x: | :x: | 126 | | **bug** | :heavy_check_mark: | :heavy_check_mark: | 127 | | comments | :heavy_check_mark: | :heavy_check_mark: | 128 | | comment editions | :heavy_check_mark: | :x: | 129 | | labels | :heavy_check_mark: | :x: | 130 | | status | :heavy_check_mark: | :x: | 131 | | title edition | :heavy_check_mark: | :x: | 132 | | **automated test suite** | :x: | :x: | 133 | 134 | ### Exporter implementations 135 | 136 | Todo ! 137 | 138 | ## Internals 139 | 140 | Interested by how it works ? Have a look at the [data model](doc/model.md) and the [internal bird-view](doc/architecture.md). 141 | 142 | ## Misc 143 | 144 | - [Bash completion](../misc/bash_completion) 145 | - [Zsh completion](../misc/zsh_completion) 146 | - [ManPages](doc/man) 147 | 148 | ## Planned features 149 | 150 | - media embedding 151 | - exporter to github issue 152 | - extendable data model to support arbitrary bug tracker 153 | - inflatable raptor 154 | 155 | ## Contribute 156 | 157 | PRs accepted. Drop by the [Gitter lobby](https://gitter.im/the-git-bug/Lobby) for a chat or browse the issues to see what is worked on or discussed. 158 | 159 | Developers unfamiliar with Go may try to clone the repository using "git clone". Instead, one should use: 160 | 161 | ```shell 162 | go get -u github.com/MichaelMure/git-bug 163 | ``` 164 | 165 | The git repository will then be available: 166 | 167 | ```shell 168 | # Note that $GOPATH defaults to $HOME/go 169 | $ cd $GOPATH/src/github.com/MichaelMure/git-bug/ 170 | ``` 171 | 172 | You can now run `make` to build the project, or `make install` to install the binary in `$GOPATH/bin/`. 173 | 174 | To work on the web UI, have a look at [the dedicated Readme.](webui/Readme.md) 175 | 176 | 177 | ## Contributors :heart: 178 | 179 | This project exists thanks to all the people who contribute. 180 | 181 | 182 | 183 | ## Backers 184 | 185 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/git-bug#backer)] 186 | 187 | 188 | 189 | 190 | ## Sponsors 191 | 192 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/git-bug#sponsor)] 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | ## License 207 | 208 | Unless otherwise stated, this project is released under the [GPLv3](LICENSE) or later license © Michael Muré. 209 | 210 | The git-bug logo by [Viktor Teplov](https://github.com/vandesign) is released under the [Creative Commons Attribution 4.0 International (CC BY 4.0)](../misc/logo/LICENSE) license © Viktor Teplov. 211 | -------------------------------------------------------------------------------- /testdata_source/Table.md: -------------------------------------------------------------------------------- 1 | Markdown | Less | Pretty 2 | --- | --- | --- 3 | *Still* | `renders` | **nicely** 4 | 1 | 2 | 3 5 | 6 | Colons can be used to align columns. 7 | 8 | | Tables | Are | Cool | 9 | | ------------- |:-------------:| -----:| 10 | | col 3 is | right-aligned | $1600 | 11 | | col 2 is | centered | $12 | 12 | | zebra stripes | are neat | $1 | 13 | |
  • item1
  • item2
| See the list | from the first column| 14 | | [URL and title](/url/ "title"). | | | 15 | | Emphasis, aka italics, with *asterisks* or _underscores_. | Strong emphasis, aka bold, with **asterisks** or __underscores__. | Strikethrough uses two tildes. ~~Scratch this.~~ | 16 | | ![GitHub Logo](/images/logo.png) | | | 17 | 18 | There must be at least 3 dashes separating each header cell. 19 | The outer pipes (|) are optional, and you don't need to make the 20 | raw Markdown line up prettily. You can also use inline Markdown. 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
TablesAreCool
col 3 isright-aligned$1600
col 2 iscentered$12
zebra stripesare neat$1
<ul><li>item1</li><li>item2</li></ul>See the listfrom the first column
URL and title.
Emphasis, aka italics, with asterisks or underscores.Strong emphasis, aka bold, with asterisks or underscores.Strikethrough uses two tildes. Scratch this.
GitHub Logo
68 | -------------------------------------------------------------------------------- /testdata_source/Tabs.md: -------------------------------------------------------------------------------- 1 | + this is a list item 2 | indented with tabs 3 | 4 | + this is a list item 5 | indented with spaces 6 | 7 | Code: 8 | 9 | this code block is indented by one tab 10 | 11 | And: 12 | 13 | this code block is indented by two tabs 14 | 15 | And: 16 | 17 | + this is an example list item 18 | indented with tabs 19 | 20 | + this is an example list item 21 | indented with spaces 22 | -------------------------------------------------------------------------------- /testdata_source/Tidyness.md: -------------------------------------------------------------------------------- 1 | > A list within a blockquote: 2 | > 3 | > * asterisk 1 4 | > * asterisk 2 5 | > * asterisk 3 6 | -------------------------------------------------------------------------------- /testdata_source/break.md: -------------------------------------------------------------------------------- 1 | some content 2 | more content 3 | 4 | -------------------------------------------------------------------------------- /testdata_source/code.md: -------------------------------------------------------------------------------- 1 | `` 2 | 3 | Fix for backticks within HTML tag: like this 4 | 5 | Here's how you put `` `backticks` `` in a code span. 6 | -------------------------------------------------------------------------------- /testdata_source/codeblock.md: -------------------------------------------------------------------------------- 1 | Here is a piece of code: 2 | 3 | ```go 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | fmt.Println("Hello world") 10 | } 11 | ``` 12 | 13 | Another one, tagged as well with the language name 14 | 15 | ```c 16 | #include 17 | int main() 18 | { 19 | printf("Hello, World!"); 20 | return 0; 21 | } 22 | ``` 23 | 24 | This one is not: 25 | 26 | ``` 27 | #include 28 | int main() 29 | { 30 | printf("Hello, World!"); 31 | return 0; 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /testdata_source/emoji.md: -------------------------------------------------------------------------------- 1 | Emoji support ! 2 | 3 | :bowtie::smile::simple_smile::laughing::blush::smiley::relaxed::smirk::heart_eyes::kissing_heart::kissing_closed_eyes::flushed::relieved::satisfied::grin::wink::stuck_out_tongue_winking_eye::stuck_out_tongue_closed_eyes::grinning::kissing::kissing_smiling_eyes::stuck_out_tongue::sleeping::worried::frowning::anguished::open_mouth::grimacing::confused::hushed::expressionless::unamused::sweat_smile::sweat::disappointed_relieved::weary::pensive::disappointed::confounded::fearful::cold_sweat::persevere::cry::sob::joy::astonished::scream::neckbeard::tired_face: 4 | 5 | 😏 😍 😘 😚 😳 😌 😆 😄 😉 😜 😝 😀 6 | 😗 😙 😛 😴 😟 😦 😧 😮 😬 😕 😯 😑 7 | 😒 😅 😓 😥 😩 😔 😞 😖 😨 😰 😣 😢 8 | -------------------------------------------------------------------------------- /testdata_source/emphasis.md: -------------------------------------------------------------------------------- 1 | Emphasis, aka italics, with *asterisks* or _underscores_. 2 | 3 | Strong emphasis, aka bold, with **asterisks** or __underscores__. 4 | 5 | Bold and italic with ***asterisks*** or ___underscores___. 6 | 7 | Strikethrough uses two tildes. ~~Scratch this.~~ 8 | 9 | ~~*striked italic*~~ 10 | 11 | ~~**striked bold**~~ 12 | 13 | ~~***striked bold and italic***~~ 14 | -------------------------------------------------------------------------------- /testdata_source/headings.md: -------------------------------------------------------------------------------- 1 | # heading 1 2 | ## heading 2 3 | ## heading 2 4 | ## heading 2 5 | ### heading 3 6 | #### heading 4 7 | ##### heading 5 8 | ###### heading 6 9 | ### heading 3 10 | 11 | 12 | ### link [my link](http://example.com) 13 | 14 | ### image ![img](http://example.com/img.png) 15 | 16 | ### link + img [![img](http://example.com/img.png)](http://example.com) 17 | 18 | ### code `ìnt x = 7` 19 | 20 | ### *emphasis* _emphasis_ 21 | 22 | ### ~~strikethrough~~ 23 | 24 | ### _bold_ __bold__ 25 | 26 | ###
foo
27 | 28 | ### comment html -------------------------------------------------------------------------------- /testdata_source/horizontal_rule.md: -------------------------------------------------------------------------------- 1 | Three or more... 2 | 3 | --- 4 | 5 | Hyphens 6 | 7 | *** 8 | 9 | Asterisks 10 | 11 | ___ 12 | 13 | Underscores -------------------------------------------------------------------------------- /testdata_source/image.md: -------------------------------------------------------------------------------- 1 | ![GitHub Logo](testdata_media/webui1.png) 2 | Format: ![Alt Text](url) 3 | Format: ![Alt Text](url) 4 | 5 |
6 | alt text 7 | 8 |
9 | 10 | alt text 11 | 12 | 13 | -------------------------------------------------------------------------------- /testdata_source/list.md: -------------------------------------------------------------------------------- 1 | 1. First ordered list item 2 | 2. Another item 3 | * Unordered sub-list. 4 | 1. Actual numbers don't matter, just that it's a number 5 | 1. Ordered sub-list 6 | 4. And another item. 7 | 8 | A definition list: 9 | 10 | Markdown 11 | : Text-to-HTML conversion tool 12 | 13 | With a link [url](http://example.com) 14 | : That's a definition list with an url in the header 15 | 16 | - [x] Write the press release 17 | - [ ] Update the website 18 | - [ ] Contact the media 19 | 20 | 1. [x] Write the press release 21 | 1. [ ] Update the website 22 | 1. [ ] Contact the media 23 | -------------------------------------------------------------------------------- /testdata_source/quote.md: -------------------------------------------------------------------------------- 1 | A paragraph. 2 | 3 | > Quoted text. 4 | > Quoted text. 5 | > Quoted text 6 | > 7 | > > Quoted quote. 8 | > > Quoted quote. 9 | > 10 | > Quoted text. 11 | > Quoted text. 12 | 13 | A paragraph. 14 | 15 | > * Quoted 16 | > * List 17 | 18 | A paragraph. 19 | --------------------------------------------------------------------------------