pax_global_header00006660000000000000000000000064137457141520014523gustar00rootroot0000000000000052 comment=4c9bf9512682b995722660a4196c0013228e2049 blackfriday-2.1.0/000077500000000000000000000000001374571415200137765ustar00rootroot00000000000000blackfriday-2.1.0/.gitignore000066400000000000000000000000561374571415200157670ustar00rootroot00000000000000*.out *.swp *.8 *.6 _obj _test* markdown tags blackfriday-2.1.0/.travis.yml000066400000000000000000000006111374571415200161050ustar00rootroot00000000000000sudo: false language: go go: - "1.10.x" - "1.11.x" - tip matrix: fast_finish: true allow_failures: - go: tip install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - go tool vet . - go test -v ./... blackfriday-2.1.0/LICENSE.txt000066400000000000000000000026101374571415200156200ustar00rootroot00000000000000Blackfriday is distributed under the Simplified BSD License: > Copyright © 2011 Russ Ross > All rights reserved. > > Redistribution and use in source and binary forms, with or without > modification, are permitted provided that the following conditions > are met: > > 1. Redistributions of source code must retain the above copyright > notice, this list of conditions and the following disclaimer. > > 2. Redistributions in binary form must reproduce the above > copyright notice, this list of conditions and the following > disclaimer in the documentation and/or other materials provided with > the distribution. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS > FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, > BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER > CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN > ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE > POSSIBILITY OF SUCH DAMAGE. blackfriday-2.1.0/README.md000066400000000000000000000277141374571415200152700ustar00rootroot00000000000000Blackfriday [![Build Status][BuildV2SVG]][BuildV2URL] [![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL] =========== Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It is paranoid about its input (so you can safely feed it user-supplied data), it is fast, it supports common extensions (tables, smart punctuation substitutions, etc.), and it is safe for all utf-8 (unicode) input. HTML output is currently supported, along with Smartypants extensions. It started as a translation from C of [Sundown][3]. Installation ------------ Blackfriday is compatible with modern Go releases in module mode. With Go installed: go get github.com/russross/blackfriday/v2 will resolve and add the package to the current development module, then build and install it. Alternatively, you can achieve the same if you import it in a package: import "github.com/russross/blackfriday/v2" and `go get` without parameters. Legacy GOPATH mode is unsupported. Versions -------- Currently maintained and recommended version of Blackfriday is `v2`. It's being developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the documentation is available at https://pkg.go.dev/github.com/russross/blackfriday/v2. It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`. Version 2 offers a number of improvements over v1: * Cleaned up API * A separate call to [`Parse`][4], which produces an abstract syntax tree for the document * Latest bug fixes * Flexibility to easily add your own rendering extensions Potential drawbacks: * Our benchmarks show v2 to be slightly slower than v1. Currently in the ballpark of around 15%. * API breakage. If you can't afford modifying your code to adhere to the new API and don't care too much about the new features, v2 is probably not for you. * Several bug fixes are trailing behind and still need to be forward-ported to v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for tracking. If you are still interested in the legacy `v1`, you can import it from `github.com/russross/blackfriday`. Documentation for the legacy v1 can be found here: https://pkg.go.dev/github.com/russross/blackfriday. Usage ----- For the most sensible markdown processing, it is as simple as getting your input into a byte slice and calling: ```go output := blackfriday.Run(input) ``` Your input will be parsed and the output rendered with a set of most popular extensions enabled. If you want the most basic feature set, corresponding with the bare Markdown specification, use: ```go output := blackfriday.Run(input, blackfriday.WithNoExtensions()) ``` ### Sanitize untrusted content Blackfriday itself does nothing to protect against malicious content. If you are dealing with user-supplied markdown, we recommend running Blackfriday's output through HTML sanitizer such as [Bluemonday][5]. Here's an example of simple usage of Blackfriday together with Bluemonday: ```go import ( "github.com/microcosm-cc/bluemonday" "github.com/russross/blackfriday/v2" ) // ... unsafe := blackfriday.Run(input) html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) ``` ### Custom options If you want to customize the set of options, use `blackfriday.WithExtensions`, `blackfriday.WithRenderer` and `blackfriday.WithRefOverride`. ### `blackfriday-tool` You can also check out `blackfriday-tool` for a more complete example of how to use it. Download and install it using: go get github.com/russross/blackfriday-tool This is a simple command-line tool that allows you to process a markdown file using a standalone program. You can also browse the source directly on github if you are just looking for some example code: * Note that if you have not already done so, installing `blackfriday-tool` will be sufficient to download and install blackfriday in addition to the tool itself. The tool binary will be installed in `$GOPATH/bin`. This is a statically-linked binary that can be copied to wherever you need it without worrying about dependencies and library versions. ### Sanitized anchor names Blackfriday includes an algorithm for creating sanitized anchor names corresponding to a given input text. This algorithm is used to create anchors for headings when `AutoHeadingIDs` extension is enabled. The algorithm has a specification, so that other packages can create compatible anchor names and links to those anchors. The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names. [`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to create compatible links to the anchor names generated by blackfriday. This algorithm is also implemented in a small standalone package at [`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients that want a small package and don't need full functionality of blackfriday. Features -------- All features of Sundown are supported, including: * **Compatibility**. The Markdown v1.0.3 test suite passes with the `--tidy` option. Without `--tidy`, the differences are mostly in whitespace and entity escaping, where blackfriday is more consistent and cleaner. * **Common extensions**, including table support, fenced code blocks, autolinks, strikethroughs, non-strict emphasis, etc. * **Safety**. Blackfriday is paranoid when parsing, making it safe to feed untrusted user input without fear of bad things happening. The test suite stress tests this and there are no known inputs that make it crash. If you find one, please let me know and send me the input that does it. NOTE: "safety" in this context means *runtime safety only*. In order to protect yourself against JavaScript injection in untrusted content, see [this example](https://github.com/russross/blackfriday#sanitize-untrusted-content). * **Fast processing**. It is fast enough to render on-demand in most web applications without having to cache the output. * **Thread safety**. You can run multiple parsers in different goroutines without ill effect. There is no dependence on global shared state. * **Minimal dependencies**. Blackfriday only depends on standard library packages in Go. The source code is pretty self-contained, so it is easy to add to any project, including Google App Engine projects. * **Standards compliant**. Output successfully validates using the W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional. Extensions ---------- In addition to the standard markdown syntax, this package implements the following extensions: * **Intra-word emphasis supression**. The `_` character is commonly used inside words when discussing code, so having markdown interpret it as an emphasis command is usually the wrong thing. Blackfriday lets you treat all emphasis markers as normal characters when they occur inside a word. * **Tables**. Tables can be created by drawing them in the input using a simple syntax: ``` Name | Age --------|------ Bob | 27 Alice | 23 ``` * **Fenced code blocks**. In addition to the normal 4-space indentation to mark code blocks, you can explicitly mark them and supply a language (to make syntax highlighting simple). Just mark it like this: ```go func getTrue() bool { return true } ``` You can use 3 or more backticks to mark the beginning of the block, and the same number to mark the end of the block. To preserve classes of fenced code blocks while using the bluemonday HTML sanitizer, use the following policy: ```go p := bluemonday.UGCPolicy() p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code") html := p.SanitizeBytes(unsafe) ``` * **Definition lists**. A simple definition list is made of a single-line term followed by a colon and the definition for that term. Cat : Fluffy animal everyone likes Internet : Vector of transmission for pictures of cats Terms must be separated from the previous definition by a blank line. * **Footnotes**. A marker in the text that will become a superscript number; a footnote definition that will be placed in a list of footnotes at the end of the document. A footnote looks like this: This is a footnote.[^1] [^1]: the footnote text. * **Autolinking**. Blackfriday can find URLs that have not been explicitly marked as links and turn them into links. * **Strikethrough**. Use two tildes (`~~`) to mark text that should be crossed out. * **Hard line breaks**. With this extension enabled newlines in the input translate into line breaks in the output. This extension is off by default. * **Smart quotes**. Smartypants-style punctuation substitution is supported, turning normal double- and single-quote marks into curly quotes, etc. * **LaTeX-style dash parsing** is an additional option, where `--` is translated into `–`, and `---` is translated into `—`. This differs from most smartypants processors, which turn a single hyphen into an ndash and a double hyphen into an mdash. * **Smart fractions**, where anything that looks like a fraction is translated into suitable HTML (instead of just a few special cases like most smartypant processors). For example, `4/5` becomes `45`, which renders as 45. Other renderers --------------- Blackfriday is structured to allow alternative rendering engines. Here are a few of note: * [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown): provides a GitHub Flavored Markdown renderer with fenced code block highlighting, clickable heading anchor links. It's not customizable, and its goal is to produce HTML output equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode), except the rendering is performed locally. * [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt, but for markdown. * [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex): renders output as LaTeX. * [bfchroma](https://github.com/Depado/bfchroma/): provides convenience integration with the [Chroma](https://github.com/alecthomas/chroma) code highlighting library. bfchroma is only compatible with v2 of Blackfriday and provides a drop-in renderer ready to use with Blackfriday, as well as options and means for further customization. * [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer. * [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style TODO ---- * More unit testing * Improve Unicode support. It does not understand all Unicode rules (about what constitutes a letter, a punctuation symbol, etc.), so it may fail to detect word boundaries correctly in some instances. It is safe on all UTF-8 input. License ------- [Blackfriday is distributed under the Simplified BSD License](LICENSE.txt) [1]: https://daringfireball.net/projects/markdown/ "Markdown" [2]: https://golang.org/ "Go Language" [3]: https://github.com/vmg/sundown "Sundown" [4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func" [5]: https://github.com/microcosm-cc/bluemonday "Bluemonday" [BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2 [BuildV2URL]: https://travis-ci.org/russross/blackfriday [PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2 [PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2 blackfriday-2.1.0/block.go000066400000000000000000001033151374571415200154220ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Functions to parse block-level elements. // package blackfriday import ( "bytes" "html" "regexp" "strings" "unicode" ) const ( charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});" escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]" ) var ( reBackslashOrAmp = regexp.MustCompile("[\\&]") reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + escapable + "|" + charEntity) ) // Parse block-level data. // Note: this function and many that it calls assume that // the input buffer ends with a newline. func (p *Markdown) block(data []byte) { // this is called recursively: enforce a maximum depth if p.nesting >= p.maxNesting { return } p.nesting++ // parse out one block-level construct at a time for len(data) > 0 { // prefixed heading: // // # Heading 1 // ## Heading 2 // ... // ###### Heading 6 if p.isPrefixHeading(data) { data = data[p.prefixHeading(data):] continue } // block of preformatted HTML: // //
// ... //
if data[0] == '<' { if i := p.html(data, true); i > 0 { data = data[i:] continue } } // title block // // % stuff // % more stuff // % even more stuff if p.extensions&Titleblock != 0 { if data[0] == '%' { if i := p.titleBlock(data, true); i > 0 { data = data[i:] continue } } } // blank lines. note: returns the # of bytes to skip if i := p.isEmpty(data); i > 0 { data = data[i:] continue } // indented code block: // // func max(a, b int) int { // if a > b { // return a // } // return b // } if p.codePrefix(data) > 0 { data = data[p.code(data):] continue } // fenced code block: // // ``` go // func fact(n int) int { // if n <= 1 { // return n // } // return n * fact(n-1) // } // ``` if p.extensions&FencedCode != 0 { if i := p.fencedCodeBlock(data, true); i > 0 { data = data[i:] continue } } // horizontal rule: // // ------ // or // ****** // or // ______ if p.isHRule(data) { p.addBlock(HorizontalRule, nil) var i int for i = 0; i < len(data) && data[i] != '\n'; i++ { } data = data[i:] continue } // block quote: // // > A big quote I found somewhere // > on the web if p.quotePrefix(data) > 0 { data = data[p.quote(data):] continue } // table: // // Name | Age | Phone // ------|-----|--------- // Bob | 31 | 555-1234 // Alice | 27 | 555-4321 if p.extensions&Tables != 0 { if i := p.table(data); i > 0 { data = data[i:] continue } } // an itemized/unordered list: // // * Item 1 // * Item 2 // // also works with + or - if p.uliPrefix(data) > 0 { data = data[p.list(data, 0):] continue } // a numbered/ordered list: // // 1. Item 1 // 2. Item 2 if p.oliPrefix(data) > 0 { data = data[p.list(data, ListTypeOrdered):] continue } // definition lists: // // Term 1 // : Definition a // : Definition b // // Term 2 // : Definition c if p.extensions&DefinitionLists != 0 { if p.dliPrefix(data) > 0 { data = data[p.list(data, ListTypeDefinition):] continue } } // anything else must look like a normal paragraph // note: this finds underlined headings, too data = data[p.paragraph(data):] } p.nesting-- } func (p *Markdown) addBlock(typ NodeType, content []byte) *Node { p.closeUnmatchedBlocks() container := p.addChild(typ, 0) container.content = content return container } func (p *Markdown) isPrefixHeading(data []byte) bool { if data[0] != '#' { return false } if p.extensions&SpaceHeadings != 0 { level := 0 for level < 6 && level < len(data) && data[level] == '#' { level++ } if level == len(data) || data[level] != ' ' { return false } } return true } func (p *Markdown) prefixHeading(data []byte) int { level := 0 for level < 6 && level < len(data) && data[level] == '#' { level++ } i := skipChar(data, level, ' ') end := skipUntilChar(data, i, '\n') skip := end id := "" if p.extensions&HeadingIDs != 0 { j, k := 0, 0 // find start/end of heading id for j = i; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ { } for k = j + 1; k < end && data[k] != '}'; k++ { } // extract heading id iff found if j < end && k < end { id = string(data[j+2 : k]) end = j skip = k + 1 for end > 0 && data[end-1] == ' ' { end-- } } } for end > 0 && data[end-1] == '#' { if isBackslashEscaped(data, end-1) { break } end-- } for end > 0 && data[end-1] == ' ' { end-- } if end > i { if id == "" && p.extensions&AutoHeadingIDs != 0 { id = SanitizedAnchorName(string(data[i:end])) } block := p.addBlock(Heading, data[i:end]) block.HeadingID = id block.Level = level } return skip } func (p *Markdown) isUnderlinedHeading(data []byte) int { // test of level 1 heading if data[0] == '=' { i := skipChar(data, 1, '=') i = skipChar(data, i, ' ') if i < len(data) && data[i] == '\n' { return 1 } return 0 } // test of level 2 heading if data[0] == '-' { i := skipChar(data, 1, '-') i = skipChar(data, i, ' ') if i < len(data) && data[i] == '\n' { return 2 } return 0 } return 0 } func (p *Markdown) titleBlock(data []byte, doRender bool) int { if data[0] != '%' { return 0 } splitData := bytes.Split(data, []byte("\n")) var i int for idx, b := range splitData { if !bytes.HasPrefix(b, []byte("%")) { i = idx // - 1 break } } data = bytes.Join(splitData[0:i], []byte("\n")) consumed := len(data) data = bytes.TrimPrefix(data, []byte("% ")) data = bytes.Replace(data, []byte("\n% "), []byte("\n"), -1) block := p.addBlock(Heading, data) block.Level = 1 block.IsTitleblock = true return consumed } func (p *Markdown) html(data []byte, doRender bool) int { var i, j int // identify the opening tag if data[0] != '<' { return 0 } curtag, tagfound := p.htmlFindTag(data[1:]) // handle special cases if !tagfound { // check for an HTML comment if size := p.htmlComment(data, doRender); size > 0 { return size } // check for an
tag if size := p.htmlHr(data, doRender); size > 0 { return size } // no special case recognized return 0 } // look for an unindented matching closing tag // followed by a blank line found := false /* closetag := []byte("\n") j = len(curtag) + 1 for !found { // scan for a closing tag at the beginning of a line if skip := bytes.Index(data[j:], closetag); skip >= 0 { j += skip + len(closetag) } else { break } // see if it is the only thing on the line if skip := p.isEmpty(data[j:]); skip > 0 { // see if it is followed by a blank line/eof j += skip if j >= len(data) { found = true i = j } else { if skip := p.isEmpty(data[j:]); skip > 0 { j += skip found = true i = j } } } } */ // if not found, try a second pass looking for indented match // but not if tag is "ins" or "del" (following original Markdown.pl) if !found && curtag != "ins" && curtag != "del" { i = 1 for i < len(data) { i++ for i < len(data) && !(data[i-1] == '<' && data[i] == '/') { i++ } if i+2+len(curtag) >= len(data) { break } j = p.htmlFindEnd(curtag, data[i-1:]) if j > 0 { i += j - 1 found = true break } } } if !found { return 0 } // the end of the block has been found if doRender { // trim newlines end := i for end > 0 && data[end-1] == '\n' { end-- } finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end])) } return i } func finalizeHTMLBlock(block *Node) { block.Literal = block.content block.content = nil } // HTML comment, lax form func (p *Markdown) htmlComment(data []byte, doRender bool) int { i := p.inlineHTMLComment(data) // needs to end with a blank line if j := p.isEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim trailing newlines end := size for end > 0 && data[end-1] == '\n' { end-- } block := p.addBlock(HTMLBlock, data[:end]) finalizeHTMLBlock(block) } return size } return 0 } // HR, which is the only self-closing block tag considered func (p *Markdown) htmlHr(data []byte, doRender bool) int { if len(data) < 4 { return 0 } if data[0] != '<' || (data[1] != 'h' && data[1] != 'H') || (data[2] != 'r' && data[2] != 'R') { return 0 } if data[3] != ' ' && data[3] != '/' && data[3] != '>' { // not an
tag after all; at least not a valid one return 0 } i := 3 for i < len(data) && data[i] != '>' && data[i] != '\n' { i++ } if i < len(data) && data[i] == '>' { i++ if j := p.isEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim newlines end := size for end > 0 && data[end-1] == '\n' { end-- } finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end])) } return size } } return 0 } func (p *Markdown) htmlFindTag(data []byte) (string, bool) { i := 0 for i < len(data) && isalnum(data[i]) { i++ } key := string(data[:i]) if _, ok := blockTags[key]; ok { return key, true } return "", false } func (p *Markdown) htmlFindEnd(tag string, data []byte) int { // assume data[0] == '<' && data[1] == '/' already tested if tag == "hr" { return 2 } // check if tag is a match closetag := []byte("") if !bytes.HasPrefix(data, closetag) { return 0 } i := len(closetag) // check that the rest of the line is blank skip := 0 if skip = p.isEmpty(data[i:]); skip == 0 { return 0 } i += skip skip = 0 if i >= len(data) { return i } if p.extensions&LaxHTMLBlocks != 0 { return i } if skip = p.isEmpty(data[i:]); skip == 0 { // following line must be blank return 0 } return i + skip } func (*Markdown) isEmpty(data []byte) int { // it is okay to call isEmpty on an empty buffer if len(data) == 0 { return 0 } var i int for i = 0; i < len(data) && data[i] != '\n'; i++ { if data[i] != ' ' && data[i] != '\t' { return 0 } } if i < len(data) && data[i] == '\n' { i++ } return i } func (*Markdown) isHRule(data []byte) bool { i := 0 // skip up to three spaces for i < 3 && data[i] == ' ' { i++ } // look at the hrule char if data[i] != '*' && data[i] != '-' && data[i] != '_' { return false } c := data[i] // the whole line must be the char or whitespace n := 0 for i < len(data) && data[i] != '\n' { switch { case data[i] == c: n++ case data[i] != ' ': return false } i++ } return n >= 3 } // isFenceLine checks if there's a fence line (e.g., ``` or ``` go) at the beginning of data, // and returns the end index if so, or 0 otherwise. It also returns the marker found. // If info is not nil, it gets set to the syntax specified in the fence line. func isFenceLine(data []byte, info *string, oldmarker string) (end int, marker string) { i, size := 0, 0 // skip up to three spaces for i < len(data) && i < 3 && data[i] == ' ' { i++ } // check for the marker characters: ~ or ` if i >= len(data) { return 0, "" } if data[i] != '~' && data[i] != '`' { return 0, "" } c := data[i] // the whole line must be the same char or whitespace for i < len(data) && data[i] == c { size++ i++ } // the marker char must occur at least 3 times if size < 3 { return 0, "" } marker = string(data[i-size : i]) // if this is the end marker, it must match the beginning marker if oldmarker != "" && marker != oldmarker { return 0, "" } // TODO(shurcooL): It's probably a good idea to simplify the 2 code paths here // into one, always get the info string, and discard it if the caller doesn't care. if info != nil { infoLength := 0 i = skipChar(data, i, ' ') if i >= len(data) { if i == len(data) { return i, marker } return 0, "" } infoStart := i if data[i] == '{' { i++ infoStart++ for i < len(data) && data[i] != '}' && data[i] != '\n' { infoLength++ i++ } if i >= len(data) || data[i] != '}' { return 0, "" } // strip all whitespace at the beginning and the end // of the {} block for infoLength > 0 && isspace(data[infoStart]) { infoStart++ infoLength-- } for infoLength > 0 && isspace(data[infoStart+infoLength-1]) { infoLength-- } i++ i = skipChar(data, i, ' ') } else { for i < len(data) && !isverticalspace(data[i]) { infoLength++ i++ } } *info = strings.TrimSpace(string(data[infoStart : infoStart+infoLength])) } if i == len(data) { return i, marker } if i > len(data) || data[i] != '\n' { return 0, "" } return i + 1, marker // Take newline into account. } // fencedCodeBlock returns the end index if data contains a fenced code block at the beginning, // or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects. // If doRender is true, a final newline is mandatory to recognize the fenced code block. func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int { var info string beg, marker := isFenceLine(data, &info, "") if beg == 0 || beg >= len(data) { return 0 } fenceLength := beg - 1 var work bytes.Buffer work.Write([]byte(info)) work.WriteByte('\n') for { // safe to assume beg < len(data) // check for the end of the code block fenceEnd, _ := isFenceLine(data[beg:], nil, marker) if fenceEnd != 0 { beg += fenceEnd break } // copy the current line end := skipUntilChar(data, beg, '\n') + 1 // did we reach the end of the buffer without a closing marker? if end >= len(data) { return 0 } // verbatim copy to the working buffer if doRender { work.Write(data[beg:end]) } beg = end } if doRender { block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer block.IsFenced = true block.FenceLength = fenceLength finalizeCodeBlock(block) } return beg } func unescapeChar(str []byte) []byte { if str[0] == '\\' { return []byte{str[1]} } return []byte(html.UnescapeString(string(str))) } func unescapeString(str []byte) []byte { if reBackslashOrAmp.Match(str) { return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar) } return str } func finalizeCodeBlock(block *Node) { if block.IsFenced { newlinePos := bytes.IndexByte(block.content, '\n') firstLine := block.content[:newlinePos] rest := block.content[newlinePos+1:] block.Info = unescapeString(bytes.Trim(firstLine, "\n")) block.Literal = rest } else { block.Literal = block.content } block.content = nil } func (p *Markdown) table(data []byte) int { table := p.addBlock(Table, nil) i, columns := p.tableHeader(data) if i == 0 { p.tip = table.Parent table.Unlink() return 0 } p.addBlock(TableBody, nil) for i < len(data) { pipes, rowStart := 0, i for ; i < len(data) && data[i] != '\n'; i++ { if data[i] == '|' { pipes++ } } if pipes == 0 { i = rowStart break } // include the newline in data sent to tableRow if i < len(data) && data[i] == '\n' { i++ } p.tableRow(data[rowStart:i], columns, false) } return i } // check if the specified position is preceded by an odd number of backslashes func isBackslashEscaped(data []byte, i int) bool { backslashes := 0 for i-backslashes-1 >= 0 && data[i-backslashes-1] == '\\' { backslashes++ } return backslashes&1 == 1 } func (p *Markdown) tableHeader(data []byte) (size int, columns []CellAlignFlags) { i := 0 colCount := 1 for i = 0; i < len(data) && data[i] != '\n'; i++ { if data[i] == '|' && !isBackslashEscaped(data, i) { colCount++ } } // doesn't look like a table header if colCount == 1 { return } // include the newline in the data sent to tableRow j := i if j < len(data) && data[j] == '\n' { j++ } header := data[:j] // column count ignores pipes at beginning or end of line if data[0] == '|' { colCount-- } if i > 2 && data[i-1] == '|' && !isBackslashEscaped(data, i-1) { colCount-- } columns = make([]CellAlignFlags, colCount) // move on to the header underline i++ if i >= len(data) { return } if data[i] == '|' && !isBackslashEscaped(data, i) { i++ } i = skipChar(data, i, ' ') // each column header is of form: / *:?-+:? *|/ with # dashes + # colons >= 3 // and trailing | optional on last column col := 0 for i < len(data) && data[i] != '\n' { dashes := 0 if data[i] == ':' { i++ columns[col] |= TableAlignmentLeft dashes++ } for i < len(data) && data[i] == '-' { i++ dashes++ } if i < len(data) && data[i] == ':' { i++ columns[col] |= TableAlignmentRight dashes++ } for i < len(data) && data[i] == ' ' { i++ } if i == len(data) { return } // end of column test is messy switch { case dashes < 3: // not a valid column return case data[i] == '|' && !isBackslashEscaped(data, i): // marker found, now skip past trailing whitespace col++ i++ for i < len(data) && data[i] == ' ' { i++ } // trailing junk found after last column if col >= colCount && i < len(data) && data[i] != '\n' { return } case (data[i] != '|' || isBackslashEscaped(data, i)) && col+1 < colCount: // something else found where marker was required return case data[i] == '\n': // marker is optional for the last column col++ default: // trailing junk found after last column return } } if col != colCount { return } p.addBlock(TableHead, nil) p.tableRow(header, columns, true) size = i if size < len(data) && data[size] == '\n' { size++ } return } func (p *Markdown) tableRow(data []byte, columns []CellAlignFlags, header bool) { p.addBlock(TableRow, nil) i, col := 0, 0 if data[i] == '|' && !isBackslashEscaped(data, i) { i++ } for col = 0; col < len(columns) && i < len(data); col++ { for i < len(data) && data[i] == ' ' { i++ } cellStart := i for i < len(data) && (data[i] != '|' || isBackslashEscaped(data, i)) && data[i] != '\n' { i++ } cellEnd := i // skip the end-of-cell marker, possibly taking us past end of buffer i++ for cellEnd > cellStart && cellEnd-1 < len(data) && data[cellEnd-1] == ' ' { cellEnd-- } cell := p.addBlock(TableCell, data[cellStart:cellEnd]) cell.IsHeader = header cell.Align = columns[col] } // pad it out with empty columns to get the right number for ; col < len(columns); col++ { cell := p.addBlock(TableCell, nil) cell.IsHeader = header cell.Align = columns[col] } // silently ignore rows with too many cells } // returns blockquote prefix length func (p *Markdown) quotePrefix(data []byte) int { i := 0 for i < 3 && i < len(data) && data[i] == ' ' { i++ } if i < len(data) && data[i] == '>' { if i+1 < len(data) && data[i+1] == ' ' { return i + 2 } return i + 1 } return 0 } // blockquote ends with at least one blank line // followed by something without a blockquote prefix func (p *Markdown) terminateBlockquote(data []byte, beg, end int) bool { if p.isEmpty(data[beg:]) <= 0 { return false } if end >= len(data) { return true } return p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0 } // parse a blockquote fragment func (p *Markdown) quote(data []byte) int { block := p.addBlock(BlockQuote, nil) var raw bytes.Buffer beg, end := 0, 0 for beg < len(data) { end = beg // Step over whole lines, collecting them. While doing that, check for // fenced code and if one's found, incorporate it altogether, // irregardless of any contents inside it for end < len(data) && data[end] != '\n' { if p.extensions&FencedCode != 0 { if i := p.fencedCodeBlock(data[end:], false); i > 0 { // -1 to compensate for the extra end++ after the loop: end += i - 1 break } } end++ } if end < len(data) && data[end] == '\n' { end++ } if pre := p.quotePrefix(data[beg:]); pre > 0 { // skip the prefix beg += pre } else if p.terminateBlockquote(data, beg, end) { break } // this line is part of the blockquote raw.Write(data[beg:end]) beg = end } p.block(raw.Bytes()) p.finalize(block) return end } // returns prefix length for block code func (p *Markdown) codePrefix(data []byte) int { if len(data) >= 1 && data[0] == '\t' { return 1 } if len(data) >= 4 && data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' { return 4 } return 0 } func (p *Markdown) code(data []byte) int { var work bytes.Buffer i := 0 for i < len(data) { beg := i for i < len(data) && data[i] != '\n' { i++ } if i < len(data) && data[i] == '\n' { i++ } blankline := p.isEmpty(data[beg:i]) > 0 if pre := p.codePrefix(data[beg:i]); pre > 0 { beg += pre } else if !blankline { // non-empty, non-prefixed line breaks the pre i = beg break } // verbatim copy to the working buffer if blankline { work.WriteByte('\n') } else { work.Write(data[beg:i]) } } // trim all the \n off the end of work workbytes := work.Bytes() eol := len(workbytes) for eol > 0 && workbytes[eol-1] == '\n' { eol-- } if eol != len(workbytes) { work.Truncate(eol) } work.WriteByte('\n') block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer block.IsFenced = false finalizeCodeBlock(block) return i } // returns unordered list item prefix func (p *Markdown) uliPrefix(data []byte) int { i := 0 // start with up to 3 spaces for i < len(data) && i < 3 && data[i] == ' ' { i++ } if i >= len(data)-1 { return 0 } // need one of {'*', '+', '-'} followed by a space or a tab if (data[i] != '*' && data[i] != '+' && data[i] != '-') || (data[i+1] != ' ' && data[i+1] != '\t') { return 0 } return i + 2 } // returns ordered list item prefix func (p *Markdown) oliPrefix(data []byte) int { i := 0 // start with up to 3 spaces for i < 3 && i < len(data) && data[i] == ' ' { i++ } // count the digits start := i for i < len(data) && data[i] >= '0' && data[i] <= '9' { i++ } if start == i || i >= len(data)-1 { return 0 } // we need >= 1 digits followed by a dot and a space or a tab if data[i] != '.' || !(data[i+1] == ' ' || data[i+1] == '\t') { return 0 } return i + 2 } // returns definition list item prefix func (p *Markdown) dliPrefix(data []byte) int { if len(data) < 2 { return 0 } i := 0 // need a ':' followed by a space or a tab if data[i] != ':' || !(data[i+1] == ' ' || data[i+1] == '\t') { return 0 } for i < len(data) && data[i] == ' ' { i++ } return i + 2 } // parse ordered or unordered list block func (p *Markdown) list(data []byte, flags ListType) int { i := 0 flags |= ListItemBeginningOfList block := p.addBlock(List, nil) block.ListFlags = flags block.Tight = true for i < len(data) { skip := p.listItem(data[i:], &flags) if flags&ListItemContainsBlock != 0 { block.ListData.Tight = false } i += skip if skip == 0 || flags&ListItemEndOfList != 0 { break } flags &= ^ListItemBeginningOfList } above := block.Parent finalizeList(block) p.tip = above return i } // Returns true if the list item is not the same type as its parent list func (p *Markdown) listTypeChanged(data []byte, flags *ListType) bool { if p.dliPrefix(data) > 0 && *flags&ListTypeDefinition == 0 { return true } else if p.oliPrefix(data) > 0 && *flags&ListTypeOrdered == 0 { return true } else if p.uliPrefix(data) > 0 && (*flags&ListTypeOrdered != 0 || *flags&ListTypeDefinition != 0) { return true } return false } // Returns true if block ends with a blank line, descending if needed // into lists and sublists. func endsWithBlankLine(block *Node) bool { // TODO: figure this out. Always false now. for block != nil { //if block.lastLineBlank { //return true //} t := block.Type if t == List || t == Item { block = block.LastChild } else { break } } return false } func finalizeList(block *Node) { block.open = false item := block.FirstChild for item != nil { // check for non-final list item ending with blank line: if endsWithBlankLine(item) && item.Next != nil { block.ListData.Tight = false break } // recurse into children of list item, to see if there are spaces // between any of them: subItem := item.FirstChild for subItem != nil { if endsWithBlankLine(subItem) && (item.Next != nil || subItem.Next != nil) { block.ListData.Tight = false break } subItem = subItem.Next } item = item.Next } } // Parse a single list item. // Assumes initial prefix is already removed if this is a sublist. func (p *Markdown) listItem(data []byte, flags *ListType) int { // keep track of the indentation of the first line itemIndent := 0 if data[0] == '\t' { itemIndent += 4 } else { for itemIndent < 3 && data[itemIndent] == ' ' { itemIndent++ } } var bulletChar byte = '*' i := p.uliPrefix(data) if i == 0 { i = p.oliPrefix(data) } else { bulletChar = data[i-2] } if i == 0 { i = p.dliPrefix(data) // reset definition term flag if i > 0 { *flags &= ^ListTypeTerm } } if i == 0 { // if in definition list, set term flag and continue if *flags&ListTypeDefinition != 0 { *flags |= ListTypeTerm } else { return 0 } } // skip leading whitespace on first line for i < len(data) && data[i] == ' ' { i++ } // find the end of the line line := i for i > 0 && i < len(data) && data[i-1] != '\n' { i++ } // get working buffer var raw bytes.Buffer // put the first line into the working buffer raw.Write(data[line:i]) line = i // process the following lines containsBlankLine := false sublist := 0 codeBlockMarker := "" gatherlines: for line < len(data) { i++ // find the end of this line for i < len(data) && data[i-1] != '\n' { i++ } // if it is an empty line, guess that it is part of this item // and move on to the next line if p.isEmpty(data[line:i]) > 0 { containsBlankLine = true line = i continue } // calculate the indentation indent := 0 indentIndex := 0 if data[line] == '\t' { indentIndex++ indent += 4 } else { for indent < 4 && line+indent < i && data[line+indent] == ' ' { indent++ indentIndex++ } } chunk := data[line+indentIndex : i] if p.extensions&FencedCode != 0 { // determine if in or out of codeblock // if in codeblock, ignore normal list processing _, marker := isFenceLine(chunk, nil, codeBlockMarker) if marker != "" { if codeBlockMarker == "" { // start of codeblock codeBlockMarker = marker } else { // end of codeblock. codeBlockMarker = "" } } // we are in a codeblock, write line, and continue if codeBlockMarker != "" || marker != "" { raw.Write(data[line+indentIndex : i]) line = i continue gatherlines } } // evaluate how this line fits in switch { // is this a nested list item? case (p.uliPrefix(chunk) > 0 && !p.isHRule(chunk)) || p.oliPrefix(chunk) > 0 || p.dliPrefix(chunk) > 0: // to be a nested list, it must be indented more // if not, it is either a different kind of list // or the next item in the same list if indent <= itemIndent { if p.listTypeChanged(chunk, flags) { *flags |= ListItemEndOfList } else if containsBlankLine { *flags |= ListItemContainsBlock } break gatherlines } if containsBlankLine { *flags |= ListItemContainsBlock } // is this the first item in the nested list? if sublist == 0 { sublist = raw.Len() } // is this a nested prefix heading? case p.isPrefixHeading(chunk): // if the heading is not indented, it is not nested in the list // and thus ends the list if containsBlankLine && indent < 4 { *flags |= ListItemEndOfList break gatherlines } *flags |= ListItemContainsBlock // anything following an empty line is only part // of this item if it is indented 4 spaces // (regardless of the indentation of the beginning of the item) case containsBlankLine && indent < 4: if *flags&ListTypeDefinition != 0 && i < len(data)-1 { // is the next item still a part of this list? next := i for next < len(data) && data[next] != '\n' { next++ } for next < len(data)-1 && data[next] == '\n' { next++ } if i < len(data)-1 && data[i] != ':' && data[next] != ':' { *flags |= ListItemEndOfList } } else { *flags |= ListItemEndOfList } break gatherlines // a blank line means this should be parsed as a block case containsBlankLine: raw.WriteByte('\n') *flags |= ListItemContainsBlock } // if this line was preceded by one or more blanks, // re-introduce the blank into the buffer if containsBlankLine { containsBlankLine = false raw.WriteByte('\n') } // add the line into the working buffer without prefix raw.Write(data[line+indentIndex : i]) line = i } rawBytes := raw.Bytes() block := p.addBlock(Item, nil) block.ListFlags = *flags block.Tight = false block.BulletChar = bulletChar block.Delimiter = '.' // Only '.' is possible in Markdown, but ')' will also be possible in CommonMark // render the contents of the list item if *flags&ListItemContainsBlock != 0 && *flags&ListTypeTerm == 0 { // intermediate render of block item, except for definition term if sublist > 0 { p.block(rawBytes[:sublist]) p.block(rawBytes[sublist:]) } else { p.block(rawBytes) } } else { // intermediate render of inline item if sublist > 0 { child := p.addChild(Paragraph, 0) child.content = rawBytes[:sublist] p.block(rawBytes[sublist:]) } else { child := p.addChild(Paragraph, 0) child.content = rawBytes } } return line } // render a single paragraph that has already been parsed out func (p *Markdown) renderParagraph(data []byte) { if len(data) == 0 { return } // trim leading spaces beg := 0 for data[beg] == ' ' { beg++ } end := len(data) // trim trailing newline if data[len(data)-1] == '\n' { end-- } // trim trailing spaces for end > beg && data[end-1] == ' ' { end-- } p.addBlock(Paragraph, data[beg:end]) } func (p *Markdown) paragraph(data []byte) int { // prev: index of 1st char of previous line // line: index of 1st char of current line // i: index of cursor/end of current line var prev, line, i int tabSize := TabSizeDefault if p.extensions&TabSizeEight != 0 { tabSize = TabSizeDouble } // keep going until we find something to mark the end of the paragraph for i < len(data) { // mark the beginning of the current line prev = line current := data[i:] line = i // did we find a reference or a footnote? If so, end a paragraph // preceding it and report that we have consumed up to the end of that // reference: if refEnd := isReference(p, current, tabSize); refEnd > 0 { p.renderParagraph(data[:i]) return i + refEnd } // did we find a blank line marking the end of the paragraph? if n := p.isEmpty(current); n > 0 { // did this blank line followed by a definition list item? if p.extensions&DefinitionLists != 0 { if i < len(data)-1 && data[i+1] == ':' { return p.list(data[prev:], ListTypeDefinition) } } p.renderParagraph(data[:i]) return i + n } // an underline under some text marks a heading, so our paragraph ended on prev line if i > 0 { if level := p.isUnderlinedHeading(current); level > 0 { // render the paragraph p.renderParagraph(data[:prev]) // ignore leading and trailing whitespace eol := i - 1 for prev < eol && data[prev] == ' ' { prev++ } for eol > prev && data[eol-1] == ' ' { eol-- } id := "" if p.extensions&AutoHeadingIDs != 0 { id = SanitizedAnchorName(string(data[prev:eol])) } block := p.addBlock(Heading, data[prev:eol]) block.Level = level block.HeadingID = id // find the end of the underline for i < len(data) && data[i] != '\n' { i++ } return i } } // if the next line starts a block of HTML, then the paragraph ends here if p.extensions&LaxHTMLBlocks != 0 { if data[i] == '<' && p.html(current, false) > 0 { // rewind to before the HTML block p.renderParagraph(data[:i]) return i } } // if there's a prefixed heading or a horizontal rule after this, paragraph is over if p.isPrefixHeading(current) || p.isHRule(current) { p.renderParagraph(data[:i]) return i } // if there's a fenced code block, paragraph is over if p.extensions&FencedCode != 0 { if p.fencedCodeBlock(current, false) > 0 { p.renderParagraph(data[:i]) return i } } // if there's a definition list item, prev line is a definition term if p.extensions&DefinitionLists != 0 { if p.dliPrefix(current) != 0 { ret := p.list(data[prev:], ListTypeDefinition) return ret } } // if there's a list after this, paragraph is over if p.extensions&NoEmptyLineBeforeBlock != 0 { if p.uliPrefix(current) != 0 || p.oliPrefix(current) != 0 || p.quotePrefix(current) != 0 || p.codePrefix(current) != 0 { p.renderParagraph(data[:i]) return i } } // otherwise, scan to the beginning of the next line nl := bytes.IndexByte(data[i:], '\n') if nl >= 0 { i += nl + 1 } else { i += len(data[i:]) } } p.renderParagraph(data[:i]) return i } func skipChar(data []byte, start int, char byte) int { i := start for i < len(data) && data[i] == char { i++ } return i } func skipUntilChar(text []byte, start int, char byte) int { i := start for i < len(text) && text[i] != char { i++ } return i } // SanitizedAnchorName returns a sanitized anchor name for the given text. // // It implements the algorithm specified in the package comment. func SanitizedAnchorName(text string) string { var anchorName []rune futureDash := false for _, r := range text { switch { case unicode.IsLetter(r) || unicode.IsNumber(r): if futureDash && len(anchorName) > 0 { anchorName = append(anchorName, '-') } futureDash = false anchorName = append(anchorName, unicode.ToLower(r)) default: futureDash = true } } return string(anchorName) } blackfriday-2.1.0/block_test.go000066400000000000000000001474331374571415200164720ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Unit tests for block parsing // package blackfriday import ( "strings" "testing" ) func TestPrefixHeaderNoExtensions(t *testing.T) { t.Parallel() var tests = []string{ "# Header 1\n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "

Header 4

\n", "##### Header 5\n", "
Header 5
\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "
# Header 7
\n", "#Header 1\n", "

Header 1

\n", "##Header 2\n", "

Header 2

\n", "###Header 3\n", "

Header 3

\n", "####Header 4\n", "

Header 4

\n", "#####Header 5\n", "
Header 5
\n", "######Header 6\n", "
Header 6
\n", "#######Header 7\n", "
#Header 7
\n", "Hello\n# Header 1\nGoodbye\n", "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", "* List\n# Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n#Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n * Nested list\n # Nested header\n", "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + "

      Nested header

    • \n
  • \n
\n", "#Header 1 \\#\n", "

Header 1 #

\n", "#Header 1 \\# foo\n", "

Header 1 # foo

\n", "#Header 1 #\\##\n", "

Header 1 ##

\n", } doTestsBlock(t, tests, 0) } func TestPrefixHeaderSpaceExtension(t *testing.T) { t.Parallel() var tests = []string{ "# Header 1\n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "

Header 4

\n", "##### Header 5\n", "
Header 5
\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "

####### Header 7

\n", "#Header 1\n", "

#Header 1

\n", "##Header 2\n", "

##Header 2

\n", "###Header 3\n", "

###Header 3

\n", "####Header 4\n", "

####Header 4

\n", "#####Header 5\n", "

#####Header 5

\n", "######Header 6\n", "

######Header 6

\n", "#######Header 7\n", "

#######Header 7

\n", "Hello\n# Header 1\nGoodbye\n", "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", "* List\n# Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n#Header\n* List\n", "
    \n
  • List\n#Header
  • \n
  • List
  • \n
\n", "* List\n * Nested list\n # Nested header\n", "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + "

      Nested header

    • \n
  • \n
\n", } doTestsBlock(t, tests, SpaceHeadings) } func TestPrefixHeaderIdExtension(t *testing.T) { t.Parallel() var tests = []string{ "# Header 1 {#someid}\n", "

Header 1

\n", "# Header 1 {#someid} \n", "

Header 1

\n", "# Header 1 {#someid}\n", "

Header 1

\n", "# Header 1 {#someid\n", "

Header 1 {#someid

\n", "# Header 1 {#someid\n", "

Header 1 {#someid

\n", "# Header 1 {#someid}}\n", "

Header 1

\n\n

}

\n", "## Header 2 {#someid}\n", "

Header 2

\n", "### Header 3 {#someid}\n", "

Header 3

\n", "#### Header 4 {#someid}\n", "

Header 4

\n", "##### Header 5 {#someid}\n", "
Header 5
\n", "###### Header 6 {#someid}\n", "
Header 6
\n", "####### Header 7 {#someid}\n", "
# Header 7
\n", "# Header 1 # {#someid}\n", "

Header 1

\n", "## Header 2 ## {#someid}\n", "

Header 2

\n", "Hello\n# Header 1\nGoodbye\n", "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", "* List\n# Header {#someid}\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n#Header {#someid}\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n * Nested list\n # Nested header {#someid}\n", "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + "

      Nested header

    • \n
  • \n
\n", } doTestsBlock(t, tests, HeadingIDs) } func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { t.Parallel() var tests = []string{ "# header 1 {#someid}\n", "

header 1

\n", "## header 2 {#someid}\n", "

header 2

\n", "### header 3 {#someid}\n", "

header 3

\n", "#### header 4 {#someid}\n", "

header 4

\n", "##### header 5 {#someid}\n", "
header 5
\n", "###### header 6 {#someid}\n", "
header 6
\n", "####### header 7 {#someid}\n", "
# header 7
\n", "# header 1 # {#someid}\n", "

header 1

\n", "## header 2 ## {#someid}\n", "

header 2

\n", "* List\n# Header {#someid}\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n#Header {#someid}\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n * Nested list\n # Nested header {#someid}\n", "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + "

      Nested header

    • \n
  • \n
\n", } parameters := HTMLRendererParameters{ HeadingIDPrefix: "PRE:", HeadingIDSuffix: ":POST", } doTestsParam(t, tests, TestParams{ extensions: HeadingIDs, HTMLFlags: UseXHTML, HTMLRendererParameters: parameters, }) } func TestPrefixAutoHeaderIdExtension(t *testing.T) { t.Parallel() var tests = []string{ "# Header 1\n", "

Header 1

\n", "# Header 1 \n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "

Header 4

\n", "##### Header 5\n", "
Header 5
\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "
# Header 7
\n", "Hello\n# Header 1\nGoodbye\n", "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", "* List\n# Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n#Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n * Nested list\n # Nested header\n", "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + "

      Nested header

    • \n
  • \n
\n", "# Header\n\n# Header\n", "

Header

\n\n

Header

\n", "# Header 1\n\n# Header 1", "

Header 1

\n\n

Header 1

\n", "# Header\n\n# Header 1\n\n# Header\n\n# Header", "

Header

\n\n

Header 1

\n\n

Header

\n\n

Header

\n", } doTestsBlock(t, tests, AutoHeadingIDs) } func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { t.Parallel() var tests = []string{ "# Header 1\n", "

Header 1

\n", "# Header 1 \n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "

Header 4

\n", "##### Header 5\n", "
Header 5
\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "
# Header 7
\n", "Hello\n# Header 1\nGoodbye\n", "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", "* List\n# Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n#Header\n* List\n", "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", "* List\n * Nested list\n # Nested header\n", "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + "

      Nested header

    • \n
  • \n
\n", "# Header\n\n# Header\n", "

Header

\n\n

Header

\n", "# Header 1\n\n# Header 1", "

Header 1

\n\n

Header 1

\n", "# Header\n\n# Header 1\n\n# Header\n\n# Header", "

Header

\n\n

Header 1

\n\n

Header

\n\n

Header

\n", } parameters := HTMLRendererParameters{ HeadingIDPrefix: "PRE:", HeadingIDSuffix: ":POST", } doTestsParam(t, tests, TestParams{ extensions: AutoHeadingIDs, HTMLFlags: UseXHTML, HTMLRendererParameters: parameters, }) } func TestPrefixHeaderLevelOffset(t *testing.T) { t.Parallel() var offsetTests = []struct { offset int tests []string }{{ offset: 0, tests: []string{ "# Header 1\n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "

Header 4

\n", "##### Header 5\n", "
Header 5
\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "
# Header 7
\n", }, }, { offset: 1, tests: []string{ "# Header 1\n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "
Header 4
\n", "##### Header 5\n", "
Header 5
\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "
# Header 7
\n", }, }, { offset: -1, tests: []string{ "# Header 1\n", "

Header 1

\n", "## Header 2\n", "

Header 2

\n", "### Header 3\n", "

Header 3

\n", "#### Header 4\n", "

Header 4

\n", "##### Header 5\n", "

Header 5

\n", "###### Header 6\n", "
Header 6
\n", "####### Header 7\n", "
# Header 7
\n", }, }} for _, offsetTest := range offsetTests { offset := offsetTest.offset tests := offsetTest.tests doTestsParam(t, tests, TestParams{ HTMLRendererParameters: HTMLRendererParameters{HeadingLevelOffset: offset}, }) } } func TestPrefixMultipleHeaderExtensions(t *testing.T) { t.Parallel() var tests = []string{ "# Header\n\n# Header {#header}\n\n# Header 1", "

Header

\n\n

Header

\n\n

Header 1

\n", } doTestsBlock(t, tests, AutoHeadingIDs|HeadingIDs) } func TestUnderlineHeaders(t *testing.T) { t.Parallel() var tests = []string{ "Header 1\n========\n", "

Header 1

\n", "Header 2\n--------\n", "

Header 2

\n", "A\n=\n", "

A

\n", "B\n-\n", "

B

\n", "Paragraph\nHeader\n=\n", "

Paragraph

\n\n

Header

\n", "Header\n===\nParagraph\n", "

Header

\n\n

Paragraph

\n", "Header\n===\nAnother header\n---\n", "

Header

\n\n

Another header

\n", " Header\n======\n", "

Header

\n", " Code\n========\n", "
Code\n
\n\n

========

\n", "Header with *inline*\n=====\n", "

Header with inline

\n", "* List\n * Sublist\n Not a header\n ------\n", "
    \n
  • List\n\n
      \n
    • Sublist\nNot a header\n------
    • \n
  • \n
\n", "Paragraph\n\n\n\n\nHeader\n===\n", "

Paragraph

\n\n

Header

\n", "Trailing space \n==== \n\n", "

Trailing space

\n", "Trailing spaces\n==== \n\n", "

Trailing spaces

\n", "Double underline\n=====\n=====\n", "

Double underline

\n\n

=====

\n", } doTestsBlock(t, tests, 0) } func TestUnderlineHeadersAutoIDs(t *testing.T) { t.Parallel() var tests = []string{ "Header 1\n========\n", "

Header 1

\n", "Header 2\n--------\n", "

Header 2

\n", "A\n=\n", "

A

\n", "B\n-\n", "

B

\n", "Paragraph\nHeader\n=\n", "

Paragraph

\n\n

Header

\n", "Header\n===\nParagraph\n", "

Header

\n\n

Paragraph

\n", "Header\n===\nAnother header\n---\n", "

Header

\n\n

Another header

\n", " Header\n======\n", "

Header

\n", "Header with *inline*\n=====\n", "

Header with inline

\n", "Paragraph\n\n\n\n\nHeader\n===\n", "

Paragraph

\n\n

Header

\n", "Trailing space \n==== \n\n", "

Trailing space

\n", "Trailing spaces\n==== \n\n", "

Trailing spaces

\n", "Double underline\n=====\n=====\n", "

Double underline

\n\n

=====

\n", "Header\n======\n\nHeader\n======\n", "

Header

\n\n

Header

\n", "Header 1\n========\n\nHeader 1\n========\n", "

Header 1

\n\n

Header 1

\n", } doTestsBlock(t, tests, AutoHeadingIDs) } func TestHorizontalRule(t *testing.T) { t.Parallel() var tests = []string{ "-\n", "

-

\n", "--\n", "

--

\n", "---\n", "
\n", "----\n", "
\n", "*\n", "

*

\n", "**\n", "

**

\n", "***\n", "
\n", "****\n", "
\n", "_\n", "

_

\n", "__\n", "

__

\n", "___\n", "
\n", "____\n", "
\n", "-*-\n", "

-*-

\n", "- - -\n", "
\n", "* * *\n", "
\n", "_ _ _\n", "
\n", "-----*\n", "

-----*

\n", " ------ \n", "
\n", "Hello\n***\n", "

Hello

\n\n
\n", "---\n***\n___\n", "
\n\n
\n\n
\n", } doTestsBlock(t, tests, 0) } func TestUnorderedList(t *testing.T) { t.Parallel() var tests = []string{ "* Hello\n", "
    \n
  • Hello
  • \n
\n", "* Yin\n* Yang\n", "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", "* Ting\n* Bong\n* Goo\n", "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", "* Yin\n\n* Yang\n", "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", "* Ting\n\n* Bong\n* Goo\n", "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", "+ Hello\n", "
    \n
  • Hello
  • \n
\n", "+ Yin\n+ Yang\n", "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", "+ Ting\n+ Bong\n+ Goo\n", "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", "+ Yin\n\n+ Yang\n", "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", "+ Ting\n\n+ Bong\n+ Goo\n", "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", "- Hello\n", "
    \n
  • Hello
  • \n
\n", "- Yin\n- Yang\n", "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", "- Ting\n- Bong\n- Goo\n", "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", "- Yin\n\n- Yang\n", "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", "- Ting\n\n- Bong\n- Goo\n", "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", "*Hello\n", "

*Hello

\n", "* Hello \n", "
    \n
  • Hello
  • \n
\n", "* Hello \n Next line \n", "
    \n
  • Hello\nNext line
  • \n
\n", "Paragraph\n* No linebreak\n", "

Paragraph\n* No linebreak

\n", "Paragraph\n\n* Linebreak\n", "

Paragraph

\n\n
    \n
  • Linebreak
  • \n
\n", "* List\n * Nested list\n", "
    \n
  • List\n\n
      \n
    • Nested list
    • \n
  • \n
\n", "* List\n\n * Nested list\n", "
    \n
  • List

    \n\n
      \n
    • Nested list
    • \n
  • \n
\n", "* List\n Second line\n\n + Nested\n", "
    \n
  • List\nSecond line

    \n\n
      \n
    • Nested
    • \n
  • \n
\n", "* List\n + Nested\n\n Continued\n", "
    \n
  • List

    \n\n
      \n
    • Nested
    • \n
    \n\n

    Continued

  • \n
\n", "* List\n * shallow indent\n", "
    \n
  • List\n\n
      \n
    • shallow indent
    • \n
  • \n
\n", "* List\n" + " * shallow indent\n" + " * part of second list\n" + " * still second\n" + " * almost there\n" + " * third level\n", "
    \n" + "
  • List\n\n" + "
      \n" + "
    • shallow indent
    • \n" + "
    • part of second list
    • \n" + "
    • still second
    • \n" + "
    • almost there\n\n" + "
        \n" + "
      • third level
      • \n" + "
    • \n" + "
  • \n" + "
\n", "* List\n extra indent, same paragraph\n", "
    \n
  • List\n extra indent, same paragraph
  • \n
\n", "* List\n\n code block\n", "
    \n
  • List

    \n\n
    code block\n
  • \n
\n", "* List\n\n code block with spaces\n", "
    \n
  • List

    \n\n
      code block with spaces\n
  • \n
\n", "* List\n\n * sublist\n\n normal text\n\n * another sublist\n", "
    \n
  • List

    \n\n
      \n
    • sublist
    • \n
    \n\n

    normal text

    \n\n
      \n
    • another sublist
    • \n
  • \n
\n", } doTestsBlock(t, tests, 0) } func TestOrderedList(t *testing.T) { t.Parallel() var tests = []string{ "1. Hello\n", "
    \n
  1. Hello
  2. \n
\n", "1. Yin\n2. Yang\n", "
    \n
  1. Yin
  2. \n
  3. Yang
  4. \n
\n", "1. Ting\n2. Bong\n3. Goo\n", "
    \n
  1. Ting
  2. \n
  3. Bong
  4. \n
  5. Goo
  6. \n
\n", "1. Yin\n\n2. Yang\n", "
    \n
  1. Yin

  2. \n\n
  3. Yang

  4. \n
\n", "1. Ting\n\n2. Bong\n3. Goo\n", "
    \n
  1. Ting

  2. \n\n
  3. Bong

  4. \n\n
  5. Goo

  6. \n
\n", "1 Hello\n", "

1 Hello

\n", "1.Hello\n", "

1.Hello

\n", "1. Hello \n", "
    \n
  1. Hello
  2. \n
\n", "1. Hello \n Next line \n", "
    \n
  1. Hello\nNext line
  2. \n
\n", "Paragraph\n1. No linebreak\n", "

Paragraph\n1. No linebreak

\n", "Paragraph\n\n1. Linebreak\n", "

Paragraph

\n\n
    \n
  1. Linebreak
  2. \n
\n", "1. List\n 1. Nested list\n", "
    \n
  1. List\n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", "1. List\n\n 1. Nested list\n", "
    \n
  1. List

    \n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", "1. List\n Second line\n\n 1. Nested\n", "
    \n
  1. List\nSecond line

    \n\n
      \n
    1. Nested
    2. \n
  2. \n
\n", "1. List\n 1. Nested\n\n Continued\n", "
    \n
  1. List

    \n\n
      \n
    1. Nested
    2. \n
    \n\n

    Continued

  2. \n
\n", "1. List\n 1. shallow indent\n", "
    \n
  1. List\n\n
      \n
    1. shallow indent
    2. \n
  2. \n
\n", "1. List\n" + " 1. shallow indent\n" + " 2. part of second list\n" + " 3. still second\n" + " 4. almost there\n" + " 1. third level\n", "
    \n" + "
  1. List\n\n" + "
      \n" + "
    1. shallow indent
    2. \n" + "
    3. part of second list
    4. \n" + "
    5. still second
    6. \n" + "
    7. almost there\n\n" + "
        \n" + "
      1. third level
      2. \n" + "
    8. \n" + "
  2. \n" + "
\n", "1. List\n extra indent, same paragraph\n", "
    \n
  1. List\n extra indent, same paragraph
  2. \n
\n", "1. List\n\n code block\n", "
    \n
  1. List

    \n\n
    code block\n
  2. \n
\n", "1. List\n\n code block with spaces\n", "
    \n
  1. List

    \n\n
      code block with spaces\n
  2. \n
\n", "1. List\n * Mixted list\n", "
    \n
  1. List\n\n
      \n
    • Mixted list
    • \n
  2. \n
\n", "1. List\n * Mixed list\n", "
    \n
  1. List\n\n
      \n
    • Mixed list
    • \n
  2. \n
\n", "* Start with unordered\n 1. Ordered\n", "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", "* Start with unordered\n 1. Ordered\n", "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", "1. numbers\n1. are ignored\n", "
    \n
  1. numbers
  2. \n
  3. are ignored
  4. \n
\n", } doTestsBlock(t, tests, 0) } func TestDefinitionList(t *testing.T) { t.Parallel() var tests = []string{ "Term 1\n: Definition a\n", "
\n
Term 1
\n
Definition a
\n
\n", "Term 1\n: Definition a \n", "
\n
Term 1
\n
Definition a
\n
\n", "Term 1\n: Definition a\n: Definition b\n", "
\n
Term 1
\n
Definition a
\n
Definition b
\n
\n", "Term 1\n: Definition a\n\nTerm 2\n: Definition b\n", "
\n" + "
Term 1
\n" + "
Definition a
\n" + "
Term 2
\n" + "
Definition b
\n" + "
\n", "Term 1\n: Definition a\n\nTerm 2\n: Definition b\n\nTerm 3\n: Definition c\n", "
\n" + "
Term 1
\n" + "
Definition a
\n" + "
Term 2
\n" + "
Definition b
\n" + "
Term 3
\n" + "
Definition c
\n" + "
\n", "Term 1\n: Definition a\n: Definition b\n\nTerm 2\n: Definition c\n", "
\n" + "
Term 1
\n" + "
Definition a
\n" + "
Definition b
\n" + "
Term 2
\n" + "
Definition c
\n" + "
\n", "Term 1\n\n: Definition a\n\nTerm 2\n\n: Definition b\n", "
\n" + "
Term 1
\n" + "

Definition a

\n" + "
Term 2
\n" + "

Definition b

\n" + "
\n", "Term 1\n\n: Definition a\n\n: Definition b\n\nTerm 2\n\n: Definition c\n", "
\n" + "
Term 1
\n" + "

Definition a

\n" + "

Definition b

\n" + "
Term 2
\n" + "

Definition c

\n" + "
\n", "Term 1\n: Definition a\nNext line\n", "
\n
Term 1
\n
Definition a\nNext line
\n
\n", "Term 1\n: Definition a\n Next line\n", "
\n
Term 1
\n
Definition a\nNext line
\n
\n", "Term 1\n: Definition a \n Next line \n", "
\n
Term 1
\n
Definition a\nNext line
\n
\n", "Term 1\n: Definition a\nNext line\n\nTerm 2\n: Definition b", "
\n" + "
Term 1
\n" + "
Definition a\nNext line
\n" + "
Term 2
\n" + "
Definition b
\n" + "
\n", "Term 1\n: Definition a\n", "
\n
Term 1
\n
Definition a
\n
\n", "Term 1\n:Definition a\n", "

Term 1\n:Definition a

\n", "Term 1\n\n: Definition a\n\nTerm 2\n\n: Definition b\n\nText 1", "
\n" + "
Term 1
\n" + "

Definition a

\n" + "
Term 2
\n" + "

Definition b

\n" + "
\n" + "\n

Text 1

\n", "Term 1\n\n: Definition a\n\nText 1\n\nTerm 2\n\n: Definition b\n\nText 2", "
\n" + "
Term 1
\n" + "

Definition a

\n" + "
\n" + "\n

Text 1

\n" + "\n
\n" + "
Term 2
\n" + "

Definition b

\n" + "
\n" + "\n

Text 2

\n", } doTestsBlock(t, tests, DefinitionLists) } func TestConsecutiveLists(t *testing.T) { t.Parallel() var tests = []string{ "1. Hello\n\n* Hello\n\nTerm 1\n: Definition a\n", "
    \n
  1. Hello
  2. \n
\n\n
    \n
  • Hello
  • \n
\n\n
\n
Term 1
\n
Definition a
\n
\n", "1. Not nested\n2. ordered list\n\n\t1. nested\n\t2. ordered list\n\n\t* nested\n\t* unordered list\n* Not nested\n* unordered list", "
    \n
  1. Not nested

  2. \n\n
  3. ordered list

    \n\n
      \n
    1. nested
    2. \n
    3. ordered list
    4. \n
    \n\n
      \n
    • nested
    • \n
    • unordered list
    • \n
  4. \n
\n\n
    \n
  • Not nested
  • \n
  • unordered list
  • \n
\n", } doTestsBlock(t, tests, DefinitionLists) } func TestPreformattedHtml(t *testing.T) { t.Parallel() var tests = []string{ "
\n", "
\n", "
\n
\n", "
\n
\n", "
\n
\nParagraph\n", "

\n
\nParagraph

\n", "
\n
\n", "
\n
\n", "
\nAnything here\n
\n", "
\nAnything here\n
\n", "
\n Anything here\n
\n", "
\n Anything here\n
\n", "
\nAnything here\n
\n", "
\nAnything here\n
\n", "
\nThis is *not* &proceessed\n
\n", "
\nThis is *not* &proceessed\n
\n", "\n Something\n\n", "

\n Something\n

\n", "
\n Something here\n\n", "

\n Something here\n

\n", "Paragraph\n
\nHere? >&<\n
\n", "

Paragraph\n

\nHere? >&<\n

\n", "Paragraph\n\n
\nHow about here? >&<\n
\n", "

Paragraph

\n\n
\nHow about here? >&<\n
\n", "Paragraph\n
\nHere? >&<\n
\nAnd here?\n", "

Paragraph\n

\nHere? >&<\n
\nAnd here?

\n", "Paragraph\n\n
\nHow about here? >&<\n
\nAnd here?\n", "

Paragraph

\n\n

\nHow about here? >&<\n
\nAnd here?

\n", "Paragraph\n
\nHere? >&<\n
\n\nAnd here?\n", "

Paragraph\n

\nHere? >&<\n

\n\n

And here?

\n", "Paragraph\n\n
\nHow about here? >&<\n
\n\nAnd here?\n", "

Paragraph

\n\n
\nHow about here? >&<\n
\n\n

And here?

\n", } doTestsBlock(t, tests, 0) } func TestPreformattedHtmlLax(t *testing.T) { t.Parallel() var tests = []string{ "Paragraph\n
\nHere? >&<\n
\n", "

Paragraph

\n\n
\nHere? >&<\n
\n", "Paragraph\n\n
\nHow about here? >&<\n
\n", "

Paragraph

\n\n
\nHow about here? >&<\n
\n", "Paragraph\n
\nHere? >&<\n
\nAnd here?\n", "

Paragraph

\n\n
\nHere? >&<\n
\n\n

And here?

\n", "Paragraph\n\n
\nHow about here? >&<\n
\nAnd here?\n", "

Paragraph

\n\n
\nHow about here? >&<\n
\n\n

And here?

\n", "Paragraph\n
\nHere? >&<\n
\n\nAnd here?\n", "

Paragraph

\n\n
\nHere? >&<\n
\n\n

And here?

\n", "Paragraph\n\n
\nHow about here? >&<\n
\n\nAnd here?\n", "

Paragraph

\n\n
\nHow about here? >&<\n
\n\n

And here?

\n", } doTestsBlock(t, tests, LaxHTMLBlocks) } func TestFencedCodeBlock(t *testing.T) { t.Parallel() var tests = []string{ "``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n", "
func foo() bool {\n\treturn true;\n}\n
\n", "``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n", "
func foo() bool {\n\treturn true;\n}\n
\n", "``` c\n/* special & char < > \" escaping */\n```\n", "
/* special & char < > " escaping */\n
\n", "``` c\nno *inline* processing ~~of text~~\n```\n", "
no *inline* processing ~~of text~~\n
\n", "```\nNo language\n```\n", "
No language\n
\n", "``` {ocaml}\nlanguage in braces\n```\n", "
language in braces\n
\n", "``` {ocaml} \nwith extra whitespace\n```\n", "
with extra whitespace\n
\n", "```{ ocaml }\nwith extra whitespace\n```\n", "
with extra whitespace\n
\n", "~ ~~ java\nWith whitespace\n~~~\n", "

~ ~~ java\nWith whitespace\n~~~

\n", "~~\nonly two\n~~\n", "

~~\nonly two\n~~

\n", "```` python\nextra\n````\n", "
extra\n
\n", "~~~ perl\nthree to start, four to end\n~~~~\n", "

~~~ perl\nthree to start, four to end\n~~~~

\n", "~~~~ perl\nfour to start, three to end\n~~~\n", "

~~~~ perl\nfour to start, three to end\n~~~

\n", "~~~ bash\ntildes\n~~~\n", "
tildes\n
\n", "``` lisp\nno ending\n", "

``` lisp\nno ending

\n", "~~~ lisp\nend with language\n~~~ lisp\n", "

~~~ lisp\nend with language\n~~~ lisp

\n", "```\nmismatched begin and end\n~~~\n", "

```\nmismatched begin and end\n~~~

\n", "~~~\nmismatched begin and end\n```\n", "

~~~\nmismatched begin and end\n```

\n", " ``` oz\nleading spaces\n```\n", "
leading spaces\n
\n", " ``` oz\nleading spaces\n ```\n", "
leading spaces\n
\n", " ``` oz\nleading spaces\n ```\n", "
leading spaces\n
\n", "``` oz\nleading spaces\n ```\n", "
leading spaces\n
\n", " ``` oz\nleading spaces\n ```\n", "
``` oz\n
\n\n

leading spaces\n ```

\n", "Bla bla\n\n``` oz\ncode blocks breakup paragraphs\n```\n\nBla Bla\n", "

Bla bla

\n\n
code blocks breakup paragraphs\n
\n\n

Bla Bla

\n", "Some text before a fenced code block\n``` oz\ncode blocks breakup paragraphs\n```\nAnd some text after a fenced code block", "

Some text before a fenced code block

\n\n
code blocks breakup paragraphs\n
\n\n

And some text after a fenced code block

\n", "`", "

`

\n", "Bla bla\n\n``` oz\ncode blocks breakup paragraphs\n```\n\nBla Bla\n\n``` oz\nmultiple code blocks work okay\n```\n\nBla Bla\n", "

Bla bla

\n\n
code blocks breakup paragraphs\n
\n\n

Bla Bla

\n\n
multiple code blocks work okay\n
\n\n

Bla Bla

\n", "Some text before a fenced code block\n``` oz\ncode blocks breakup paragraphs\n```\nSome text in between\n``` oz\nmultiple code blocks work okay\n```\nAnd some text after a fenced code block", "

Some text before a fenced code block

\n\n
code blocks breakup paragraphs\n
\n\n

Some text in between

\n\n
multiple code blocks work okay\n
\n\n

And some text after a fenced code block

\n", "```\n[]:()\n```\n", "
[]:()\n
\n", "```\n[]:()\n[]:)\n[]:(\n[]:x\n[]:testing\n[:testing\n\n[]:\nlinebreak\n[]()\n\n[]:\n[]()\n```", "
[]:()\n[]:)\n[]:(\n[]:x\n[]:testing\n[:testing\n\n[]:\nlinebreak\n[]()\n\n[]:\n[]()\n
\n", } doTestsBlock(t, tests, FencedCode) } func TestFencedCodeInsideBlockquotes(t *testing.T) { t.Parallel() cat := func(s ...string) string { return strings.Join(s, "\n") } var tests = []string{ cat("> ```go", "package moo", "", "```", ""), `
package moo

`, // ------------------------------------------- cat("> foo", "> ", "> ```go", "package moo", "```", "> ", "> goo.", ""), `

foo

package moo

goo.

`, // ------------------------------------------- cat("> foo", "> ", "> quote", "continues", "```", ""), `

foo

quote continues ` + "```" + `

`, // ------------------------------------------- cat("> foo", "> ", "> ```go", "package moo", "```", "> ", "> goo.", "> ", "> ```go", "package zoo", "```", "> ", "> woo.", ""), `

foo

package moo

goo.

package zoo

woo.

`, } // These 2 alternative forms of blockquoted fenced code blocks should produce same output. forms := [2]string{ cat("> plain quoted text", "> ```fenced", "code", " with leading single space correctly preserved", "okay", "```", "> rest of quoted text"), cat("> plain quoted text", "> ```fenced", "> code", "> with leading single space correctly preserved", "> okay", "> ```", "> rest of quoted text"), } want := `

plain quoted text

code
 with leading single space correctly preserved
okay

rest of quoted text

` tests = append(tests, forms[0], want) tests = append(tests, forms[1], want) doTestsBlock(t, tests, FencedCode) } func TestTable(t *testing.T) { t.Parallel() var tests = []string{ "a | b\n---|---\nc | d\n", "\n\n\n\n\n\n\n\n" + "\n\n\n\n\n\n
ab
cd
\n", "a | b\n---|--\nc | d\n", "

a | b\n---|--\nc | d

\n", "|a|b|c|d|\n|----|----|----|---|\n|e|f|g|h|\n", "\n\n\n\n\n\n\n\n\n\n" + "\n\n\n\n\n\n\n\n
abcd
efgh
\n", "*a*|__b__|[c](C)|d\n---|---|---|---\ne|f|g|h\n", "\n\n\n\n\n\n\n\n\n\n" + "\n\n\n\n\n\n\n\n
abcd
efgh
\n", "a|b|c\n---|---|---\nd|e|f\ng|h\ni|j|k|l|m\nn|o|p\n", "\n\n\n\n\n\n\n\n\n" + "\n\n\n\n\n\n\n" + "\n\n\n\n\n\n" + "\n\n\n\n\n\n" + "\n\n\n\n\n\n
abc
def
gh
ijk
nop
\n", "a|b|c\n---|---|---\n*d*|__e__|f\n", "\n\n\n\n\n\n\n\n\n" + "\n\n\n\n\n\n\n
abc
def
\n", "a|b|c|d\n:--|--:|:-:|---\ne|f|g|h\n", "\n\n\n\n\n" + "\n\n\n\n\n" + "\n\n\n\n" + "\n\n\n\n
abcd
efgh
\n", "a|b|c\n---|---|---\n", "\n\n\n\n\n\n\n\n\n\n\n
abc
\n", "a| b|c | d | e\n---|---|---|---|---\nf| g|h | i |j\n", "\n\n\n\n\n\n\n\n\n\n\n" + "\n\n\n\n\n\n\n\n\n
abcde
fghij
\n", "a|b\\|c|d\n---|---|---\nf|g\\|h|i\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ab|cd
fg|hi
\n", } doTestsBlock(t, tests, Tables) } func TestUnorderedListWith_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { t.Parallel() var tests = []string{ "* Hello\n", "
    \n
  • Hello
  • \n
\n", "* Yin\n* Yang\n", "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", "* Ting\n* Bong\n* Goo\n", "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", "* Yin\n\n* Yang\n", "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", "* Ting\n\n* Bong\n* Goo\n", "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", "+ Hello\n", "
    \n
  • Hello
  • \n
\n", "+ Yin\n+ Yang\n", "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", "+ Ting\n+ Bong\n+ Goo\n", "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", "+ Yin\n\n+ Yang\n", "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", "+ Ting\n\n+ Bong\n+ Goo\n", "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", "- Hello\n", "
    \n
  • Hello
  • \n
\n", "- Yin\n- Yang\n", "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", "- Ting\n- Bong\n- Goo\n", "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", "- Yin\n\n- Yang\n", "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", "- Ting\n\n- Bong\n- Goo\n", "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", "*Hello\n", "

*Hello

\n", "* Hello \n", "
    \n
  • Hello
  • \n
\n", "* Hello \n Next line \n", "
    \n
  • Hello\nNext line
  • \n
\n", "Paragraph\n* No linebreak\n", "

Paragraph

\n\n
    \n
  • No linebreak
  • \n
\n", "Paragraph\n\n* Linebreak\n", "

Paragraph

\n\n
    \n
  • Linebreak
  • \n
\n", "* List\n * Nested list\n", "
    \n
  • List\n\n
      \n
    • Nested list
    • \n
  • \n
\n", "* List\n\n * Nested list\n", "
    \n
  • List

    \n\n
      \n
    • Nested list
    • \n
  • \n
\n", "* List\n Second line\n\n + Nested\n", "
    \n
  • List\nSecond line

    \n\n
      \n
    • Nested
    • \n
  • \n
\n", "* List\n + Nested\n\n Continued\n", "
    \n
  • List

    \n\n
      \n
    • Nested
    • \n
    \n\n

    Continued

  • \n
\n", "* List\n * shallow indent\n", "
    \n
  • List\n\n
      \n
    • shallow indent
    • \n
  • \n
\n", "* List\n" + " * shallow indent\n" + " * part of second list\n" + " * still second\n" + " * almost there\n" + " * third level\n", "
    \n" + "
  • List\n\n" + "
      \n" + "
    • shallow indent
    • \n" + "
    • part of second list
    • \n" + "
    • still second
    • \n" + "
    • almost there\n\n" + "
        \n" + "
      • third level
      • \n" + "
    • \n" + "
  • \n" + "
\n", "* List\n extra indent, same paragraph\n", "
    \n
  • List\n extra indent, same paragraph
  • \n
\n", "* List\n\n code block\n", "
    \n
  • List

    \n\n
    code block\n
  • \n
\n", "* List\n\n code block with spaces\n", "
    \n
  • List

    \n\n
      code block with spaces\n
  • \n
\n", "* List\n\n * sublist\n\n normal text\n\n * another sublist\n", "
    \n
  • List

    \n\n
      \n
    • sublist
    • \n
    \n\n

    normal text

    \n\n
      \n
    • another sublist
    • \n
  • \n
\n", } doTestsBlock(t, tests, NoEmptyLineBeforeBlock) } func TestOrderedList_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { t.Parallel() var tests = []string{ "1. Hello\n", "
    \n
  1. Hello
  2. \n
\n", "1. Yin\n2. Yang\n", "
    \n
  1. Yin
  2. \n
  3. Yang
  4. \n
\n", "1. Ting\n2. Bong\n3. Goo\n", "
    \n
  1. Ting
  2. \n
  3. Bong
  4. \n
  5. Goo
  6. \n
\n", "1. Yin\n\n2. Yang\n", "
    \n
  1. Yin

  2. \n\n
  3. Yang

  4. \n
\n", "1. Ting\n\n2. Bong\n3. Goo\n", "
    \n
  1. Ting

  2. \n\n
  3. Bong

  4. \n\n
  5. Goo

  6. \n
\n", "1 Hello\n", "

1 Hello

\n", "1.Hello\n", "

1.Hello

\n", "1. Hello \n", "
    \n
  1. Hello
  2. \n
\n", "1. Hello \n Next line \n", "
    \n
  1. Hello\nNext line
  2. \n
\n", "Paragraph\n1. No linebreak\n", "

Paragraph

\n\n
    \n
  1. No linebreak
  2. \n
\n", "Paragraph\n\n1. Linebreak\n", "

Paragraph

\n\n
    \n
  1. Linebreak
  2. \n
\n", "1. List\n 1. Nested list\n", "
    \n
  1. List\n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", "1. List\n\n 1. Nested list\n", "
    \n
  1. List

    \n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", "1. List\n Second line\n\n 1. Nested\n", "
    \n
  1. List\nSecond line

    \n\n
      \n
    1. Nested
    2. \n
  2. \n
\n", "1. List\n 1. Nested\n\n Continued\n", "
    \n
  1. List

    \n\n
      \n
    1. Nested
    2. \n
    \n\n

    Continued

  2. \n
\n", "1. List\n 1. shallow indent\n", "
    \n
  1. List\n\n
      \n
    1. shallow indent
    2. \n
  2. \n
\n", "1. List\n" + " 1. shallow indent\n" + " 2. part of second list\n" + " 3. still second\n" + " 4. almost there\n" + " 1. third level\n", "
    \n" + "
  1. List\n\n" + "
      \n" + "
    1. shallow indent
    2. \n" + "
    3. part of second list
    4. \n" + "
    5. still second
    6. \n" + "
    7. almost there\n\n" + "
        \n" + "
      1. third level
      2. \n" + "
    8. \n" + "
  2. \n" + "
\n", "1. List\n extra indent, same paragraph\n", "
    \n
  1. List\n extra indent, same paragraph
  2. \n
\n", "1. List\n\n code block\n", "
    \n
  1. List

    \n\n
    code block\n
  2. \n
\n", "1. List\n\n code block with spaces\n", "
    \n
  1. List

    \n\n
      code block with spaces\n
  2. \n
\n", "1. List\n * Mixted list\n", "
    \n
  1. List\n\n
      \n
    • Mixted list
    • \n
  2. \n
\n", "1. List\n * Mixed list\n", "
    \n
  1. List\n\n
      \n
    • Mixed list
    • \n
  2. \n
\n", "* Start with unordered\n 1. Ordered\n", "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", "* Start with unordered\n 1. Ordered\n", "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", "1. numbers\n1. are ignored\n", "
    \n
  1. numbers
  2. \n
  3. are ignored
  4. \n
\n", } doTestsBlock(t, tests, NoEmptyLineBeforeBlock) } func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { t.Parallel() var tests = []string{ "``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n", "
func foo() bool {\n\treturn true;\n}\n
\n", "``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n", "
func foo() bool {\n\treturn true;\n}\n
\n", "``` c\n/* special & char < > \" escaping */\n```\n", "
/* special & char < > " escaping */\n
\n", "``` c\nno *inline* processing ~~of text~~\n```\n", "
no *inline* processing ~~of text~~\n
\n", "```\nNo language\n```\n", "
No language\n
\n", "``` {ocaml}\nlanguage in braces\n```\n", "
language in braces\n
\n", "``` {ocaml} \nwith extra whitespace\n```\n", "
with extra whitespace\n
\n", "```{ ocaml }\nwith extra whitespace\n```\n", "
with extra whitespace\n
\n", "~ ~~ java\nWith whitespace\n~~~\n", "

~ ~~ java\nWith whitespace\n~~~

\n", "~~\nonly two\n~~\n", "

~~\nonly two\n~~

\n", "```` python\nextra\n````\n", "
extra\n
\n", "~~~ perl\nthree to start, four to end\n~~~~\n", "

~~~ perl\nthree to start, four to end\n~~~~

\n", "~~~~ perl\nfour to start, three to end\n~~~\n", "

~~~~ perl\nfour to start, three to end\n~~~

\n", "~~~ bash\ntildes\n~~~\n", "
tildes\n
\n", "``` lisp\nno ending\n", "

``` lisp\nno ending

\n", "~~~ lisp\nend with language\n~~~ lisp\n", "

~~~ lisp\nend with language\n~~~ lisp

\n", "```\nmismatched begin and end\n~~~\n", "

```\nmismatched begin and end\n~~~

\n", "~~~\nmismatched begin and end\n```\n", "

~~~\nmismatched begin and end\n```

\n", " ``` oz\nleading spaces\n```\n", "
leading spaces\n
\n", " ``` oz\nleading spaces\n ```\n", "
leading spaces\n
\n", " ``` oz\nleading spaces\n ```\n", "
leading spaces\n
\n", "``` oz\nleading spaces\n ```\n", "
leading spaces\n
\n", " ``` oz\nleading spaces\n ```\n", "
``` oz\n
\n\n

leading spaces

\n\n
```\n
\n", } doTestsBlock(t, tests, FencedCode|NoEmptyLineBeforeBlock) } func TestListWithFencedCodeBlock(t *testing.T) { t.Parallel() var tests = []string{ "1. one\n\n ```\n code\n ```\n\n2. two\n", "
    \n
  1. one

    \n\n
    code\n
  2. \n\n
  3. two

  4. \n
\n", // https://github.com/russross/blackfriday/issues/239 "1. one\n\n ```\n - code\n ```\n\n2. two\n", "
    \n
  1. one

    \n\n
    - code\n
  2. \n\n
  3. two

  4. \n
\n", } doTestsBlock(t, tests, FencedCode) } func TestListWithMalformedFencedCodeBlock(t *testing.T) { t.Parallel() // Ensure that in the case of an unclosed fenced code block in a list, // no source gets ommitted (even if it is malformed). // See russross/blackfriday#372 for context. var tests = []string{ "1. one\n\n ```\n code\n\n2. two\n", "
    \n
  1. one\n```\ncode\n2. two
  2. \n
\n", "1. one\n\n ```\n - code\n\n2. two\n", "
    \n
  1. one\n```\n- code\n2. two
  2. \n
\n", } doTestsBlock(t, tests, FencedCode) } func TestListWithFencedCodeBlockNoExtensions(t *testing.T) { t.Parallel() // If there is a fenced code block in a list, and FencedCode is not set, // lists should be processed normally. var tests = []string{ "1. one\n\n ```\n code\n ```\n\n2. two\n", "
    \n
  1. one

    \n\n

    \ncode\n

  2. \n\n
  3. two

  4. \n
\n", "1. one\n\n ```\n - code\n ```\n\n2. two\n", "
    \n
  1. one

    \n\n

    ```

    \n\n
      \n
    • code\n```
    • \n
  2. \n\n
  3. two

  4. \n
\n", } doTestsBlock(t, tests, 0) } func TestTitleBlock_EXTENSION_TITLEBLOCK(t *testing.T) { t.Parallel() var tests = []string{ "% Some title\n" + "% Another title line\n" + "% Yep, more here too\n", "

" + "Some title\n" + "Another title line\n" + "Yep, more here too" + "

\n", } doTestsBlock(t, tests, Titleblock) } func TestBlockComments(t *testing.T) { t.Parallel() var tests = []string{ "Some text\n\n\n", "

Some text

\n\n\n", "Some text\n\n\n", "

Some text

\n\n\n", "Some text\n\n\n", "

Some text

\n\n\n", } doTestsBlock(t, tests, 0) } func TestTOC(t *testing.T) { t.Parallel() var tests = []string{ "# Title\n\n##Subtitle1\n\n##Subtitle2", //"\n\n

Title

\n\n

Subtitle1

\n\n

Subtitle2

\n", `

Title

Subtitle1

Subtitle2

`, "# Title\n\n##Subtitle\n\n#Title2", //"\n\n

Title

\n\n

Subtitle

\n\n

Title2

\n", `

Title

Subtitle

Title2

`, "## Subtitle\n\n# Title", `

Subtitle

Title

`, "# Title 1\n\n## Subtitle 1\n\n### Subsubtitle 1\n\n# Title 2\n\n### Subsubtitle 2", `

Title 1

Subtitle 1

Subsubtitle 1

Title 2

Subsubtitle 2

`, "# Title with `code`", `

Title with code

`, // Trigger empty TOC "#", "", } doTestsParam(t, tests, TestParams{ HTMLFlags: UseXHTML | TOC, }) } func TestCompletePage(t *testing.T) { t.Parallel() var tests = []string{ "*foo*", `

foo

`, } doTestsParam(t, tests, TestParams{HTMLFlags: UseXHTML | CompletePage}) } func TestIsFenceLine(t *testing.T) { t.Parallel() tests := []struct { data []byte infoRequested bool wantEnd int wantMarker string wantInfo string }{ { data: []byte("```"), wantEnd: 3, wantMarker: "```", }, { data: []byte("```\nstuff here\n"), wantEnd: 4, wantMarker: "```", }, { data: []byte("```\nstuff here\n"), infoRequested: true, wantEnd: 4, wantMarker: "```", }, { data: []byte("stuff here\n```\n"), wantEnd: 0, }, { data: []byte("```"), infoRequested: true, wantEnd: 3, wantMarker: "```", }, { data: []byte("``` go"), infoRequested: true, wantEnd: 6, wantMarker: "```", wantInfo: "go", }, { data: []byte("``` go foo bar"), infoRequested: true, wantEnd: 14, wantMarker: "```", wantInfo: "go foo bar", }, { data: []byte("``` go foo bar "), infoRequested: true, wantEnd: 16, wantMarker: "```", wantInfo: "go foo bar", }, } for _, test := range tests { var info *string if test.infoRequested { info = new(string) } end, marker := isFenceLine(test.data, info, "```") if got, want := end, test.wantEnd; got != want { t.Errorf("got end %v, want %v", got, want) } if got, want := marker, test.wantMarker; got != want { t.Errorf("got marker %q, want %q", got, want) } if test.infoRequested { if got, want := *info, test.wantInfo; got != want { t.Errorf("got info string %q, want %q", got, want) } } } } func TestSanitizedAnchorName(t *testing.T) { tests := []struct { text string want string }{ { text: "This is a header", want: "this-is-a-header", }, { text: "This is also a header", want: "this-is-also-a-header", }, { text: "main.go", want: "main-go", }, { text: "Article 123", want: "article-123", }, { text: "<- Let's try this, shall we?", want: "let-s-try-this-shall-we", }, { text: " ", want: "", }, { text: "Hello, 世界", want: "hello-世界", }, } for _, test := range tests { if got := SanitizedAnchorName(test.text); got != test.want { t.Errorf("SanitizedAnchorName(%q):\ngot %q\nwant %q", test.text, got, test.want) } } } blackfriday-2.1.0/doc.go000066400000000000000000000047111374571415200150750ustar00rootroot00000000000000// Package blackfriday is a markdown processor. // // It translates plain text with simple formatting rules into an AST, which can // then be further processed to HTML (provided by Blackfriday itself) or other // formats (provided by the community). // // The simplest way to invoke Blackfriday is to call the Run function. It will // take a text input and produce a text output in HTML (or other format). // // A slightly more sophisticated way to use Blackfriday is to create a Markdown // processor and to call Parse, which returns a syntax tree for the input // document. You can leverage Blackfriday's parsing for content extraction from // markdown documents. You can assign a custom renderer and set various options // to the Markdown processor. // // If you're interested in calling Blackfriday from command line, see // https://github.com/russross/blackfriday-tool. // // Sanitized Anchor Names // // Blackfriday includes an algorithm for creating sanitized anchor names // corresponding to a given input text. This algorithm is used to create // anchors for headings when AutoHeadingIDs extension is enabled. The // algorithm is specified below, so that other packages can create // compatible anchor names and links to those anchors. // // The algorithm iterates over the input text, interpreted as UTF-8, // one Unicode code point (rune) at a time. All runes that are letters (category L) // or numbers (category N) are considered valid characters. They are mapped to // lower case, and included in the output. All other runes are considered // invalid characters. Invalid characters that precede the first valid character, // as well as invalid character that follow the last valid character // are dropped completely. All other sequences of invalid characters // between two valid characters are replaced with a single dash character '-'. // // SanitizedAnchorName exposes this functionality, and can be used to // create compatible links to the anchor names generated by blackfriday. // This algorithm is also implemented in a small standalone package at // github.com/shurcooL/sanitized_anchor_name. It can be useful for clients // that want a small package and don't need full functionality of blackfriday. package blackfriday // NOTE: Keep Sanitized Anchor Name algorithm in sync with package // github.com/shurcooL/sanitized_anchor_name. // Otherwise, users of sanitized_anchor_name will get anchor names // that are incompatible with those generated by blackfriday. blackfriday-2.1.0/entities.go000066400000000000000000002777621374571415200161760ustar00rootroot00000000000000package blackfriday // Extracted from https://html.spec.whatwg.org/multipage/entities.json var entities = map[string]bool{ "Æ": true, "Æ": true, "&": true, "&": true, "Á": true, "Á": true, "Ă": true, "Â": true, "Â": true, "А": true, "𝔄": true, "À": true, "À": true, "Α": true, "Ā": true, "⩓": true, "Ą": true, "𝔸": true, "⁡": true, "Å": true, "Å": true, "𝒜": true, "≔": true, "Ã": true, "Ã": true, "Ä": true, "Ä": true, "∖": true, "⫧": true, "⌆": true, "Б": true, "∵": true, "ℬ": true, "Β": true, "𝔅": true, "𝔹": true, "˘": true, "ℬ": true, "≎": true, "Ч": true, "©": true, "©": true, "Ć": true, "⋒": true, "ⅅ": true, "ℭ": true, "Č": true, "Ç": true, "Ç": true, "Ĉ": true, "∰": true, "Ċ": true, "¸": true, "·": true, "ℭ": true, "Χ": true, "⊙": true, "⊖": true, "⊕": true, "⊗": true, "∲": true, "”": true, "’": true, "∷": true, "⩴": true, "≡": true, "∯": true, "∮": true, "ℂ": true, "∐": true, "∳": true, "⨯": true, "𝒞": true, "⋓": true, "≍": true, "ⅅ": true, "⤑": true, "Ђ": true, "Ѕ": true, "Џ": true, "‡": true, "↡": true, "⫤": true, "Ď": true, "Д": true, "∇": true, "Δ": true, "𝔇": true, "´": true, "˙": true, "˝": true, "`": true, "˜": true, "⋄": true, "ⅆ": true, "𝔻": true, "¨": true, "⃜": true, "≐": true, "∯": true, "¨": true, "⇓": true, "⇐": true, "⇔": true, "⫤": true, "⟸": true, "⟺": true, "⟹": true, "⇒": true, "⊨": true, "⇑": true, "⇕": true, "∥": true, "↓": true, "⤓": true, "⇵": true, "̑": true, "⥐": true, "⥞": true, "↽": true, "⥖": true, "⥟": true, "⇁": true, "⥗": true, "⊤": true, "↧": true, "⇓": true, "𝒟": true, "Đ": true, "Ŋ": true, "Ð": true, "Ð": true, "É": true, "É": true, "Ě": true, "Ê": true, "Ê": true, "Э": true, "Ė": true, "𝔈": true, "È": true, "È": true, "∈": true, "Ē": true, "◻": true, "▫": true, "Ę": true, "𝔼": true, "Ε": true, "⩵": true, "≂": true, "⇌": true, "ℰ": true, "⩳": true, "Η": true, "Ë": true, "Ë": true, "∃": true, "ⅇ": true, "Ф": true, "𝔉": true, "◼": true, "▪": true, "𝔽": true, "∀": true, "ℱ": true, "ℱ": true, "Ѓ": true, ">": true, ">": true, "Γ": true, "Ϝ": true, "Ğ": true, "Ģ": true, "Ĝ": true, "Г": true, "Ġ": true, "𝔊": true, "⋙": true, "𝔾": true, "≥": true, "⋛": true, "≧": true, "⪢": true, "≷": true, "⩾": true, "≳": true, "𝒢": true, "≫": true, "Ъ": true, "ˇ": true, "^": true, "Ĥ": true, "ℌ": true, "ℋ": true, "ℍ": true, "─": true, "ℋ": true, "Ħ": true, "≎": true, "≏": true, "Е": true, "IJ": true, "Ё": true, "Í": true, "Í": true, "Î": true, "Î": true, "И": true, "İ": true, "ℑ": true, "Ì": true, "Ì": true, "ℑ": true, "Ī": true, "ⅈ": true, "⇒": true, "∬": true, "∫": true, "⋂": true, "⁣": true, "⁢": true, "Į": true, "𝕀": true, "Ι": true, "ℐ": true, "Ĩ": true, "І": true, "Ï": true, "Ï": true, "Ĵ": true, "Й": true, "𝔍": true, "𝕁": true, "𝒥": true, "Ј": true, "Є": true, "Х": true, "Ќ": true, "Κ": true, "Ķ": true, "К": true, "𝔎": true, "𝕂": true, "𝒦": true, "Љ": true, "<": true, "<": true, "Ĺ": true, "Λ": true, "⟪": true, "ℒ": true, "↞": true, "Ľ": true, "Ļ": true, "Л": true, "⟨": true, "←": true, "⇤": true, "⇆": true, "⌈": true, "⟦": true, "⥡": true, "⇃": true, "⥙": true, "⌊": true, "↔": true, "⥎": true, "⊣": true, "↤": true, "⥚": true, "⊲": true, "⧏": true, "⊴": true, "⥑": true, "⥠": true, "↿": true, "⥘": true, "↼": true, "⥒": true, "⇐": true, "⇔": true, "⋚": true, "≦": true, "≶": true, "⪡": true, "⩽": true, "≲": true, "𝔏": true, "⋘": true, "⇚": true, "Ŀ": true, "⟵": true, "⟷": true, "⟶": true, "⟸": true, "⟺": true, "⟹": true, "𝕃": true, "↙": true, "↘": true, "ℒ": true, "↰": true, "Ł": true, "≪": true, "⤅": true, "М": true, " ": true, "ℳ": true, "𝔐": true, "∓": true, "𝕄": true, "ℳ": true, "Μ": true, "Њ": true, "Ń": true, "Ň": true, "Ņ": true, "Н": true, "​": true, "​": true, "​": true, "​": true, "≫": true, "≪": true, " ": true, "𝔑": true, "⁠": true, " ": true, "ℕ": true, "⫬": true, "≢": true, "≭": true, "∦": true, "∉": true, "≠": true, "≂̸": true, "∄": true, "≯": true, "≱": true, "≧̸": true, "≫̸": true, "≹": true, "⩾̸": true, "≵": true, "≎̸": true, "≏̸": true, "⋪": true, "⧏̸": true, "⋬": true, "≮": true, "≰": true, "≸": true, "≪̸": true, "⩽̸": true, "≴": true, "⪢̸": true, "⪡̸": true, "⊀": true, "⪯̸": true, "⋠": true, "∌": true, "⋫": true, "⧐̸": true, "⋭": true, "⊏̸": true, "⋢": true, "⊐̸": true, "⋣": true, "⊂⃒": true, "⊈": true, "⊁": true, "⪰̸": true, "⋡": true, "≿̸": true, "⊃⃒": true, "⊉": true, "≁": true, "≄": true, "≇": true, "≉": true, "∤": true, "𝒩": true, "Ñ": true, "Ñ": true, "Ν": true, "Œ": true, "Ó": true, "Ó": true, "Ô": true, "Ô": true, "О": true, "Ő": true, "𝔒": true, "Ò": true, "Ò": true, "Ō": true, "Ω": true, "Ο": true, "𝕆": true, "“": true, "‘": true, "⩔": true, "𝒪": true, "Ø": true, "Ø": true, "Õ": true, "Õ": true, "⨷": true, "Ö": true, "Ö": true, "‾": true, "⏞": true, "⎴": true, "⏜": true, "∂": true, "П": true, "𝔓": true, "Φ": true, "Π": true, "±": true, "ℌ": true, "ℙ": true, "⪻": true, "≺": true, "⪯": true, "≼": true, "≾": true, "″": true, "∏": true, "∷": true, "∝": true, "𝒫": true, "Ψ": true, """: true, """: true, "𝔔": true, "ℚ": true, "𝒬": true, "⤐": true, "®": true, "®": true, "Ŕ": true, "⟫": true, "↠": true, "⤖": true, "Ř": true, "Ŗ": true, "Р": true, "ℜ": true, "∋": true, "⇋": true, "⥯": true, "ℜ": true, "Ρ": true, "⟩": true, "→": true, "⇥": true, "⇄": true, "⌉": true, "⟧": true, "⥝": true, "⇂": true, "⥕": true, "⌋": true, "⊢": true, "↦": true, "⥛": true, "⊳": true, "⧐": true, "⊵": true, "⥏": true, "⥜": true, "↾": true, "⥔": true, "⇀": true, "⥓": true, "⇒": true, "ℝ": true, "⥰": true, "⇛": true, "ℛ": true, "↱": true, "⧴": true, "Щ": true, "Ш": true, "Ь": true, "Ś": true, "⪼": true, "Š": true, "Ş": true, "Ŝ": true, "С": true, "𝔖": true, "↓": true, "←": true, "→": true, "↑": true, "Σ": true, "∘": true, "𝕊": true, "√": true, "□": true, "⊓": true, "⊏": true, "⊑": true, "⊐": true, "⊒": true, "⊔": true, "𝒮": true, "⋆": true, "⋐": true, "⋐": true, "⊆": true, "≻": true, "⪰": true, "≽": true, "≿": true, "∋": true, "∑": true, "⋑": true, "⊃": true, "⊇": true, "⋑": true, "Þ": true, "Þ": true, "™": true, "Ћ": true, "Ц": true, " ": true, "Τ": true, "Ť": true, "Ţ": true, "Т": true, "𝔗": true, "∴": true, "Θ": true, "  ": true, " ": true, "∼": true, "≃": true, "≅": true, "≈": true, "𝕋": true, "⃛": true, "𝒯": true, "Ŧ": true, "Ú": true, "Ú": true, "↟": true, "⥉": true, "Ў": true, "Ŭ": true, "Û": true, "Û": true, "У": true, "Ű": true, "𝔘": true, "Ù": true, "Ù": true, "Ū": true, "_": true, "⏟": true, "⎵": true, "⏝": true, "⋃": true, "⊎": true, "Ų": true, "𝕌": true, "↑": true, "⤒": true, "⇅": true, "↕": true, "⥮": true, "⊥": true, "↥": true, "⇑": true, "⇕": true, "↖": true, "↗": true, "ϒ": true, "Υ": true, "Ů": true, "𝒰": true, "Ũ": true, "Ü": true, "Ü": true, "⊫": true, "⫫": true, "В": true, "⊩": true, "⫦": true, "⋁": true, "‖": true, "‖": true, "∣": true, "|": true, "❘": true, "≀": true, " ": true, "𝔙": true, "𝕍": true, "𝒱": true, "⊪": true, "Ŵ": true, "⋀": true, "𝔚": true, "𝕎": true, "𝒲": true, "𝔛": true, "Ξ": true, "𝕏": true, "𝒳": true, "Я": true, "Ї": true, "Ю": true, "Ý": true, "Ý": true, "Ŷ": true, "Ы": true, "𝔜": true, "𝕐": true, "𝒴": true, "Ÿ": true, "Ж": true, "Ź": true, "Ž": true, "З": true, "Ż": true, "​": true, "Ζ": true, "ℨ": true, "ℤ": true, "𝒵": true, "á": true, "á": true, "ă": true, "∾": true, "∾̳": true, "∿": true, "â": true, "â": true, "´": true, "´": true, "а": true, "æ": true, "æ": true, "⁡": true, "𝔞": true, "à": true, "à": true, "ℵ": true, "ℵ": true, "α": true, "ā": true, "⨿": true, "&": true, "&": true, "∧": true, "⩕": true, "⩜": true, "⩘": true, "⩚": true, "∠": true, "⦤": true, "∠": true, "∡": true, "⦨": true, "⦩": true, "⦪": true, "⦫": true, "⦬": true, "⦭": true, "⦮": true, "⦯": true, "∟": true, "⊾": true, "⦝": true, "∢": true, "Å": true, "⍼": true, "ą": true, "𝕒": true, "≈": true, "⩰": true, "⩯": true, "≊": true, "≋": true, "'": true, "≈": true, "≊": true, "å": true, "å": true, "𝒶": true, "*": true, "≈": true, "≍": true, "ã": true, "ã": true, "ä": true, "ä": true, "∳": true, "⨑": true, "⫭": true, "≌": true, "϶": true, "‵": true, "∽": true, "⋍": true, "⊽": true, "⌅": true, "⌅": true, "⎵": true, "⎶": true, "≌": true, "б": true, "„": true, "∵": true, "∵": true, "⦰": true, "϶": true, "ℬ": true, "β": true, "ℶ": true, "≬": true, "𝔟": true, "⋂": true, "◯": true, "⋃": true, "⨀": true, "⨁": true, "⨂": true, "⨆": true, "★": true, "▽": true, "△": true, "⨄": true, "⋁": true, "⋀": true, "⤍": true, "⧫": true, "▪": true, "▴": true, "▾": true, "◂": true, "▸": true, "␣": true, "▒": true, "░": true, "▓": true, "█": true, "=⃥": true, "≡⃥": true, "⌐": true, "𝕓": true, "⊥": true, "⊥": true, "⋈": true, "╗": true, "╔": true, "╖": true, "╓": true, "═": true, "╦": true, "╩": true, "╤": true, "╧": true, "╝": true, "╚": true, "╜": true, "╙": true, "║": true, "╬": true, "╣": true, "╠": true, "╫": true, "╢": true, "╟": true, "⧉": true, "╕": true, "╒": true, "┐": true, "┌": true, "─": true, "╥": true, "╨": true, "┬": true, "┴": true, "⊟": true, "⊞": true, "⊠": true, "╛": true, "╘": true, "┘": true, "└": true, "│": true, "╪": true, "╡": true, "╞": true, "┼": true, "┤": true, "├": true, "‵": true, "˘": true, "¦": true, "¦": true, "𝒷": true, "⁏": true, "∽": true, "⋍": true, "\": true, "⧅": true, "⟈": true, "•": true, "•": true, "≎": true, "⪮": true, "≏": true, "≏": true, "ć": true, "∩": true, "⩄": true, "⩉": true, "⩋": true, "⩇": true, "⩀": true, "∩︀": true, "⁁": true, "ˇ": true, "⩍": true, "č": true, "ç": true, "ç": true, "ĉ": true, "⩌": true, "⩐": true, "ċ": true, "¸": true, "¸": true, "⦲": true, "¢": true, "¢": true, "·": true, "𝔠": true, "ч": true, "✓": true, "✓": true, "χ": true, "○": true, "⧃": true, "ˆ": true, "≗": true, "↺": true, "↻": true, "®": true, "Ⓢ": true, "⊛": true, "⊚": true, "⊝": true, "≗": true, "⨐": true, "⫯": true, "⧂": true, "♣": true, "♣": true, ":": true, "≔": true, "≔": true, ",": true, "@": true, "∁": true, "∘": true, "∁": true, "ℂ": true, "≅": true, "⩭": true, "∮": true, "𝕔": true, "∐": true, "©": true, "©": true, "℗": true, "↵": true, "✗": true, "𝒸": true, "⫏": true, "⫑": true, "⫐": true, "⫒": true, "⋯": true, "⤸": true, "⤵": true, "⋞": true, "⋟": true, "↶": true, "⤽": true, "∪": true, "⩈": true, "⩆": true, "⩊": true, "⊍": true, "⩅": true, "∪︀": true, "↷": true, "⤼": true, "⋞": true, "⋟": true, "⋎": true, "⋏": true, "¤": true, "¤": true, "↶": true, "↷": true, "⋎": true, "⋏": true, "∲": true, "∱": true, "⌭": true, "⇓": true, "⥥": true, "†": true, "ℸ": true, "↓": true, "‐": true, "⊣": true, "⤏": true, "˝": true, "ď": true, "д": true, "ⅆ": true, "‡": true, "⇊": true, "⩷": true, "°": true, "°": true, "δ": true, "⦱": true, "⥿": true, "𝔡": true, "⇃": true, "⇂": true, "⋄": true, "⋄": true, "♦": true, "♦": true, "¨": true, "ϝ": true, "⋲": true, "÷": true, "÷": true, "÷": true, "⋇": true, "⋇": true, "ђ": true, "⌞": true, "⌍": true, "$": true, "𝕕": true, "˙": true, "≐": true, "≑": true, "∸": true, "∔": true, "⊡": true, "⌆": true, "↓": true, "⇊": true, "⇃": true, "⇂": true, "⤐": true, "⌟": true, "⌌": true, "𝒹": true, "ѕ": true, "⧶": true, "đ": true, "⋱": true, "▿": true, "▾": true, "⇵": true, "⥯": true, "⦦": true, "џ": true, "⟿": true, "⩷": true, "≑": true, "é": true, "é": true, "⩮": true, "ě": true, "≖": true, "ê": true, "ê": true, "≕": true, "э": true, "ė": true, "ⅇ": true, "≒": true, "𝔢": true, "⪚": true, "è": true, "è": true, "⪖": true, "⪘": true, "⪙": true, "⏧": true, "ℓ": true, "⪕": true, "⪗": true, "ē": true, "∅": true, "∅": true, "∅": true, " ": true, " ": true, " ": true, "ŋ": true, " ": true, "ę": true, "𝕖": true, "⋕": true, "⧣": true, "⩱": true, "ε": true, "ε": true, "ϵ": true, "≖": true, "≕": true, "≂": true, "⪖": true, "⪕": true, "=": true, "≟": true, "≡": true, "⩸": true, "⧥": true, "≓": true, "⥱": true, "ℯ": true, "≐": true, "≂": true, "η": true, "ð": true, "ð": true, "ë": true, "ë": true, "€": true, "!": true, "∃": true, "ℰ": true, "ⅇ": true, "≒": true, "ф": true, "♀": true, "ffi": true, "ff": true, "ffl": true, "𝔣": true, "fi": true, "fj": true, "♭": true, "fl": true, "▱": true, "ƒ": true, "𝕗": true, "∀": true, "⋔": true, "⫙": true, "⨍": true, "½": true, "½": true, "⅓": true, "¼": true, "¼": true, "⅕": true, "⅙": true, "⅛": true, "⅔": true, "⅖": true, "¾": true, "¾": true, "⅗": true, "⅜": true, "⅘": true, "⅚": true, "⅝": true, "⅞": true, "⁄": true, "⌢": true, "𝒻": true, "≧": true, "⪌": true, "ǵ": true, "γ": true, "ϝ": true, "⪆": true, "ğ": true, "ĝ": true, "г": true, "ġ": true, "≥": true, "⋛": true, "≥": true, "≧": true, "⩾": true, "⩾": true, "⪩": true, "⪀": true, "⪂": true, "⪄": true, "⋛︀": true, "⪔": true, "𝔤": true, "≫": true, "⋙": true, "ℷ": true, "ѓ": true, "≷": true, "⪒": true, "⪥": true, "⪤": true, "≩": true, "⪊": true, "⪊": true, "⪈": true, "⪈": true, "≩": true, "⋧": true, "𝕘": true, "`": true, "ℊ": true, "≳": true, "⪎": true, "⪐": true, ">": true, ">": true, "⪧": true, "⩺": true, "⋗": true, "⦕": true, "⩼": true, "⪆": true, "⥸": true, "⋗": true, "⋛": true, "⪌": true, "≷": true, "≳": true, "≩︀": true, "≩︀": true, "⇔": true, " ": true, "½": true, "ℋ": true, "ъ": true, "↔": true, "⥈": true, "↭": true, "ℏ": true, "ĥ": true, "♥": true, "♥": true, "…": true, "⊹": true, "𝔥": true, "⤥": true, "⤦": true, "⇿": true, "∻": true, "↩": true, "↪": true, "𝕙": true, "―": true, "𝒽": true, "ℏ": true, "ħ": true, "⁃": true, "‐": true, "í": true, "í": true, "⁣": true, "î": true, "î": true, "и": true, "е": true, "¡": true, "¡": true, "⇔": true, "𝔦": true, "ì": true, "ì": true, "ⅈ": true, "⨌": true, "∭": true, "⧜": true, "℩": true, "ij": true, "ī": true, "ℑ": true, "ℐ": true, "ℑ": true, "ı": true, "⊷": true, "Ƶ": true, "∈": true, "℅": true, "∞": true, "⧝": true, "ı": true, "∫": true, "⊺": true, "ℤ": true, "⊺": true, "⨗": true, "⨼": true, "ё": true, "į": true, "𝕚": true, "ι": true, "⨼": true, "¿": true, "¿": true, "𝒾": true, "∈": true, "⋹": true, "⋵": true, "⋴": true, "⋳": true, "∈": true, "⁢": true, "ĩ": true, "і": true, "ï": true, "ï": true, "ĵ": true, "й": true, "𝔧": true, "ȷ": true, "𝕛": true, "𝒿": true, "ј": true, "є": true, "κ": true, "ϰ": true, "ķ": true, "к": true, "𝔨": true, "ĸ": true, "х": true, "ќ": true, "𝕜": true, "𝓀": true, "⇚": true, "⇐": true, "⤛": true, "⤎": true, "≦": true, "⪋": true, "⥢": true, "ĺ": true, "⦴": true, "ℒ": true, "λ": true, "⟨": true, "⦑": true, "⟨": true, "⪅": true, "«": true, "«": true, "←": true, "⇤": true, "⤟": true, "⤝": true, "↩": true, "↫": true, "⤹": true, "⥳": true, "↢": true, "⪫": true, "⤙": true, "⪭": true, "⪭︀": true, "⤌": true, "❲": true, "{": true, "[": true, "⦋": true, "⦏": true, "⦍": true, "ľ": true, "ļ": true, "⌈": true, "{": true, "л": true, "⤶": true, "“": true, "„": true, "⥧": true, "⥋": true, "↲": true, "≤": true, "←": true, "↢": true, "↽": true, "↼": true, "⇇": true, "↔": true, "⇆": true, "⇋": true, "↭": true, "⋋": true, "⋚": true, "≤": true, "≦": true, "⩽": true, "⩽": true, "⪨": true, "⩿": true, "⪁": true, "⪃": true, "⋚︀": true, "⪓": true, "⪅": true, "⋖": true, "⋚": true, "⪋": true, "≶": true, "≲": true, "⥼": true, "⌊": true, "𝔩": true, "≶": true, "⪑": true, "↽": true, "↼": true, "⥪": true, "▄": true, "љ": true, "≪": true, "⇇": true, "⌞": true, "⥫": true, "◺": true, "ŀ": true, "⎰": true, "⎰": true, "≨": true, "⪉": true, "⪉": true, "⪇": true, "⪇": true, "≨": true, "⋦": true, "⟬": true, "⇽": true, "⟦": true, "⟵": true, "⟷": true, "⟼": true, "⟶": true, "↫": true, "↬": true, "⦅": true, "𝕝": true, "⨭": true, "⨴": true, "∗": true, "_": true, "◊": true, "◊": true, "⧫": true, "(": true, "⦓": true, "⇆": true, "⌟": true, "⇋": true, "⥭": true, "‎": true, "⊿": true, "‹": true, "𝓁": true, "↰": true, "≲": true, "⪍": true, "⪏": true, "[": true, "‘": true, "‚": true, "ł": true, "<": true, "<": true, "⪦": true, "⩹": true, "⋖": true, "⋋": true, "⋉": true, "⥶": true, "⩻": true, "⦖": true, "◃": true, "⊴": true, "◂": true, "⥊": true, "⥦": true, "≨︀": true, "≨︀": true, "∺": true, "¯": true, "¯": true, "♂": true, "✠": true, "✠": true, "↦": true, "↦": true, "↧": true, "↤": true, "↥": true, "▮": true, "⨩": true, "м": true, "—": true, "∡": true, "𝔪": true, "℧": true, "µ": true, "µ": true, "∣": true, "*": true, "⫰": true, "·": true, "·": true, "−": true, "⊟": true, "∸": true, "⨪": true, "⫛": true, "…": true, "∓": true, "⊧": true, "𝕞": true, "∓": true, "𝓂": true, "∾": true, "μ": true, "⊸": true, "⊸": true, "⋙̸": true, "≫⃒": true, "≫̸": true, "⇍": true, "⇎": true, "⋘̸": true, "≪⃒": true, "≪̸": true, "⇏": true, "⊯": true, "⊮": true, "∇": true, "ń": true, "∠⃒": true, "≉": true, "⩰̸": true, "≋̸": true, "ʼn": true, "≉": true, "♮": true, "♮": true, "ℕ": true, " ": true, " ": true, "≎̸": true, "≏̸": true, "⩃": true, "ň": true, "ņ": true, "≇": true, "⩭̸": true, "⩂": true, "н": true, "–": true, "≠": true, "⇗": true, "⤤": true, "↗": true, "↗": true, "≐̸": true, "≢": true, "⤨": true, "≂̸": true, "∄": true, "∄": true, "𝔫": true, "≧̸": true, "≱": true, "≱": true, "≧̸": true, "⩾̸": true, "⩾̸": true, "≵": true, "≯": true, "≯": true, "⇎": true, "↮": true, "⫲": true, "∋": true, "⋼": true, "⋺": true, "∋": true, "њ": true, "⇍": true, "≦̸": true, "↚": true, "‥": true, "≰": true, "↚": true, "↮": true, "≰": true, "≦̸": true, "⩽̸": true, "⩽̸": true, "≮": true, "≴": true, "≮": true, "⋪": true, "⋬": true, "∤": true, "𝕟": true, "¬": true, "¬": true, "∉": true, "⋹̸": true, "⋵̸": true, "∉": true, "⋷": true, "⋶": true, "∌": true, "∌": true, "⋾": true, "⋽": true, "∦": true, "∦": true, "⫽⃥": true, "∂̸": true, "⨔": true, "⊀": true, "⋠": true, "⪯̸": true, "⊀": true, "⪯̸": true, "⇏": true, "↛": true, "⤳̸": true, "↝̸": true, "↛": true, "⋫": true, "⋭": true, "⊁": true, "⋡": true, "⪰̸": true, "𝓃": true, "∤": true, "∦": true, "≁": true, "≄": true, "≄": true, "∤": true, "∦": true, "⋢": true, "⋣": true, "⊄": true, "⫅̸": true, "⊈": true, "⊂⃒": true, "⊈": true, "⫅̸": true, "⊁": true, "⪰̸": true, "⊅": true, "⫆̸": true, "⊉": true, "⊃⃒": true, "⊉": true, "⫆̸": true, "≹": true, "ñ": true, "ñ": true, "≸": true, "⋪": true, "⋬": true, "⋫": true, "⋭": true, "ν": true, "#": true, "№": true, " ": true, "⊭": true, "⤄": true, "≍⃒": true, "⊬": true, "≥⃒": true, ">⃒": true, "⧞": true, "⤂": true, "≤⃒": true, "<⃒": true, "⊴⃒": true, "⤃": true, "⊵⃒": true, "∼⃒": true, "⇖": true, "⤣": true, "↖": true, "↖": true, "⤧": true, "Ⓢ": true, "ó": true, "ó": true, "⊛": true, "⊚": true, "ô": true, "ô": true, "о": true, "⊝": true, "ő": true, "⨸": true, "⊙": true, "⦼": true, "œ": true, "⦿": true, "𝔬": true, "˛": true, "ò": true, "ò": true, "⧁": true, "⦵": true, "Ω": true, "∮": true, "↺": true, "⦾": true, "⦻": true, "‾": true, "⧀": true, "ō": true, "ω": true, "ο": true, "⦶": true, "⊖": true, "𝕠": true, "⦷": true, "⦹": true, "⊕": true, "∨": true, "↻": true, "⩝": true, "ℴ": true, "ℴ": true, "ª": true, "ª": true, "º": true, "º": true, "⊶": true, "⩖": true, "⩗": true, "⩛": true, "ℴ": true, "ø": true, "ø": true, "⊘": true, "õ": true, "õ": true, "⊗": true, "⨶": true, "ö": true, "ö": true, "⌽": true, "∥": true, "¶": true, "¶": true, "∥": true, "⫳": true, "⫽": true, "∂": true, "п": true, "%": true, ".": true, "‰": true, "⊥": true, "‱": true, "𝔭": true, "φ": true, "ϕ": true, "ℳ": true, "☎": true, "π": true, "⋔": true, "ϖ": true, "ℏ": true, "ℎ": true, "ℏ": true, "+": true, "⨣": true, "⊞": true, "⨢": true, "∔": true, "⨥": true, "⩲": true, "±": true, "±": true, "⨦": true, "⨧": true, "±": true, "⨕": true, "𝕡": true, "£": true, "£": true, "≺": true, "⪳": true, "⪷": true, "≼": true, "⪯": true, "≺": true, "⪷": true, "≼": true, "⪯": true, "⪹": true, "⪵": true, "⋨": true, "≾": true, "′": true, "ℙ": true, "⪵": true, "⪹": true, "⋨": true, "∏": true, "⌮": true, "⌒": true, "⌓": true, "∝": true, "∝": true, "≾": true, "⊰": true, "𝓅": true, "ψ": true, " ": true, "𝔮": true, "⨌": true, "𝕢": true, "⁗": true, "𝓆": true, "ℍ": true, "⨖": true, "?": true, "≟": true, """: true, """: true, "⇛": true, "⇒": true, "⤜": true, "⤏": true, "⥤": true, "∽̱": true, "ŕ": true, "√": true, "⦳": true, "⟩": true, "⦒": true, "⦥": true, "⟩": true, "»": true, "»": true, "→": true, "⥵": true, "⇥": true, "⤠": true, "⤳": true, "⤞": true, "↪": true, "↬": true, "⥅": true, "⥴": true, "↣": true, "↝": true, "⤚": true, "∶": true, "ℚ": true, "⤍": true, "❳": true, "}": true, "]": true, "⦌": true, "⦎": true, "⦐": true, "ř": true, "ŗ": true, "⌉": true, "}": true, "р": true, "⤷": true, "⥩": true, "”": true, "”": true, "↳": true, "ℜ": true, "ℛ": true, "ℜ": true, "ℝ": true, "▭": true, "®": true, "®": true, "⥽": true, "⌋": true, "𝔯": true, "⇁": true, "⇀": true, "⥬": true, "ρ": true, "ϱ": true, "→": true, "↣": true, "⇁": true, "⇀": true, "⇄": true, "⇌": true, "⇉": true, "↝": true, "⋌": true, "˚": true, "≓": true, "⇄": true, "⇌": true, "‏": true, "⎱": true, "⎱": true, "⫮": true, "⟭": true, "⇾": true, "⟧": true, "⦆": true, "𝕣": true, "⨮": true, "⨵": true, ")": true, "⦔": true, "⨒": true, "⇉": true, "›": true, "𝓇": true, "↱": true, "]": true, "’": true, "’": true, "⋌": true, "⋊": true, "▹": true, "⊵": true, "▸": true, "⧎": true, "⥨": true, "℞": true, "ś": true, "‚": true, "≻": true, "⪴": true, "⪸": true, "š": true, "≽": true, "⪰": true, "ş": true, "ŝ": true, "⪶": true, "⪺": true, "⋩": true, "⨓": true, "≿": true, "с": true, "⋅": true, "⊡": true, "⩦": true, "⇘": true, "⤥": true, "↘": true, "↘": true, "§": true, "§": true, ";": true, "⤩": true, "∖": true, "∖": true, "✶": true, "𝔰": true, "⌢": true, "♯": true, "щ": true, "ш": true, "∣": true, "∥": true, "­": true, "­": true, "σ": true, "ς": true, "ς": true, "∼": true, "⩪": true, "≃": true, "≃": true, "⪞": true, "⪠": true, "⪝": true, "⪟": true, "≆": true, "⨤": true, "⥲": true, "←": true, "∖": true, "⨳": true, "⧤": true, "∣": true, "⌣": true, "⪪": true, "⪬": true, "⪬︀": true, "ь": true, "/": true, "⧄": true, "⌿": true, "𝕤": true, "♠": true, "♠": true, "∥": true, "⊓": true, "⊓︀": true, "⊔": true, "⊔︀": true, "⊏": true, "⊑": true, "⊏": true, "⊑": true, "⊐": true, "⊒": true, "⊐": true, "⊒": true, "□": true, "□": true, "▪": true, "▪": true, "→": true, "𝓈": true, "∖": true, "⌣": true, "⋆": true, "☆": true, "★": true, "ϵ": true, "ϕ": true, "¯": true, "⊂": true, "⫅": true, "⪽": true, "⊆": true, "⫃": true, "⫁": true, "⫋": true, "⊊": true, "⪿": true, "⥹": true, "⊂": true, "⊆": true, "⫅": true, "⊊": true, "⫋": true, "⫇": true, "⫕": true, "⫓": true, "≻": true, "⪸": true, "≽": true, "⪰": true, "⪺": true, "⪶": true, "⋩": true, "≿": true, "∑": true, "♪": true, "¹": true, "¹": true, "²": true, "²": true, "³": true, "³": true, "⊃": true, "⫆": true, "⪾": true, "⫘": true, "⊇": true, "⫄": true, "⟉": true, "⫗": true, "⥻": true, "⫂": true, "⫌": true, "⊋": true, "⫀": true, "⊃": true, "⊇": true, "⫆": true, "⊋": true, "⫌": true, "⫈": true, "⫔": true, "⫖": true, "⇙": true, "⤦": true, "↙": true, "↙": true, "⤪": true, "ß": true, "ß": true, "⌖": true, "τ": true, "⎴": true, "ť": true, "ţ": true, "т": true, "⃛": true, "⌕": true, "𝔱": true, "∴": true, "∴": true, "θ": true, "ϑ": true, "ϑ": true, "≈": true, "∼": true, " ": true, "≈": true, "∼": true, "þ": true, "þ": true, "˜": true, "×": true, "×": true, "⊠": true, "⨱": true, "⨰": true, "∭": true, "⤨": true, "⊤": true, "⌶": true, "⫱": true, "𝕥": true, "⫚": true, "⤩": true, "‴": true, "™": true, "▵": true, "▿": true, "◃": true, "⊴": true, "≜": true, "▹": true, "⊵": true, "◬": true, "≜": true, "⨺": true, "⨹": true, "⧍": true, "⨻": true, "⏢": true, "𝓉": true, "ц": true, "ћ": true, "ŧ": true, "≬": true, "↞": true, "↠": true, "⇑": true, "⥣": true, "ú": true, "ú": true, "↑": true, "ў": true, "ŭ": true, "û": true, "û": true, "у": true, "⇅": true, "ű": true, "⥮": true, "⥾": true, "𝔲": true, "ù": true, "ù": true, "↿": true, "↾": true, "▀": true, "⌜": true, "⌜": true, "⌏": true, "◸": true, "ū": true, "¨": true, "¨": true, "ų": true, "𝕦": true, "↑": true, "↕": true, "↿": true, "↾": true, "⊎": true, "υ": true, "ϒ": true, "υ": true, "⇈": true, "⌝": true, "⌝": true, "⌎": true, "ů": true, "◹": true, "𝓊": true, "⋰": true, "ũ": true, "▵": true, "▴": true, "⇈": true, "ü": true, "ü": true, "⦧": true, "⇕": true, "⫨": true, "⫩": true, "⊨": true, "⦜": true, "ϵ": true, "ϰ": true, "∅": true, "ϕ": true, "ϖ": true, "∝": true, "↕": true, "ϱ": true, "ς": true, "⊊︀": true, "⫋︀": true, "⊋︀": true, "⫌︀": true, "ϑ": true, "⊲": true, "⊳": true, "в": true, "⊢": true, "∨": true, "⊻": true, "≚": true, "⋮": true, "|": true, "|": true, "𝔳": true, "⊲": true, "⊂⃒": true, "⊃⃒": true, "𝕧": true, "∝": true, "⊳": true, "𝓋": true, "⫋︀": true, "⊊︀": true, "⫌︀": true, "⊋︀": true, "⦚": true, "ŵ": true, "⩟": true, "∧": true, "≙": true, "℘": true, "𝔴": true, "𝕨": true, "℘": true, "≀": true, "≀": true, "𝓌": true, "⋂": true, "◯": true, "⋃": true, "▽": true, "𝔵": true, "⟺": true, "⟷": true, "ξ": true, "⟸": true, "⟵": true, "⟼": true, "⋻": true, "⨀": true, "𝕩": true, "⨁": true, "⨂": true, "⟹": true, "⟶": true, "𝓍": true, "⨆": true, "⨄": true, "△": true, "⋁": true, "⋀": true, "ý": true, "ý": true, "я": true, "ŷ": true, "ы": true, "¥": true, "¥": true, "𝔶": true, "ї": true, "𝕪": true, "𝓎": true, "ю": true, "ÿ": true, "ÿ": true, "ź": true, "ž": true, "з": true, "ż": true, "ℨ": true, "ζ": true, "𝔷": true, "ж": true, "⇝": true, "𝕫": true, "𝓏": true, "‍": true, "‌": true, } blackfriday-2.1.0/esc.go000066400000000000000000000025271374571415200151050ustar00rootroot00000000000000package blackfriday import ( "html" "io" ) var htmlEscaper = [256][]byte{ '&': []byte("&"), '<': []byte("<"), '>': []byte(">"), '"': []byte("""), } func escapeHTML(w io.Writer, s []byte) { escapeEntities(w, s, false) } func escapeAllHTML(w io.Writer, s []byte) { escapeEntities(w, s, true) } func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) { var start, end int for end < len(s) { escSeq := htmlEscaper[s[end]] if escSeq != nil { isEntity, entityEnd := nodeIsEntity(s, end) if isEntity && !escapeValidEntities { w.Write(s[start : entityEnd+1]) start = entityEnd + 1 } else { w.Write(s[start:end]) w.Write(escSeq) start = end + 1 } } end++ } if start < len(s) && end <= len(s) { w.Write(s[start:end]) } } func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) { isEntity = false endEntityPos = end + 1 if s[end] == '&' { for endEntityPos < len(s) { if s[endEntityPos] == ';' { if entities[string(s[end:endEntityPos+1])] { isEntity = true break } } if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' { break } endEntityPos++ } } return isEntity, endEntityPos } func escLink(w io.Writer, text []byte) { unesc := html.UnescapeString(string(text)) escapeHTML(w, []byte(unesc)) } blackfriday-2.1.0/esc_test.go000066400000000000000000000022571374571415200161440ustar00rootroot00000000000000package blackfriday import ( "bytes" "testing" ) func TestEsc(t *testing.T) { t.Parallel() tests := []string{ "abc", "abc", "a&c", "a&c", "<", "<", "[]:<", "[]:<", "Hello |" processingInstruction = "[<][?].*?[?][>]" singleQuotedValue = "'[^']*'" tagName = "[A-Za-z][A-Za-z0-9-]*" unquotedValue = "[^\"'=<>`\\x00-\\x20]+" ) // HTMLRendererParameters is a collection of supplementary parameters tweaking // the behavior of various parts of HTML renderer. type HTMLRendererParameters struct { // Prepend this text to each relative URL. AbsolutePrefix string // Add this text to each footnote anchor, to ensure uniqueness. FootnoteAnchorPrefix string // Show this text inside the tag for a footnote return link, if the // HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string // [return] is used. FootnoteReturnLinkContents string // If set, add this text to the front of each Heading ID, to ensure // uniqueness. HeadingIDPrefix string // If set, add this text to the back of each Heading ID, to ensure uniqueness. HeadingIDSuffix string // Increase heading levels: if the offset is 1,

becomes

etc. // Negative offset is also valid. // Resulting levels are clipped between 1 and 6. HeadingLevelOffset int Title string // Document title (used if CompletePage is set) CSS string // Optional CSS file URL (used if CompletePage is set) Icon string // Optional icon file URL (used if CompletePage is set) Flags HTMLFlags // Flags allow customizing this renderer's behavior } // HTMLRenderer is a type that implements the Renderer interface for HTML output. // // Do not create this directly, instead use the NewHTMLRenderer function. type HTMLRenderer struct { HTMLRendererParameters closeTag string // how to end singleton tags: either " />" or ">" // Track heading IDs to prevent ID collision in a single generation. headingIDs map[string]int lastOutputLen int disableTags int sr *SPRenderer } const ( xhtmlClose = " />" htmlClose = ">" ) // NewHTMLRenderer creates and configures an HTMLRenderer object, which // satisfies the Renderer interface. func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer { // configure the rendering engine closeTag := htmlClose if params.Flags&UseXHTML != 0 { closeTag = xhtmlClose } if params.FootnoteReturnLinkContents == "" { // U+FE0E is VARIATION SELECTOR-15. // It suppresses automatic emoji presentation of the preceding // U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS. params.FootnoteReturnLinkContents = "↩\ufe0e" } return &HTMLRenderer{ HTMLRendererParameters: params, closeTag: closeTag, headingIDs: make(map[string]int), sr: NewSmartypantsRenderer(params.Flags), } } func isHTMLTag(tag []byte, tagname string) bool { found, _ := findHTMLTagPos(tag, tagname) return found } // Look for a character, but ignore it when it's in any kind of quotes, it // might be JavaScript func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int { inSingleQuote := false inDoubleQuote := false inGraveQuote := false i := start for i < len(html) { switch { case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote: return i case html[i] == '\'': inSingleQuote = !inSingleQuote case html[i] == '"': inDoubleQuote = !inDoubleQuote case html[i] == '`': inGraveQuote = !inGraveQuote } i++ } return start } func findHTMLTagPos(tag []byte, tagname string) (bool, int) { i := 0 if i < len(tag) && tag[0] != '<' { return false, -1 } i++ i = skipSpace(tag, i) if i < len(tag) && tag[i] == '/' { i++ } i = skipSpace(tag, i) j := 0 for ; i < len(tag); i, j = i+1, j+1 { if j >= len(tagname) { break } if strings.ToLower(string(tag[i]))[0] != tagname[j] { return false, -1 } } if i == len(tag) { return false, -1 } rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>') if rightAngle >= i { return true, rightAngle } return false, -1 } func skipSpace(tag []byte, i int) int { for i < len(tag) && isspace(tag[i]) { i++ } return i } func isRelativeLink(link []byte) (yes bool) { // a tag begin with '#' if link[0] == '#' { return true } // link begin with '/' but not '//', the second maybe a protocol relative link if len(link) >= 2 && link[0] == '/' && link[1] != '/' { return true } // only the root '/' if len(link) == 1 && link[0] == '/' { return true } // current directory : begin with "./" if bytes.HasPrefix(link, []byte("./")) { return true } // parent directory : begin with "../" if bytes.HasPrefix(link, []byte("../")) { return true } return false } func (r *HTMLRenderer) ensureUniqueHeadingID(id string) string { for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] { tmp := fmt.Sprintf("%s-%d", id, count+1) if _, tmpFound := r.headingIDs[tmp]; !tmpFound { r.headingIDs[id] = count + 1 id = tmp } else { id = id + "-1" } } if _, found := r.headingIDs[id]; !found { r.headingIDs[id] = 0 } return id } func (r *HTMLRenderer) addAbsPrefix(link []byte) []byte { if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' { newDest := r.AbsolutePrefix if link[0] != '/' { newDest += "/" } newDest += string(link) return []byte(newDest) } return link } func appendLinkAttrs(attrs []string, flags HTMLFlags, link []byte) []string { if isRelativeLink(link) { return attrs } val := []string{} if flags&NofollowLinks != 0 { val = append(val, "nofollow") } if flags&NoreferrerLinks != 0 { val = append(val, "noreferrer") } if flags&NoopenerLinks != 0 { val = append(val, "noopener") } if flags&HrefTargetBlank != 0 { attrs = append(attrs, "target=\"_blank\"") } if len(val) == 0 { return attrs } attr := fmt.Sprintf("rel=%q", strings.Join(val, " ")) return append(attrs, attr) } func isMailto(link []byte) bool { return bytes.HasPrefix(link, []byte("mailto:")) } func needSkipLink(flags HTMLFlags, dest []byte) bool { if flags&SkipLinks != 0 { return true } return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest) } func isSmartypantable(node *Node) bool { pt := node.Parent.Type return pt != Link && pt != CodeBlock && pt != Code } func appendLanguageAttr(attrs []string, info []byte) []string { if len(info) == 0 { return attrs } endOfLang := bytes.IndexAny(info, "\t ") if endOfLang < 0 { endOfLang = len(info) } return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang])) } func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) { w.Write(name) if len(attrs) > 0 { w.Write(spaceBytes) w.Write([]byte(strings.Join(attrs, " "))) } w.Write(gtBytes) r.lastOutputLen = 1 } func footnoteRef(prefix string, node *Node) []byte { urlFrag := prefix + string(slugify(node.Destination)) anchor := fmt.Sprintf(`%d`, urlFrag, node.NoteID) return []byte(fmt.Sprintf(`%s`, urlFrag, anchor)) } func footnoteItem(prefix string, slug []byte) []byte { return []byte(fmt.Sprintf(`
  • `, prefix, slug)) } func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte { const format = ` %s` return []byte(fmt.Sprintf(format, prefix, slug, returnLink)) } func itemOpenCR(node *Node) bool { if node.Prev == nil { return false } ld := node.Parent.ListData return !ld.Tight && ld.ListFlags&ListTypeDefinition == 0 } func skipParagraphTags(node *Node) bool { grandparent := node.Parent.Parent if grandparent == nil || grandparent.Type != List { return false } tightOrTerm := grandparent.Tight || node.Parent.ListFlags&ListTypeTerm != 0 return grandparent.Type == List && tightOrTerm } func cellAlignment(align CellAlignFlags) string { switch align { case TableAlignmentLeft: return "left" case TableAlignmentRight: return "right" case TableAlignmentCenter: return "center" default: return "" } } func (r *HTMLRenderer) out(w io.Writer, text []byte) { if r.disableTags > 0 { w.Write(htmlTagRe.ReplaceAll(text, []byte{})) } else { w.Write(text) } r.lastOutputLen = len(text) } func (r *HTMLRenderer) cr(w io.Writer) { if r.lastOutputLen > 0 { r.out(w, nlBytes) } } var ( nlBytes = []byte{'\n'} gtBytes = []byte{'>'} spaceBytes = []byte{' '} ) var ( brTag = []byte("
    ") brXHTMLTag = []byte("
    ") emTag = []byte("") emCloseTag = []byte("") strongTag = []byte("") strongCloseTag = []byte("") delTag = []byte("") delCloseTag = []byte("") ttTag = []byte("") ttCloseTag = []byte("") aTag = []byte("") preTag = []byte("
    ")
    	preCloseTag        = []byte("
    ") codeTag = []byte("") codeCloseTag = []byte("") pTag = []byte("

    ") pCloseTag = []byte("

    ") blockquoteTag = []byte("
    ") blockquoteCloseTag = []byte("
    ") hrTag = []byte("
    ") hrXHTMLTag = []byte("
    ") ulTag = []byte("
      ") ulCloseTag = []byte("
    ") olTag = []byte("
      ") olCloseTag = []byte("
    ") dlTag = []byte("
    ") dlCloseTag = []byte("
    ") liTag = []byte("
  • ") liCloseTag = []byte("
  • ") ddTag = []byte("
    ") ddCloseTag = []byte("
    ") dtTag = []byte("
    ") dtCloseTag = []byte("
    ") tableTag = []byte("") tableCloseTag = []byte("
    ") tdTag = []byte("") thTag = []byte("") theadTag = []byte("") theadCloseTag = []byte("") tbodyTag = []byte("") tbodyCloseTag = []byte("") trTag = []byte("") trCloseTag = []byte("") h1Tag = []byte("") h2Tag = []byte("") h3Tag = []byte("") h4Tag = []byte("") h5Tag = []byte("") h6Tag = []byte("") footnotesDivBytes = []byte("\n
    \n\n") footnotesCloseDivBytes = []byte("\n
    \n") ) func headingTagsFromLevel(level int) ([]byte, []byte) { if level <= 1 { return h1Tag, h1CloseTag } switch level { case 2: return h2Tag, h2CloseTag case 3: return h3Tag, h3CloseTag case 4: return h4Tag, h4CloseTag case 5: return h5Tag, h5CloseTag } return h6Tag, h6CloseTag } func (r *HTMLRenderer) outHRTag(w io.Writer) { if r.Flags&UseXHTML == 0 { r.out(w, hrTag) } else { r.out(w, hrXHTMLTag) } } // RenderNode is a default renderer of a single node of a syntax tree. For // block nodes it will be called twice: first time with entering=true, second // time with entering=false, so that it could know when it's working on an open // tag and when on close. It writes the result to w. // // The return value is a way to tell the calling walker to adjust its walk // pattern: e.g. it can terminate the traversal by returning Terminate. Or it // can ask the walker to skip a subtree of this node by returning SkipChildren. // The typical behavior is to return GoToNext, which asks for the usual // traversal to the next node. func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus { attrs := []string{} switch node.Type { case Text: if r.Flags&Smartypants != 0 { var tmp bytes.Buffer escapeHTML(&tmp, node.Literal) r.sr.Process(w, tmp.Bytes()) } else { if node.Parent.Type == Link { escLink(w, node.Literal) } else { escapeHTML(w, node.Literal) } } case Softbreak: r.cr(w) // TODO: make it configurable via out(renderer.softbreak) case Hardbreak: if r.Flags&UseXHTML == 0 { r.out(w, brTag) } else { r.out(w, brXHTMLTag) } r.cr(w) case Emph: if entering { r.out(w, emTag) } else { r.out(w, emCloseTag) } case Strong: if entering { r.out(w, strongTag) } else { r.out(w, strongCloseTag) } case Del: if entering { r.out(w, delTag) } else { r.out(w, delCloseTag) } case HTMLSpan: if r.Flags&SkipHTML != 0 { break } r.out(w, node.Literal) case Link: // mark it but don't link it if it is not a safe link: no smartypants dest := node.LinkData.Destination if needSkipLink(r.Flags, dest) { if entering { r.out(w, ttTag) } else { r.out(w, ttCloseTag) } } else { if entering { dest = r.addAbsPrefix(dest) var hrefBuf bytes.Buffer hrefBuf.WriteString("href=\"") escLink(&hrefBuf, dest) hrefBuf.WriteByte('"') attrs = append(attrs, hrefBuf.String()) if node.NoteID != 0 { r.out(w, footnoteRef(r.FootnoteAnchorPrefix, node)) break } attrs = appendLinkAttrs(attrs, r.Flags, dest) if len(node.LinkData.Title) > 0 { var titleBuff bytes.Buffer titleBuff.WriteString("title=\"") escapeHTML(&titleBuff, node.LinkData.Title) titleBuff.WriteByte('"') attrs = append(attrs, titleBuff.String()) } r.tag(w, aTag, attrs) } else { if node.NoteID != 0 { break } r.out(w, aCloseTag) } } case Image: if r.Flags&SkipImages != 0 { return SkipChildren } if entering { dest := node.LinkData.Destination dest = r.addAbsPrefix(dest) if r.disableTags == 0 { //if options.safe && potentiallyUnsafe(dest) { //out(w, ``)
				//} else {
				r.out(w, []byte(`<img src=`)) } } case Code: r.out(w, codeTag) escapeAllHTML(w, node.Literal) r.out(w, codeCloseTag) case Document: break case Paragraph: if skipParagraphTags(node) { break } if entering { // TODO: untangle this clusterfuck about when the newlines need // to be added and when not. if node.Prev != nil { switch node.Prev.Type { case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule: r.cr(w) } } if node.Parent.Type == BlockQuote && node.Prev == nil { r.cr(w) } r.out(w, pTag) } else { r.out(w, pCloseTag) if !(node.Parent.Type == Item && node.Next == nil) { r.cr(w) } } case BlockQuote: if entering { r.cr(w) r.out(w, blockquoteTag) } else { r.out(w, blockquoteCloseTag) r.cr(w) } case HTMLBlock: if r.Flags&SkipHTML != 0 { break } r.cr(w) r.out(w, node.Literal) r.cr(w) case Heading: headingLevel := r.HTMLRendererParameters.HeadingLevelOffset + node.Level openTag, closeTag := headingTagsFromLevel(headingLevel) if entering { if node.IsTitleblock { attrs = append(attrs, `class="title"`) } if node.HeadingID != "" { id := r.ensureUniqueHeadingID(node.HeadingID) if r.HeadingIDPrefix != "" { id = r.HeadingIDPrefix + id } if r.HeadingIDSuffix != "" { id = id + r.HeadingIDSuffix } attrs = append(attrs, fmt.Sprintf(`id="%s"`, id)) } r.cr(w) r.tag(w, openTag, attrs) } else { r.out(w, closeTag) if !(node.Parent.Type == Item && node.Next == nil) { r.cr(w) } } case HorizontalRule: r.cr(w) r.outHRTag(w) r.cr(w) case List: openTag := ulTag closeTag := ulCloseTag if node.ListFlags&ListTypeOrdered != 0 { openTag = olTag closeTag = olCloseTag } if node.ListFlags&ListTypeDefinition != 0 { openTag = dlTag closeTag = dlCloseTag } if entering { if node.IsFootnotesList { r.out(w, footnotesDivBytes) r.outHRTag(w) r.cr(w) } r.cr(w) if node.Parent.Type == Item && node.Parent.Parent.Tight { r.cr(w) } r.tag(w, openTag[:len(openTag)-1], attrs) r.cr(w) } else { r.out(w, closeTag) //cr(w) //if node.parent.Type != Item { // cr(w) //} if node.Parent.Type == Item && node.Next != nil { r.cr(w) } if node.Parent.Type == Document || node.Parent.Type == BlockQuote { r.cr(w) } if node.IsFootnotesList { r.out(w, footnotesCloseDivBytes) } } case Item: openTag := liTag closeTag := liCloseTag if node.ListFlags&ListTypeDefinition != 0 { openTag = ddTag closeTag = ddCloseTag } if node.ListFlags&ListTypeTerm != 0 { openTag = dtTag closeTag = dtCloseTag } if entering { if itemOpenCR(node) { r.cr(w) } if node.ListData.RefLink != nil { slug := slugify(node.ListData.RefLink) r.out(w, footnoteItem(r.FootnoteAnchorPrefix, slug)) break } r.out(w, openTag) } else { if node.ListData.RefLink != nil { slug := slugify(node.ListData.RefLink) if r.Flags&FootnoteReturnLinks != 0 { r.out(w, footnoteReturnLink(r.FootnoteAnchorPrefix, r.FootnoteReturnLinkContents, slug)) } } r.out(w, closeTag) r.cr(w) } case CodeBlock: attrs = appendLanguageAttr(attrs, node.Info) r.cr(w) r.out(w, preTag) r.tag(w, codeTag[:len(codeTag)-1], attrs) escapeAllHTML(w, node.Literal) r.out(w, codeCloseTag) r.out(w, preCloseTag) if node.Parent.Type != Item { r.cr(w) } case Table: if entering { r.cr(w) r.out(w, tableTag) } else { r.out(w, tableCloseTag) r.cr(w) } case TableCell: openTag := tdTag closeTag := tdCloseTag if node.IsHeader { openTag = thTag closeTag = thCloseTag } if entering { align := cellAlignment(node.Align) if align != "" { attrs = append(attrs, fmt.Sprintf(`align="%s"`, align)) } if node.Prev == nil { r.cr(w) } r.tag(w, openTag, attrs) } else { r.out(w, closeTag) r.cr(w) } case TableHead: if entering { r.cr(w) r.out(w, theadTag) } else { r.out(w, theadCloseTag) r.cr(w) } case TableBody: if entering { r.cr(w) r.out(w, tbodyTag) // XXX: this is to adhere to a rather silly test. Should fix test. if node.FirstChild == nil { r.cr(w) } } else { r.out(w, tbodyCloseTag) r.cr(w) } case TableRow: if entering { r.cr(w) r.out(w, trTag) } else { r.out(w, trCloseTag) r.cr(w) } default: panic("Unknown node type " + node.Type.String()) } return GoToNext } // RenderHeader writes HTML document preamble and TOC if requested. func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) { r.writeDocumentHeader(w) if r.Flags&TOC != 0 { r.writeTOC(w, ast) } } // RenderFooter writes HTML document footer. func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) { if r.Flags&CompletePage == 0 { return } io.WriteString(w, "\n\n\n") } func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) { if r.Flags&CompletePage == 0 { return } ending := "" if r.Flags&UseXHTML != 0 { io.WriteString(w, "\n") io.WriteString(w, "\n") ending = " /" } else { io.WriteString(w, "\n") io.WriteString(w, "\n") } io.WriteString(w, "\n") io.WriteString(w, " ") if r.Flags&Smartypants != 0 { r.sr.Process(w, []byte(r.Title)) } else { escapeHTML(w, []byte(r.Title)) } io.WriteString(w, "\n") io.WriteString(w, " \n") io.WriteString(w, " \n") if r.CSS != "" { io.WriteString(w, " \n") } if r.Icon != "" { io.WriteString(w, " \n") } io.WriteString(w, "\n") io.WriteString(w, "\n\n") } func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) { buf := bytes.Buffer{} inHeading := false tocLevel := 0 headingCount := 0 ast.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Heading && !node.HeadingData.IsTitleblock { inHeading = entering if entering { node.HeadingID = fmt.Sprintf("toc_%d", headingCount) if node.Level == tocLevel { buf.WriteString("\n\n
  • ") } else if node.Level < tocLevel { for node.Level < tocLevel { tocLevel-- buf.WriteString("
  • \n") } buf.WriteString("\n\n
  • ") } else { for node.Level > tocLevel { tocLevel++ buf.WriteString("\n") } if buf.Len() > 0 { io.WriteString(w, "\n") } r.lastOutputLen = buf.Len() } blackfriday-2.1.0/inline.go000066400000000000000000000623401374571415200156100ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Functions to parse inline elements. // package blackfriday import ( "bytes" "regexp" "strconv" ) var ( urlRe = `((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+` anchorRe = regexp.MustCompile(`^(]+")?\s?>` + urlRe + `<\/a>)`) // https://www.w3.org/TR/html5/syntax.html#character-references // highest unicode code point in 17 planes (2^20): 1,114,112d = // 7 dec digits or 6 hex digits // named entity references can be 2-31 characters with stuff like < // at one end and ∳ at the other. There // are also sometimes numbers at the end, although this isn't inherent // in the specification; there are never numbers anywhere else in // current character references, though; see ¾ and ▒, etc. // https://www.w3.org/TR/html5/syntax.html#named-character-references // // entity := "&" (named group | number ref) ";" // named group := [a-zA-Z]{2,31}[0-9]{0,2} // number ref := "#" (dec ref | hex ref) // dec ref := [0-9]{1,7} // hex ref := ("x" | "X") [0-9a-fA-F]{1,6} htmlEntityRe = regexp.MustCompile(`&([a-zA-Z]{2,31}[0-9]{0,2}|#([0-9]{1,7}|[xX][0-9a-fA-F]{1,6}));`) ) // Functions to parse text within a block // Each function returns the number of chars taken care of // data is the complete block being rendered // offset is the number of valid chars before the current cursor func (p *Markdown) inline(currBlock *Node, data []byte) { // handlers might call us recursively: enforce a maximum depth if p.nesting >= p.maxNesting || len(data) == 0 { return } p.nesting++ beg, end := 0, 0 for end < len(data) { handler := p.inlineCallback[data[end]] if handler != nil { if consumed, node := handler(p, data, end); consumed == 0 { // No action from the callback. end++ } else { // Copy inactive chars into the output. currBlock.AppendChild(text(data[beg:end])) if node != nil { currBlock.AppendChild(node) } // Skip past whatever the callback used. beg = end + consumed end = beg } } else { end++ } } if beg < len(data) { if data[end-1] == '\n' { end-- } currBlock.AppendChild(text(data[beg:end])) } p.nesting-- } // single and double emphasis parsing func emphasis(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] c := data[0] if len(data) > 2 && data[1] != c { // whitespace cannot follow an opening emphasis; // strikethrough only takes two characters '~~' if c == '~' || isspace(data[1]) { return 0, nil } ret, node := helperEmphasis(p, data[1:], c) if ret == 0 { return 0, nil } return ret + 1, node } if len(data) > 3 && data[1] == c && data[2] != c { if isspace(data[2]) { return 0, nil } ret, node := helperDoubleEmphasis(p, data[2:], c) if ret == 0 { return 0, nil } return ret + 2, node } if len(data) > 4 && data[1] == c && data[2] == c && data[3] != c { if c == '~' || isspace(data[3]) { return 0, nil } ret, node := helperTripleEmphasis(p, data, 3, c) if ret == 0 { return 0, nil } return ret + 3, node } return 0, nil } func codeSpan(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] nb := 0 // count the number of backticks in the delimiter for nb < len(data) && data[nb] == '`' { nb++ } // find the next delimiter i, end := 0, 0 for end = nb; end < len(data) && i < nb; end++ { if data[end] == '`' { i++ } else { i = 0 } } // no matching delimiter? if i < nb && end >= len(data) { return 0, nil } // trim outside whitespace fBegin := nb for fBegin < end && data[fBegin] == ' ' { fBegin++ } fEnd := end - nb for fEnd > fBegin && data[fEnd-1] == ' ' { fEnd-- } // render the code span if fBegin != fEnd { code := NewNode(Code) code.Literal = data[fBegin:fEnd] return end, code } return end, nil } // newline preceded by two spaces becomes
    func maybeLineBreak(p *Markdown, data []byte, offset int) (int, *Node) { origOffset := offset for offset < len(data) && data[offset] == ' ' { offset++ } if offset < len(data) && data[offset] == '\n' { if offset-origOffset >= 2 { return offset - origOffset + 1, NewNode(Hardbreak) } return offset - origOffset, nil } return 0, nil } // newline without two spaces works when HardLineBreak is enabled func lineBreak(p *Markdown, data []byte, offset int) (int, *Node) { if p.extensions&HardLineBreak != 0 { return 1, NewNode(Hardbreak) } return 0, nil } type linkType int const ( linkNormal linkType = iota linkImg linkDeferredFootnote linkInlineFootnote ) func isReferenceStyleLink(data []byte, pos int, t linkType) bool { if t == linkDeferredFootnote { return false } return pos < len(data)-1 && data[pos] == '[' && data[pos+1] != '^' } func maybeImage(p *Markdown, data []byte, offset int) (int, *Node) { if offset < len(data)-1 && data[offset+1] == '[' { return link(p, data, offset) } return 0, nil } func maybeInlineFootnote(p *Markdown, data []byte, offset int) (int, *Node) { if offset < len(data)-1 && data[offset+1] == '[' { return link(p, data, offset) } return 0, nil } // '[': parse a link or an image or a footnote func link(p *Markdown, data []byte, offset int) (int, *Node) { // no links allowed inside regular links, footnote, and deferred footnotes if p.insideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') { return 0, nil } var t linkType switch { // special case: ![^text] == deferred footnote (that follows something with // an exclamation point) case p.extensions&Footnotes != 0 && len(data)-1 > offset && data[offset+1] == '^': t = linkDeferredFootnote // ![alt] == image case offset >= 0 && data[offset] == '!': t = linkImg offset++ // ^[text] == inline footnote // [^refId] == deferred footnote case p.extensions&Footnotes != 0: if offset >= 0 && data[offset] == '^' { t = linkInlineFootnote offset++ } else if len(data)-1 > offset && data[offset+1] == '^' { t = linkDeferredFootnote } // [text] == regular link default: t = linkNormal } data = data[offset:] var ( i = 1 noteID int title, link, altContent []byte textHasNl = false ) if t == linkDeferredFootnote { i++ } // look for the matching closing bracket for level := 1; level > 0 && i < len(data); i++ { switch { case data[i] == '\n': textHasNl = true case isBackslashEscaped(data, i): continue case data[i] == '[': level++ case data[i] == ']': level-- if level <= 0 { i-- // compensate for extra i++ in for loop } } } if i >= len(data) { return 0, nil } txtE := i i++ var footnoteNode *Node // skip any amount of whitespace or newline // (this is much more lax than original markdown syntax) for i < len(data) && isspace(data[i]) { i++ } // inline style link switch { case i < len(data) && data[i] == '(': // skip initial whitespace i++ for i < len(data) && isspace(data[i]) { i++ } linkB := i // look for link end: ' " ) findlinkend: for i < len(data) { switch { case data[i] == '\\': i += 2 case data[i] == ')' || data[i] == '\'' || data[i] == '"': break findlinkend default: i++ } } if i >= len(data) { return 0, nil } linkE := i // look for title end if present titleB, titleE := 0, 0 if data[i] == '\'' || data[i] == '"' { i++ titleB = i findtitleend: for i < len(data) { switch { case data[i] == '\\': i += 2 case data[i] == ')': break findtitleend default: i++ } } if i >= len(data) { return 0, nil } // skip whitespace after title titleE = i - 1 for titleE > titleB && isspace(data[titleE]) { titleE-- } // check for closing quote presence if data[titleE] != '\'' && data[titleE] != '"' { titleB, titleE = 0, 0 linkE = i } } // remove whitespace at the end of the link for linkE > linkB && isspace(data[linkE-1]) { linkE-- } // remove optional angle brackets around the link if data[linkB] == '<' { linkB++ } if data[linkE-1] == '>' { linkE-- } // build escaped link and title if linkE > linkB { link = data[linkB:linkE] } if titleE > titleB { title = data[titleB:titleE] } i++ // reference style link case isReferenceStyleLink(data, i, t): var id []byte altContentConsidered := false // look for the id i++ linkB := i for i < len(data) && data[i] != ']' { i++ } if i >= len(data) { return 0, nil } linkE := i // find the reference if linkB == linkE { if textHasNl { var b bytes.Buffer for j := 1; j < txtE; j++ { switch { case data[j] != '\n': b.WriteByte(data[j]) case data[j-1] != ' ': b.WriteByte(' ') } } id = b.Bytes() } else { id = data[1:txtE] altContentConsidered = true } } else { id = data[linkB:linkE] } // find the reference with matching id lr, ok := p.getRef(string(id)) if !ok { return 0, nil } // keep link and title from reference link = lr.link title = lr.title if altContentConsidered { altContent = lr.text } i++ // shortcut reference style link or reference or inline footnote default: var id []byte // craft the id if textHasNl { var b bytes.Buffer for j := 1; j < txtE; j++ { switch { case data[j] != '\n': b.WriteByte(data[j]) case data[j-1] != ' ': b.WriteByte(' ') } } id = b.Bytes() } else { if t == linkDeferredFootnote { id = data[2:txtE] // get rid of the ^ } else { id = data[1:txtE] } } footnoteNode = NewNode(Item) if t == linkInlineFootnote { // create a new reference noteID = len(p.notes) + 1 var fragment []byte if len(id) > 0 { if len(id) < 16 { fragment = make([]byte, len(id)) } else { fragment = make([]byte, 16) } copy(fragment, slugify(id)) } else { fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteID))...) } ref := &reference{ noteID: noteID, hasBlock: false, link: fragment, title: id, footnote: footnoteNode, } p.notes = append(p.notes, ref) link = ref.link title = ref.title } else { // find the reference with matching id lr, ok := p.getRef(string(id)) if !ok { return 0, nil } if t == linkDeferredFootnote { lr.noteID = len(p.notes) + 1 lr.footnote = footnoteNode p.notes = append(p.notes, lr) } // keep link and title from reference link = lr.link // if inline footnote, title == footnote contents title = lr.title noteID = lr.noteID } // rewind the whitespace i = txtE + 1 } var uLink []byte if t == linkNormal || t == linkImg { if len(link) > 0 { var uLinkBuf bytes.Buffer unescapeText(&uLinkBuf, link) uLink = uLinkBuf.Bytes() } // links need something to click on and somewhere to go if len(uLink) == 0 || (t == linkNormal && txtE <= 1) { return 0, nil } } // call the relevant rendering function var linkNode *Node switch t { case linkNormal: linkNode = NewNode(Link) linkNode.Destination = normalizeURI(uLink) linkNode.Title = title if len(altContent) > 0 { linkNode.AppendChild(text(altContent)) } else { // links cannot contain other links, so turn off link parsing // temporarily and recurse insideLink := p.insideLink p.insideLink = true p.inline(linkNode, data[1:txtE]) p.insideLink = insideLink } case linkImg: linkNode = NewNode(Image) linkNode.Destination = uLink linkNode.Title = title linkNode.AppendChild(text(data[1:txtE])) i++ case linkInlineFootnote, linkDeferredFootnote: linkNode = NewNode(Link) linkNode.Destination = link linkNode.Title = title linkNode.NoteID = noteID linkNode.Footnote = footnoteNode if t == linkInlineFootnote { i++ } default: return 0, nil } return i, linkNode } func (p *Markdown) inlineHTMLComment(data []byte) int { if len(data) < 5 { return 0 } if data[0] != '<' || data[1] != '!' || data[2] != '-' || data[3] != '-' { return 0 } i := 5 // scan for an end-of-comment marker, across lines if necessary for i < len(data) && !(data[i-2] == '-' && data[i-1] == '-' && data[i] == '>') { i++ } // no end-of-comment marker if i >= len(data) { return 0 } return i + 1 } func stripMailto(link []byte) []byte { if bytes.HasPrefix(link, []byte("mailto://")) { return link[9:] } else if bytes.HasPrefix(link, []byte("mailto:")) { return link[7:] } else { return link } } // autolinkType specifies a kind of autolink that gets detected. type autolinkType int // These are the possible flag values for the autolink renderer. const ( notAutolink autolinkType = iota normalAutolink emailAutolink ) // '<' when tags or autolinks are allowed func leftAngle(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] altype, end := tagLength(data) if size := p.inlineHTMLComment(data); size > 0 { end = size } if end > 2 { if altype != notAutolink { var uLink bytes.Buffer unescapeText(&uLink, data[1:end+1-2]) if uLink.Len() > 0 { link := uLink.Bytes() node := NewNode(Link) node.Destination = link if altype == emailAutolink { node.Destination = append([]byte("mailto:"), link...) } node.AppendChild(text(stripMailto(link))) return end, node } } else { htmlTag := NewNode(HTMLSpan) htmlTag.Literal = data[:end] return end, htmlTag } } return end, nil } // '\\' backslash escape var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~") func escape(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] if len(data) > 1 { if p.extensions&BackslashLineBreak != 0 && data[1] == '\n' { return 2, NewNode(Hardbreak) } if bytes.IndexByte(escapeChars, data[1]) < 0 { return 0, nil } return 2, text(data[1:2]) } return 2, nil } func unescapeText(ob *bytes.Buffer, src []byte) { i := 0 for i < len(src) { org := i for i < len(src) && src[i] != '\\' { i++ } if i > org { ob.Write(src[org:i]) } if i+1 >= len(src) { break } ob.WriteByte(src[i+1]) i += 2 } } // '&' escaped when it doesn't belong to an entity // valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; func entity(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] end := 1 if end < len(data) && data[end] == '#' { end++ } for end < len(data) && isalnum(data[end]) { end++ } if end < len(data) && data[end] == ';' { end++ // real entity } else { return 0, nil // lone '&' } ent := data[:end] // undo & escaping or it will be converted to &amp; by another // escaper in the renderer if bytes.Equal(ent, []byte("&")) { ent = []byte{'&'} } return end, text(ent) } func linkEndsWithEntity(data []byte, linkEnd int) bool { entityRanges := htmlEntityRe.FindAllIndex(data[:linkEnd], -1) return entityRanges != nil && entityRanges[len(entityRanges)-1][1] == linkEnd } // hasPrefixCaseInsensitive is a custom implementation of // strings.HasPrefix(strings.ToLower(s), prefix) // we rolled our own because ToLower pulls in a huge machinery of lowercasing // anything from Unicode and that's very slow. Since this func will only be // used on ASCII protocol prefixes, we can take shortcuts. func hasPrefixCaseInsensitive(s, prefix []byte) bool { if len(s) < len(prefix) { return false } delta := byte('a' - 'A') for i, b := range prefix { if b != s[i] && b != s[i]+delta { return false } } return true } var protocolPrefixes = [][]byte{ []byte("http://"), []byte("https://"), []byte("ftp://"), []byte("file://"), []byte("mailto:"), } const shortestPrefix = 6 // len("ftp://"), the shortest of the above func maybeAutoLink(p *Markdown, data []byte, offset int) (int, *Node) { // quick check to rule out most false hits if p.insideLink || len(data) < offset+shortestPrefix { return 0, nil } for _, prefix := range protocolPrefixes { endOfHead := offset + 8 // 8 is the len() of the longest prefix if endOfHead > len(data) { endOfHead = len(data) } if hasPrefixCaseInsensitive(data[offset:endOfHead], prefix) { return autoLink(p, data, offset) } } return 0, nil } func autoLink(p *Markdown, data []byte, offset int) (int, *Node) { // Now a more expensive check to see if we're not inside an anchor element anchorStart := offset offsetFromAnchor := 0 for anchorStart > 0 && data[anchorStart] != '<' { anchorStart-- offsetFromAnchor++ } anchorStr := anchorRe.Find(data[anchorStart:]) if anchorStr != nil { anchorClose := NewNode(HTMLSpan) anchorClose.Literal = anchorStr[offsetFromAnchor:] return len(anchorStr) - offsetFromAnchor, anchorClose } // scan backward for a word boundary rewind := 0 for offset-rewind > 0 && rewind <= 7 && isletter(data[offset-rewind-1]) { rewind++ } if rewind > 6 { // longest supported protocol is "mailto" which has 6 letters return 0, nil } origData := data data = data[offset-rewind:] if !isSafeLink(data) { return 0, nil } linkEnd := 0 for linkEnd < len(data) && !isEndOfLink(data[linkEnd]) { linkEnd++ } // Skip punctuation at the end of the link if (data[linkEnd-1] == '.' || data[linkEnd-1] == ',') && data[linkEnd-2] != '\\' { linkEnd-- } // But don't skip semicolon if it's a part of escaped entity: if data[linkEnd-1] == ';' && data[linkEnd-2] != '\\' && !linkEndsWithEntity(data, linkEnd) { linkEnd-- } // See if the link finishes with a punctuation sign that can be closed. var copen byte switch data[linkEnd-1] { case '"': copen = '"' case '\'': copen = '\'' case ')': copen = '(' case ']': copen = '[' case '}': copen = '{' default: copen = 0 } if copen != 0 { bufEnd := offset - rewind + linkEnd - 2 openDelim := 1 /* Try to close the final punctuation sign in this same line; * if we managed to close it outside of the URL, that means that it's * not part of the URL. If it closes inside the URL, that means it * is part of the URL. * * Examples: * * foo http://www.pokemon.com/Pikachu_(Electric) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo (http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric)) * * (foo http://www.pokemon.com/Pikachu_(Electric)) bar * => foo http://www.pokemon.com/Pikachu_(Electric) */ for bufEnd >= 0 && origData[bufEnd] != '\n' && openDelim != 0 { if origData[bufEnd] == data[linkEnd-1] { openDelim++ } if origData[bufEnd] == copen { openDelim-- } bufEnd-- } if openDelim == 0 { linkEnd-- } } var uLink bytes.Buffer unescapeText(&uLink, data[:linkEnd]) if uLink.Len() > 0 { node := NewNode(Link) node.Destination = uLink.Bytes() node.AppendChild(text(uLink.Bytes())) return linkEnd, node } return linkEnd, nil } func isEndOfLink(char byte) bool { return isspace(char) || char == '<' } var validUris = [][]byte{[]byte("http://"), []byte("https://"), []byte("ftp://"), []byte("mailto://")} var validPaths = [][]byte{[]byte("/"), []byte("./"), []byte("../")} func isSafeLink(link []byte) bool { for _, path := range validPaths { if len(link) >= len(path) && bytes.Equal(link[:len(path)], path) { if len(link) == len(path) { return true } else if isalnum(link[len(path)]) { return true } } } for _, prefix := range validUris { // TODO: handle unicode here // case-insensitive prefix test if len(link) > len(prefix) && bytes.Equal(bytes.ToLower(link[:len(prefix)]), prefix) && isalnum(link[len(prefix)]) { return true } } return false } // return the length of the given tag, or 0 is it's not valid func tagLength(data []byte) (autolink autolinkType, end int) { var i, j int // a valid tag can't be shorter than 3 chars if len(data) < 3 { return notAutolink, 0 } // begins with a '<' optionally followed by '/', followed by letter or number if data[0] != '<' { return notAutolink, 0 } if data[1] == '/' { i = 2 } else { i = 1 } if !isalnum(data[i]) { return notAutolink, 0 } // scheme test autolink = notAutolink // try to find the beginning of an URI for i < len(data) && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-') { i++ } if i > 1 && i < len(data) && data[i] == '@' { if j = isMailtoAutoLink(data[i:]); j != 0 { return emailAutolink, i + j } } if i > 2 && i < len(data) && data[i] == ':' { autolink = normalAutolink i++ } // complete autolink test: no whitespace or ' or " switch { case i >= len(data): autolink = notAutolink case autolink != notAutolink: j = i for i < len(data) { if data[i] == '\\' { i += 2 } else if data[i] == '>' || data[i] == '\'' || data[i] == '"' || isspace(data[i]) { break } else { i++ } } if i >= len(data) { return autolink, 0 } if i > j && data[i] == '>' { return autolink, i + 1 } // one of the forbidden chars has been found autolink = notAutolink } i += bytes.IndexByte(data[i:], '>') if i < 0 { return autolink, 0 } return autolink, i + 1 } // look for the address part of a mail autolink and '>' // this is less strict than the original markdown e-mail address matching func isMailtoAutoLink(data []byte) int { nb := 0 // address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' for i := 0; i < len(data); i++ { if isalnum(data[i]) { continue } switch data[i] { case '@': nb++ case '-', '.', '_': break case '>': if nb == 1 { return i + 1 } return 0 default: return 0 } } return 0 } // look for the next emph char, skipping other constructs func helperFindEmphChar(data []byte, c byte) int { i := 0 for i < len(data) { for i < len(data) && data[i] != c && data[i] != '`' && data[i] != '[' { i++ } if i >= len(data) { return 0 } // do not count escaped chars if i != 0 && data[i-1] == '\\' { i++ continue } if data[i] == c { return i } if data[i] == '`' { // skip a code span tmpI := 0 i++ for i < len(data) && data[i] != '`' { if tmpI == 0 && data[i] == c { tmpI = i } i++ } if i >= len(data) { return tmpI } i++ } else if data[i] == '[' { // skip a link tmpI := 0 i++ for i < len(data) && data[i] != ']' { if tmpI == 0 && data[i] == c { tmpI = i } i++ } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\n') { i++ } if i >= len(data) { return tmpI } if data[i] != '[' && data[i] != '(' { // not a link if tmpI > 0 { return tmpI } continue } cc := data[i] i++ for i < len(data) && data[i] != cc { if tmpI == 0 && data[i] == c { return i } i++ } if i >= len(data) { return tmpI } i++ } } return 0 } func helperEmphasis(p *Markdown, data []byte, c byte) (int, *Node) { i := 0 // skip one symbol if coming from emph3 if len(data) > 1 && data[0] == c && data[1] == c { i = 1 } for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length if i >= len(data) { return 0, nil } if i+1 < len(data) && data[i+1] == c { i++ continue } if data[i] == c && !isspace(data[i-1]) { if p.extensions&NoIntraEmphasis != 0 { if !(i+1 == len(data) || isspace(data[i+1]) || ispunct(data[i+1])) { continue } } emph := NewNode(Emph) p.inline(emph, data[:i]) return i + 1, emph } } return 0, nil } func helperDoubleEmphasis(p *Markdown, data []byte, c byte) (int, *Node) { i := 0 for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { nodeType := Strong if c == '~' { nodeType = Del } node := NewNode(nodeType) p.inline(node, data[:i]) return i + 2, node } i++ } return 0, nil } func helperTripleEmphasis(p *Markdown, data []byte, offset int, c byte) (int, *Node) { i := 0 origData := data data = data[offset:] for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length // skip whitespace preceded symbols if data[i] != c || isspace(data[i-1]) { continue } switch { case i+2 < len(data) && data[i+1] == c && data[i+2] == c: // triple symbol found strong := NewNode(Strong) em := NewNode(Emph) strong.AppendChild(em) p.inline(em, data[:i]) return i + 3, strong case (i+1 < len(data) && data[i+1] == c): // double symbol found, hand over to emph1 length, node := helperEmphasis(p, origData[offset-2:], c) if length == 0 { return 0, nil } return length - 2, node default: // single symbol found, hand over to emph2 length, node := helperDoubleEmphasis(p, origData[offset-1:], c) if length == 0 { return 0, nil } return length - 1, node } } return 0, nil } func text(s []byte) *Node { node := NewNode(Text) node.Literal = s return node } func normalizeURI(s []byte) []byte { return s // TODO: implement } blackfriday-2.1.0/inline_test.go000066400000000000000000001007341374571415200166470ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Unit tests for inline parsing // package blackfriday import ( "regexp" "testing" "strings" ) func TestEmphasis(t *testing.T) { t.Parallel() var tests = []string{ "nothing inline\n", "

    nothing inline

    \n", "simple *inline* test\n", "

    simple inline test

    \n", "*at the* beginning\n", "

    at the beginning

    \n", "at the *end*\n", "

    at the end

    \n", "*try two* in *one line*\n", "

    try two in one line

    \n", "over *two\nlines* test\n", "

    over two\nlines test

    \n", "odd *number of* markers* here\n", "

    odd number of markers* here

    \n", "odd *number\nof* markers* here\n", "

    odd number\nof markers* here

    \n", "simple _inline_ test\n", "

    simple inline test

    \n", "_at the_ beginning\n", "

    at the beginning

    \n", "at the _end_\n", "

    at the end

    \n", "_try two_ in _one line_\n", "

    try two in one line

    \n", "over _two\nlines_ test\n", "

    over two\nlines test

    \n", "odd _number of_ markers_ here\n", "

    odd number of markers_ here

    \n", "odd _number\nof_ markers_ here\n", "

    odd number\nof markers_ here

    \n", "mix of *markers_\n", "

    mix of *markers_

    \n", "*What is A\\* algorithm?*\n", "

    What is A* algorithm?

    \n", } doTestsInline(t, tests) } func TestReferenceOverride(t *testing.T) { t.Parallel() var tests = []string{ "test [ref1][]\n", "

    test ref1

    \n", "test [my ref][ref1]\n", "

    test my ref

    \n", "test [ref2][]\n\n[ref2]: http://www.leftalone.com/ (Ref left alone)\n", "

    test ref2

    \n", "test [ref3][]\n\n[ref3]: http://www.leftalone.com/ (Ref left alone)\n", "

    test ref3

    \n", "test [ref4][]\n\n[ref4]: http://zombo.com/ (You can do anything)\n", "

    test [ref4][]

    \n", "test [!(*http.ServeMux).ServeHTTP][] complicated ref\n", "

    test !(*http.ServeMux).ServeHTTP complicated ref

    \n", "test [ref5][]\n", "

    test Moo

    \n", } doTestsInlineParam(t, tests, TestParams{ referenceOverride: func(reference string) (rv *Reference, overridden bool) { switch reference { case "ref1": // just an overridden reference exists without definition return &Reference{ Link: "http://www.ref1.com/", Title: "Reference 1"}, true case "ref2": // overridden exists and reference defined return &Reference{ Link: "http://www.overridden.com/", Title: "Reference Overridden"}, true case "ref3": // not overridden and reference defined return nil, false case "ref4": // overridden missing and defined return nil, true case "!(*http.ServeMux).ServeHTTP": return &Reference{ Link: "http://localhost:6060/pkg/net/http/#ServeMux.ServeHTTP", Title: "ServeHTTP docs"}, true case "ref5": return &Reference{ Link: "http://www.ref5.com/", Title: "Reference 5", Text: "Moo", }, true } return nil, false }, }) } func TestStrong(t *testing.T) { t.Parallel() var tests = []string{ "nothing inline\n", "

    nothing inline

    \n", "simple **inline** test\n", "

    simple inline test

    \n", "**at the** beginning\n", "

    at the beginning

    \n", "at the **end**\n", "

    at the end

    \n", "**try two** in **one line**\n", "

    try two in one line

    \n", "over **two\nlines** test\n", "

    over two\nlines test

    \n", "odd **number of** markers** here\n", "

    odd number of markers** here

    \n", "odd **number\nof** markers** here\n", "

    odd number\nof markers** here

    \n", "simple __inline__ test\n", "

    simple inline test

    \n", "__at the__ beginning\n", "

    at the beginning

    \n", "at the __end__\n", "

    at the end

    \n", "__try two__ in __one line__\n", "

    try two in one line

    \n", "over __two\nlines__ test\n", "

    over two\nlines test

    \n", "odd __number of__ markers__ here\n", "

    odd number of markers__ here

    \n", "odd __number\nof__ markers__ here\n", "

    odd number\nof markers__ here

    \n", "mix of **markers__\n", "

    mix of **markers__

    \n", "**`/usr`** : this folder is named `usr`\n", "

    /usr : this folder is named usr

    \n", "**`/usr`** :\n\n this folder is named `usr`\n", "

    /usr :

    \n\n

    this folder is named usr

    \n", } doTestsInline(t, tests) } func TestEmphasisMix(t *testing.T) { t.Parallel() var tests = []string{ "***triple emphasis***\n", "

    triple emphasis

    \n", "***triple\nemphasis***\n", "

    triple\nemphasis

    \n", "___triple emphasis___\n", "

    triple emphasis

    \n", "***triple emphasis___\n", "

    ***triple emphasis___

    \n", "*__triple emphasis__*\n", "

    triple emphasis

    \n", "__*triple emphasis*__\n", "

    triple emphasis

    \n", "**improper *nesting** is* bad\n", "

    improper *nesting is* bad

    \n", "*improper **nesting* is** bad\n", "

    *improper nesting* is bad

    \n", } doTestsInline(t, tests) } func TestEmphasisLink(t *testing.T) { t.Parallel() var tests = []string{ "[first](before) *text[second] (inside)text* [third](after)\n", "

    first textsecondtext third

    \n", "*incomplete [link] definition*\n", "

    incomplete [link] definition

    \n", "*it's [emphasis*] (not link)\n", "

    it's [emphasis] (not link)

    \n", "*it's [emphasis*] and *[asterisk]\n", "

    it's [emphasis] and *[asterisk]

    \n", } doTestsInline(t, tests) } func TestStrikeThrough(t *testing.T) { t.Parallel() var tests = []string{ "nothing inline\n", "

    nothing inline

    \n", "simple ~~inline~~ test\n", "

    simple inline test

    \n", "~~at the~~ beginning\n", "

    at the beginning

    \n", "at the ~~end~~\n", "

    at the end

    \n", "~~try two~~ in ~~one line~~\n", "

    try two in one line

    \n", "over ~~two\nlines~~ test\n", "

    over two\nlines test

    \n", "odd ~~number of~~ markers~~ here\n", "

    odd number of markers~~ here

    \n", "odd ~~number\nof~~ markers~~ here\n", "

    odd number\nof markers~~ here

    \n", } doTestsInline(t, tests) } func TestCodeSpan(t *testing.T) { t.Parallel() var tests = []string{ "`source code`\n", "

    source code

    \n", "` source code with spaces `\n", "

    source code with spaces

    \n", "` source code with spaces `not here\n", "

    source code with spacesnot here

    \n", "a `single marker\n", "

    a `single marker

    \n", "a single multi-tick marker with ``` no text\n", "

    a single multi-tick marker with ``` no text

    \n", "markers with ` ` a space\n", "

    markers with a space

    \n", "`source code` and a `stray\n", "

    source code and a `stray

    \n", "`source *with* _awkward characters_ in it`\n", "

    source *with* _awkward characters_ in it

    \n", "`split over\ntwo lines`\n", "

    split over\ntwo lines

    \n", "```multiple ticks``` for the marker\n", "

    multiple ticks for the marker

    \n", "```multiple ticks `with` ticks inside```\n", "

    multiple ticks `with` ticks inside

    \n", } doTestsInline(t, tests) } func TestLineBreak(t *testing.T) { t.Parallel() var tests = []string{ "this line \nhas a break\n", "

    this line
    \nhas a break

    \n", "this line \ndoes not\n", "

    this line\ndoes not

    \n", "this line\\\ndoes not\n", "

    this line\\\ndoes not

    \n", "this line\\ \ndoes not\n", "

    this line\\\ndoes not

    \n", "this has an \nextra space\n", "

    this has an
    \nextra space

    \n", } doTestsInline(t, tests) tests = []string{ "this line \nhas a break\n", "

    this line
    \nhas a break

    \n", "this line \ndoes not\n", "

    this line\ndoes not

    \n", "this line\\\nhas a break\n", "

    this line
    \nhas a break

    \n", "this line\\ \ndoes not\n", "

    this line\\\ndoes not

    \n", "this has an \nextra space\n", "

    this has an
    \nextra space

    \n", } doTestsInlineParam(t, tests, TestParams{ extensions: BackslashLineBreak}) } func TestInlineLink(t *testing.T) { t.Parallel() var tests = []string{ "[foo](/bar/)\n", "

    foo

    \n", "[foo with a title](/bar/ \"title\")\n", "

    foo with a title

    \n", "[foo with a title](/bar/\t\"title\")\n", "

    foo with a title

    \n", "[foo with a title](/bar/ \"title\" )\n", "

    foo with a title

    \n", "[foo with a title](/bar/ title with no quotes)\n", "

    foo with a title

    \n", "[foo]()\n", "

    [foo]()

    \n", "![foo](/bar/)\n", "

    \"foo\"

    \n", "![foo with a title](/bar/ \"title\")\n", "

    \"foo

    \n", "![foo with a title](/bar/\t\"title\")\n", "

    \"foo

    \n", "![foo with a title](/bar/ \"title\" )\n", "

    \"foo

    \n", "![foo with a title](/bar/ title with no quotes)\n", "

    \"foo

    \n", "![](img.jpg)\n", "

    \"\"

    \n", "[link](url)\n", "

    link

    \n", "![foo]()\n", "

    ![foo]()

    \n", "[a link]\t(/with_a_tab/)\n", "

    a link

    \n", "[a link] (/with_spaces/)\n", "

    a link

    \n", "[text (with) [[nested] (brackets)]](/url/)\n", "

    text (with) [[nested] (brackets)]

    \n", "[text (with) [broken nested] (brackets)]](/url/)\n", "

    [text (with) broken nested]](/url/)

    \n", "[text\nwith a newline](/link/)\n", "

    text\nwith a newline

    \n", "[text in brackets] [followed](/by a link/)\n", "

    [text in brackets] followed

    \n", "[link with\\] a closing bracket](/url/)\n", "

    link with] a closing bracket

    \n", "[link with\\[ an opening bracket](/url/)\n", "

    link with[ an opening bracket

    \n", "[link with\\) a closing paren](/url/)\n", "

    link with) a closing paren

    \n", "[link with\\( an opening paren](/url/)\n", "

    link with( an opening paren

    \n", "[link]( with whitespace)\n", "

    link

    \n", "[link]( with whitespace )\n", "

    link

    \n", "[![image](someimage)](with image)\n", "

    \"image\"

    \n", "[link](url \"one quote)\n", "

    link

    \n", "[link](url 'one quote)\n", "

    link

    \n", "[link]()\n", "

    link

    \n", "[link & ampersand](/url/)\n", "

    link & ampersand

    \n", "[link & ampersand](/url/)\n", "

    link & ampersand

    \n", "[link](/url/&query)\n", "

    link

    \n", "[[t]](/t)\n", "

    [t]

    \n", "[link]()\n", "

    link

    \n", "[link](<./>)\n", "

    link

    \n", "[link](<../>)\n", "

    link

    \n", } doLinkTestsInline(t, tests) } func TestRelAttrLink(t *testing.T) { t.Parallel() var nofollowTests = []string{ "[foo](http://bar.com/foo/)\n", "

    foo

    \n", "[foo](/bar/)\n", "

    foo

    \n", "[foo](/)\n", "

    foo

    \n", "[foo](./)\n", "

    foo

    \n", "[foo](../)\n", "

    foo

    \n", "[foo](../bar)\n", "

    foo

    \n", } doTestsInlineParam(t, nofollowTests, TestParams{ HTMLFlags: Safelink | NofollowLinks, }) var noreferrerTests = []string{ "[foo](http://bar.com/foo/)\n", "

    foo

    \n", "[foo](/bar/)\n", "

    foo

    \n", } doTestsInlineParam(t, noreferrerTests, TestParams{ HTMLFlags: Safelink | NoreferrerLinks, }) var noopenerTests = []string{ "[foo](http://bar.com/foo/)\n", "

    foo

    \n", "[foo](/bar/)\n", "

    foo

    \n", } doTestsInlineParam(t, noopenerTests, TestParams{ HTMLFlags: Safelink | NoopenerLinks, }) var nofollownoreferrernoopenerTests = []string{ "[foo](http://bar.com/foo/)\n", "

    foo

    \n", "[foo](/bar/)\n", "

    foo

    \n", } doTestsInlineParam(t, nofollownoreferrernoopenerTests, TestParams{ HTMLFlags: Safelink | NofollowLinks | NoreferrerLinks | NoopenerLinks, }) } func TestHrefTargetBlank(t *testing.T) { t.Parallel() var tests = []string{ // internal link "[foo](/bar/)\n", "

    foo

    \n", "[foo](/)\n", "

    foo

    \n", "[foo](./)\n", "

    foo

    \n", "[foo](./bar)\n", "

    foo

    \n", "[foo](../)\n", "

    foo

    \n", "[foo](../bar)\n", "

    foo

    \n", "[foo](http://example.com)\n", "

    foo

    \n", } doTestsInlineParam(t, tests, TestParams{ HTMLFlags: Safelink | HrefTargetBlank, }) } func TestSafeInlineLink(t *testing.T) { t.Parallel() var tests = []string{ "[foo](/bar/)\n", "

    foo

    \n", "[foo](/)\n", "

    foo

    \n", "[foo](./)\n", "

    foo

    \n", "[foo](../)\n", "

    foo

    \n", "[foo](http://bar/)\n", "

    foo

    \n", "[foo](https://bar/)\n", "

    foo

    \n", "[foo](ftp://bar/)\n", "

    foo

    \n", "[foo](mailto://bar/)\n", "

    foo

    \n", // Not considered safe "[foo](baz://bar/)\n", "

    foo

    \n", } doSafeTestsInline(t, tests) } func TestReferenceLink(t *testing.T) { t.Parallel() var tests = []string{ "[link][ref]\n", "

    [link][ref]

    \n", "[link][ref]\n [ref]: /url/ \"title\"\n", "

    link

    \n", "[link][ref]\n [ref]: /url/\n", "

    link

    \n", " [ref]: /url/\n", "", " [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n", "", " [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n [4spaces]: /url/\n", "
    [4spaces]: /url/\n
    \n", "[hmm](ref2)\n [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n", "

    hmm

    \n", "[ref]\n", "

    [ref]

    \n", "[ref]\n [ref]: /url/ \"title\"\n", "

    ref

    \n", "[ref]\n [ref]: ../url/ \"title\"\n", "

    ref

    \n", "[link][ref]\n [ref]: /url/", "

    link

    \n", } doLinkTestsInline(t, tests) } func TestTags(t *testing.T) { t.Parallel() var tests = []string{ "a tag\n", "

    a tag

    \n", "tag\n", "

    tag

    \n", "mismatch\n", "

    mismatch

    \n", "a tag\n", "

    a tag

    \n", } doTestsInline(t, tests) } func TestAutoLink(t *testing.T) { t.Parallel() var tests = []string{ "http://foo.com/\n", "

    http://foo.com/

    \n", "1 http://foo.com/\n", "

    1 http://foo.com/

    \n", "1http://foo.com/\n", "

    1http://foo.com/

    \n", "1.http://foo.com/\n", "

    1.http://foo.com/

    \n", "1. http://foo.com/\n", "
      \n
    1. http://foo.com/
    2. \n
    \n", "-http://foo.com/\n", "

    -http://foo.com/

    \n", "- http://foo.com/\n", "\n", "_http://foo.com/\n", "

    _http://foo.com/

    \n", "令狐http://foo.com/\n", "

    令狐http://foo.com/

    \n", "令狐 http://foo.com/\n", "

    令狐 http://foo.com/

    \n", "ahttp://foo.com/\n", "

    ahttp://foo.com/

    \n", ">http://foo.com/\n", "
    \n

    http://foo.com/

    \n
    \n", "> http://foo.com/\n", "
    \n

    http://foo.com/

    \n
    \n", "go to \n", "

    go to http://foo.com/

    \n", "a secure \n", "

    a secure https://link.org

    \n", "an email \n", "

    an email some@one.com

    \n", "an email \n", "

    an email some@one.com

    \n", "an email \n", "

    an email some@one.com

    \n", "an ftp \n", "

    an ftp ftp://old.com

    \n", "an ftp \n", "

    an ftp ftp:old.com

    \n", "a link with \n", "

    a link with " + "http://new.com?query=foo&bar

    \n", "quotes mean a tag \n", "

    quotes mean a tag

    \n", "quotes mean a tag \n", "

    quotes mean a tag

    \n", "unless escaped \n", "

    unless escaped " + "http://new.com?query="foo"&bar

    \n", "even a > can be escaped &etc>\n", "

    even a > can be escaped " + "http://new.com?q=>&etc

    \n", "http://fancy.com\n", "

    http://fancy.com

    \n", "This is a link\n", "

    This is a link

    \n", "http://www.fancy.com/A_B.pdf\n", "

    http://www.fancy.com/A_B.pdf

    \n", "(http://www.fancy.com/A_B (\n", "

    (http://www.fancy.com/A_B (

    \n", "(http://www.fancy.com/A_B (part two: http://www.fancy.com/A_B)).\n", "

    (http://www.fancy.com/A_B (part two: http://www.fancy.com/A_B)).

    \n", "http://www.foo.com
    \n", "

    http://www.foo.com

    \n", "http://foo.com/viewtopic.php?f=18&t=297", "

    http://foo.com/viewtopic.php?f=18&t=297

    \n", "http://foo.com/viewtopic.php?param="18"zz", "

    http://foo.com/viewtopic.php?param="18"zz

    \n", "http://foo.com/viewtopic.php?param="18"", "

    http://foo.com/viewtopic.php?param="18"

    \n", "https://fancy.com\n", "

    https://fancy.com

    \n", } doLinkTestsInline(t, tests) } var footnoteTests = []string{ "testing footnotes.[^a]\n\n[^a]: This is the note\n", `

    testing footnotes.1


    1. This is the note
    `, `testing long[^b] notes. [^b]: Paragraph 1 Paragraph 2 ` + "```\n\tsome code\n\t```" + ` Paragraph 3 No longer in the footnote `, `

    testing long1 notes.

    No longer in the footnote


    1. Paragraph 1

      Paragraph 2

      some code

      Paragraph 3

    `, `testing[^c] multiple[^d] notes. [^c]: this is [note] c omg [^d]: this is note d what happens here [note]: /link/c `, `

    testing1 multiple2 notes.

    omg

    what happens here


    1. this is note c
    2. this is note d
    `, "testing inline^[this is the note] notes.\n", `

    testing inline1 notes.


    1. this is the note
    `, "testing multiple[^1] types^[inline note] of notes[^2]\n\n[^2]: the second deferred note\n[^1]: the first deferred note\n\n\twhich happens to be a block\n", `

    testing multiple1 types2 of notes3


    1. the first deferred note

      which happens to be a block

    2. inline note
    3. the second deferred note
    `, `This is a footnote[^1]^[and this is an inline footnote] [^1]: the footnote text. may be multiple paragraphs. `, `

    This is a footnote12


    1. the footnote text.

      may be multiple paragraphs.

    2. and this is an inline footnote
    `, "empty footnote[^]\n\n[^]: fn text", "

    empty footnote1

    \n\n
    \n\n
    \n\n
      \n
    1. fn text
    2. \n
    \n\n
    \n", "Some text.[^note1]\n\n[^note1]: fn1", "

    Some text.1

    \n\n
    \n\n
    \n\n
      \n
    1. fn1
    2. \n
    \n\n
    \n", "Some text.[^note1][^note2]\n\n[^note1]: fn1\n[^note2]: fn2\n", "

    Some text.12

    \n\n
    \n\n
    \n\n
      \n
    1. fn1
    2. \n\n
    3. fn2
    4. \n
    \n\n
    \n", `Bla bla [^1] [WWW][w3] [^1]: This is a footnote [w3]: http://www.w3.org/ `, `

    Bla bla 1 WWW


    1. This is a footnote
    `, `This is exciting![^fn1] [^fn1]: Fine print `, `

    This is exciting!1


    1. Fine print
    `, `This text does not reference a footnote. [^footnote]: But it has a footnote! And it gets omitted. `, "

    This text does not reference a footnote.

    \n", } func TestFootnotes(t *testing.T) { t.Parallel() doTestsInlineParam(t, footnoteTests, TestParams{ extensions: Footnotes, }) } func TestFootnotesWithParameters(t *testing.T) { t.Parallel() tests := make([]string, len(footnoteTests)) prefix := "testPrefix" returnText := "ret" re := regexp.MustCompile(`(?ms)
  • (.*?)
  • `) // Transform the test expectations to match the parameters we're using. for i, test := range footnoteTests { if i%2 == 1 { test = strings.Replace(test, "fn:", "fn:"+prefix, -1) test = strings.Replace(test, "fnref:", "fnref:"+prefix, -1) test = re.ReplaceAllString(test, `
  • $2 ret
  • `) } tests[i] = test } params := HTMLRendererParameters{ FootnoteAnchorPrefix: prefix, FootnoteReturnLinkContents: returnText, } doTestsInlineParam(t, tests, TestParams{ extensions: Footnotes, HTMLFlags: FootnoteReturnLinks, HTMLRendererParameters: params, }) } func TestNestedFootnotes(t *testing.T) { t.Parallel() var tests = []string{ `Paragraph.[^fn1] [^fn1]: Asterisk[^fn2] [^fn2]: Obelisk`, `

    Paragraph.1


    1. Asterisk2
    2. Obelisk
    `, } doTestsInlineParam(t, tests, TestParams{extensions: Footnotes}) } func TestInlineComments(t *testing.T) { t.Parallel() var tests = []string{ "Hello \n", "

    Hello

    \n", "Hello ", "

    Hello

    \n", "Hello \n", "

    Hello

    \n", "Hello \na", "

    Hello \na

    \n", "* list \n", "
      \n
    • list
    • \n
    \n", " comment\n", "

    comment

    \n", "blahblah\n\nrhubarb\n", "

    blahblah\n\nrhubarb

    \n", } doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsDashes}) } func TestSmartDoubleQuotes(t *testing.T) { t.Parallel() var tests = []string{ "this should be normal \"quoted\" text.\n", "

    this should be normal “quoted” text.

    \n", "this \" single double\n", "

    this “ single double

    \n", "two pair of \"some\" quoted \"text\".\n", "

    two pair of “some” quoted “text”.

    \n"} doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants}) } func TestSmartDoubleQuotesNBSP(t *testing.T) { t.Parallel() var tests = []string{ "this should be normal \"quoted\" text.\n", "

    this should be normal “ quoted ” text.

    \n", "this \" single double\n", "

    this “  single double

    \n", "two pair of \"some\" quoted \"text\".\n", "

    two pair of “ some ” quoted “ text ”.

    \n"} doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsQuotesNBSP}) } func TestSmartAngledDoubleQuotes(t *testing.T) { t.Parallel() var tests = []string{ "this should be angled \"quoted\" text.\n", "

    this should be angled «quoted» text.

    \n", "this \" single double\n", "

    this « single double

    \n", "two pair of \"some\" quoted \"text\".\n", "

    two pair of «some» quoted «text».

    \n"} doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsAngledQuotes}) } func TestSmartAngledDoubleQuotesNBSP(t *testing.T) { t.Parallel() var tests = []string{ "this should be angled \"quoted\" text.\n", "

    this should be angled « quoted » text.

    \n", "this \" single double\n", "

    this «  single double

    \n", "two pair of \"some\" quoted \"text\".\n", "

    two pair of « some » quoted « text ».

    \n"} doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsAngledQuotes | SmartypantsQuotesNBSP}) } func TestSmartFractions(t *testing.T) { t.Parallel() var tests = []string{ "1/2, 1/4 and 3/4; 1/4th and 3/4ths\n", "

    ½, ¼ and ¾; ¼th and ¾ths

    \n", "1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n", "

    1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.

    \n"} doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants}) tests = []string{ "1/2, 2/3, 81/100 and 1000000/1048576.\n", "

    12, 23, 81100 and 10000001048576.

    \n", "1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n", "

    1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.

    \n"} doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsFractions}) } func TestDisableSmartDashes(t *testing.T) { t.Parallel() doTestsInlineParam(t, []string{ "foo - bar\n", "

    foo - bar

    \n", "foo -- bar\n", "

    foo -- bar

    \n", "foo --- bar\n", "

    foo --- bar

    \n", }, TestParams{}) doTestsInlineParam(t, []string{ "foo - bar\n", "

    foo – bar

    \n", "foo -- bar\n", "

    foo — bar

    \n", "foo --- bar\n", "

    foo —– bar

    \n", }, TestParams{HTMLFlags: Smartypants | SmartypantsDashes}) doTestsInlineParam(t, []string{ "foo - bar\n", "

    foo - bar

    \n", "foo -- bar\n", "

    foo – bar

    \n", "foo --- bar\n", "

    foo — bar

    \n", }, TestParams{HTMLFlags: Smartypants | SmartypantsLatexDashes | SmartypantsDashes}) doTestsInlineParam(t, []string{ "foo - bar\n", "

    foo - bar

    \n", "foo -- bar\n", "

    foo -- bar

    \n", "foo --- bar\n", "

    foo --- bar

    \n", }, TestParams{HTMLFlags: Smartypants | SmartypantsLatexDashes}) } func TestSkipLinks(t *testing.T) { t.Parallel() doTestsInlineParam(t, []string{ "[foo](gopher://foo.bar)", "

    foo

    \n", "[foo](mailto://bar/)\n", "

    foo

    \n", }, TestParams{ HTMLFlags: SkipLinks, }) } func TestSkipImages(t *testing.T) { t.Parallel() doTestsInlineParam(t, []string{ "![foo](/bar/)\n", "

    \n", }, TestParams{ HTMLFlags: SkipImages, }) } func TestUseXHTML(t *testing.T) { t.Parallel() doTestsParam(t, []string{ "---", "
    \n", }, TestParams{}) doTestsParam(t, []string{ "---", "
    \n", }, TestParams{HTMLFlags: UseXHTML}) } func TestSkipHTML(t *testing.T) { t.Parallel() doTestsParam(t, []string{ "
    \n\ntext\n\n
    the form
    ", "

    text

    \n\n

    the form

    \n", "text inline html more text", "

    text inline html more text

    \n", }, TestParams{HTMLFlags: SkipHTML}) } func BenchmarkSmartDoubleQuotes(b *testing.B) { params := TestParams{HTMLFlags: Smartypants} params.extensions |= Autolink | Strikethrough params.HTMLFlags |= UseXHTML for i := 0; i < b.N; i++ { runMarkdown("this should be normal \"quoted\" text.\n", params) } } blackfriday-2.1.0/markdown.go000066400000000000000000000633341374571415200161600ustar00rootroot00000000000000// Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. package blackfriday import ( "bytes" "fmt" "io" "strings" "unicode/utf8" ) // // Markdown parsing and processing // // Version string of the package. Appears in the rendered document when // CompletePage flag is on. const Version = "2.0" // Extensions is a bitwise or'ed collection of enabled Blackfriday's // extensions. type Extensions int // These are the supported markdown parsing extensions. // OR these values together to select multiple extensions. const ( NoExtensions Extensions = 0 NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words Tables // Render tables FencedCode // Render fenced code blocks Autolink // Detect embedded URLs that are not explicitly marked Strikethrough // Strikethrough text using ~~test~~ LaxHTMLBlocks // Loosen up HTML block parsing rules SpaceHeadings // Be strict about prefix heading rules HardLineBreak // Translate newlines into line breaks TabSizeEight // Expand tabs to eight spaces instead of four Footnotes // Pandoc-style footnotes NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block HeadingIDs // specify heading IDs with {#id} Titleblock // Titleblock ala pandoc AutoHeadingIDs // Create the heading ID from the text BackslashLineBreak // Translate trailing backslashes into line breaks DefinitionLists // Render definition lists CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants | SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists ) // ListType contains bitwise or'ed flags for list and list item objects. type ListType int // These are the possible flag values for the ListItem renderer. // Multiple flag values may be ORed together. // These are mostly of interest if you are writing a new output format. const ( ListTypeOrdered ListType = 1 << iota ListTypeDefinition ListTypeTerm ListItemContainsBlock ListItemBeginningOfList // TODO: figure out if this is of any use now ListItemEndOfList ) // CellAlignFlags holds a type of alignment in a table cell. type CellAlignFlags int // These are the possible flag values for the table cell renderer. // Only a single one of these values will be used; they are not ORed together. // These are mostly of interest if you are writing a new output format. const ( TableAlignmentLeft CellAlignFlags = 1 << iota TableAlignmentRight TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight) ) // The size of a tab stop. const ( TabSizeDefault = 4 TabSizeDouble = 8 ) // blockTags is a set of tags that are recognized as HTML block tags. // Any of these can be included in markdown text without special escaping. var blockTags = map[string]struct{}{ "blockquote": {}, "del": {}, "div": {}, "dl": {}, "fieldset": {}, "form": {}, "h1": {}, "h2": {}, "h3": {}, "h4": {}, "h5": {}, "h6": {}, "iframe": {}, "ins": {}, "math": {}, "noscript": {}, "ol": {}, "pre": {}, "p": {}, "script": {}, "style": {}, "table": {}, "ul": {}, // HTML5 "address": {}, "article": {}, "aside": {}, "canvas": {}, "figcaption": {}, "figure": {}, "footer": {}, "header": {}, "hgroup": {}, "main": {}, "nav": {}, "output": {}, "progress": {}, "section": {}, "video": {}, } // Renderer is the rendering interface. This is mostly of interest if you are // implementing a new rendering format. // // Only an HTML implementation is provided in this repository, see the README // for external implementations. type Renderer interface { // RenderNode is the main rendering method. It will be called once for // every leaf node and twice for every non-leaf node (first with // entering=true, then with entering=false). The method should write its // rendition of the node to the supplied writer w. RenderNode(w io.Writer, node *Node, entering bool) WalkStatus // RenderHeader is a method that allows the renderer to produce some // content preceding the main body of the output document. The header is // understood in the broad sense here. For example, the default HTML // renderer will write not only the HTML document preamble, but also the // table of contents if it was requested. // // The method will be passed an entire document tree, in case a particular // implementation needs to inspect it to produce output. // // The output should be written to the supplied writer w. If your // implementation has no header to write, supply an empty implementation. RenderHeader(w io.Writer, ast *Node) // RenderFooter is a symmetric counterpart of RenderHeader. RenderFooter(w io.Writer, ast *Node) } // Callback functions for inline parsing. One such function is defined // for each character that triggers a response when parsing inline data. type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node) // Markdown is a type that holds extensions and the runtime state used by // Parse, and the renderer. You can not use it directly, construct it with New. type Markdown struct { renderer Renderer referenceOverride ReferenceOverrideFunc refs map[string]*reference inlineCallback [256]inlineParser extensions Extensions nesting int maxNesting int insideLink bool // Footnotes need to be ordered as well as available to quickly check for // presence. If a ref is also a footnote, it's stored both in refs and here // in notes. Slice is nil if footnotes not enabled. notes []*reference doc *Node tip *Node // = doc oldTip *Node lastMatchedContainer *Node // = doc allClosed bool } func (p *Markdown) getRef(refid string) (ref *reference, found bool) { if p.referenceOverride != nil { r, overridden := p.referenceOverride(refid) if overridden { if r == nil { return nil, false } return &reference{ link: []byte(r.Link), title: []byte(r.Title), noteID: 0, hasBlock: false, text: []byte(r.Text)}, true } } // refs are case insensitive ref, found = p.refs[strings.ToLower(refid)] return ref, found } func (p *Markdown) finalize(block *Node) { above := block.Parent block.open = false p.tip = above } func (p *Markdown) addChild(node NodeType, offset uint32) *Node { return p.addExistingChild(NewNode(node), offset) } func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node { for !p.tip.canContain(node.Type) { p.finalize(p.tip) } p.tip.AppendChild(node) p.tip = node return node } func (p *Markdown) closeUnmatchedBlocks() { if !p.allClosed { for p.oldTip != p.lastMatchedContainer { parent := p.oldTip.Parent p.finalize(p.oldTip) p.oldTip = parent } p.allClosed = true } } // // // Public interface // // // Reference represents the details of a link. // See the documentation in Options for more details on use-case. type Reference struct { // Link is usually the URL the reference points to. Link string // Title is the alternate text describing the link in more detail. Title string // Text is the optional text to override the ref with if the syntax used was // [refid][] Text string } // ReferenceOverrideFunc is expected to be called with a reference string and // return either a valid Reference type that the reference string maps to or // nil. If overridden is false, the default reference logic will be executed. // See the documentation in Options for more details on use-case. type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool) // New constructs a Markdown processor. You can use the same With* functions as // for Run() to customize parser's behavior and the renderer. func New(opts ...Option) *Markdown { var p Markdown for _, opt := range opts { opt(&p) } p.refs = make(map[string]*reference) p.maxNesting = 16 p.insideLink = false docNode := NewNode(Document) p.doc = docNode p.tip = docNode p.oldTip = docNode p.lastMatchedContainer = docNode p.allClosed = true // register inline parsers p.inlineCallback[' '] = maybeLineBreak p.inlineCallback['*'] = emphasis p.inlineCallback['_'] = emphasis if p.extensions&Strikethrough != 0 { p.inlineCallback['~'] = emphasis } p.inlineCallback['`'] = codeSpan p.inlineCallback['\n'] = lineBreak p.inlineCallback['['] = link p.inlineCallback['<'] = leftAngle p.inlineCallback['\\'] = escape p.inlineCallback['&'] = entity p.inlineCallback['!'] = maybeImage p.inlineCallback['^'] = maybeInlineFootnote if p.extensions&Autolink != 0 { p.inlineCallback['h'] = maybeAutoLink p.inlineCallback['m'] = maybeAutoLink p.inlineCallback['f'] = maybeAutoLink p.inlineCallback['H'] = maybeAutoLink p.inlineCallback['M'] = maybeAutoLink p.inlineCallback['F'] = maybeAutoLink } if p.extensions&Footnotes != 0 { p.notes = make([]*reference, 0) } return &p } // Option customizes the Markdown processor's default behavior. type Option func(*Markdown) // WithRenderer allows you to override the default renderer. func WithRenderer(r Renderer) Option { return func(p *Markdown) { p.renderer = r } } // WithExtensions allows you to pick some of the many extensions provided by // Blackfriday. You can bitwise OR them. func WithExtensions(e Extensions) Option { return func(p *Markdown) { p.extensions = e } } // WithNoExtensions turns off all extensions and custom behavior. func WithNoExtensions() Option { return func(p *Markdown) { p.extensions = NoExtensions p.renderer = NewHTMLRenderer(HTMLRendererParameters{ Flags: HTMLFlagsNone, }) } } // WithRefOverride sets an optional function callback that is called every // time a reference is resolved. // // In Markdown, the link reference syntax can be made to resolve a link to // a reference instead of an inline URL, in one of the following ways: // // * [link text][refid] // * [refid][] // // Usually, the refid is defined at the bottom of the Markdown document. If // this override function is provided, the refid is passed to the override // function first, before consulting the defined refids at the bottom. If // the override function indicates an override did not occur, the refids at // the bottom will be used to fill in the link details. func WithRefOverride(o ReferenceOverrideFunc) Option { return func(p *Markdown) { p.referenceOverride = o } } // Run is the main entry point to Blackfriday. It parses and renders a // block of markdown-encoded text. // // The simplest invocation of Run takes one argument, input: // output := Run(input) // This will parse the input with CommonExtensions enabled and render it with // the default HTMLRenderer (with CommonHTMLFlags). // // Variadic arguments opts can customize the default behavior. Since Markdown // type does not contain exported fields, you can not use it directly. Instead, // use the With* functions. For example, this will call the most basic // functionality, with no extensions: // output := Run(input, WithNoExtensions()) // // You can use any number of With* arguments, even contradicting ones. They // will be applied in order of appearance and the latter will override the // former: // output := Run(input, WithNoExtensions(), WithExtensions(exts), // WithRenderer(yourRenderer)) func Run(input []byte, opts ...Option) []byte { r := NewHTMLRenderer(HTMLRendererParameters{ Flags: CommonHTMLFlags, }) optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)} optList = append(optList, opts...) parser := New(optList...) ast := parser.Parse(input) var buf bytes.Buffer parser.renderer.RenderHeader(&buf, ast) ast.Walk(func(node *Node, entering bool) WalkStatus { return parser.renderer.RenderNode(&buf, node, entering) }) parser.renderer.RenderFooter(&buf, ast) return buf.Bytes() } // Parse is an entry point to the parsing part of Blackfriday. It takes an // input markdown document and produces a syntax tree for its contents. This // tree can then be rendered with a default or custom renderer, or // analyzed/transformed by the caller to whatever non-standard needs they have. // The return value is the root node of the syntax tree. func (p *Markdown) Parse(input []byte) *Node { p.block(input) // Walk the tree and finish up some of unfinished blocks for p.tip != nil { p.finalize(p.tip) } // Walk the tree again and process inline markdown in each block p.doc.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell { p.inline(node, node.content) node.content = nil } return GoToNext }) p.parseRefsToAST() return p.doc } func (p *Markdown) parseRefsToAST() { if p.extensions&Footnotes == 0 || len(p.notes) == 0 { return } p.tip = p.doc block := p.addBlock(List, nil) block.IsFootnotesList = true block.ListFlags = ListTypeOrdered flags := ListItemBeginningOfList // Note: this loop is intentionally explicit, not range-form. This is // because the body of the loop will append nested footnotes to p.notes and // we need to process those late additions. Range form would only walk over // the fixed initial set. for i := 0; i < len(p.notes); i++ { ref := p.notes[i] p.addExistingChild(ref.footnote, 0) block := ref.footnote block.ListFlags = flags | ListTypeOrdered block.RefLink = ref.link if ref.hasBlock { flags |= ListItemContainsBlock p.block(ref.title) } else { p.inline(block, ref.title) } flags &^= ListItemBeginningOfList | ListItemContainsBlock } above := block.Parent finalizeList(block) p.tip = above block.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Paragraph || node.Type == Heading { p.inline(node, node.content) node.content = nil } return GoToNext }) } // // Link references // // This section implements support for references that (usually) appear // as footnotes in a document, and can be referenced anywhere in the document. // The basic format is: // // [1]: http://www.google.com/ "Google" // [2]: http://www.github.com/ "Github" // // Anywhere in the document, the reference can be linked by referring to its // label, i.e., 1 and 2 in this example, as in: // // This library is hosted on [Github][2], a git hosting site. // // Actual footnotes as specified in Pandoc and supported by some other Markdown // libraries such as php-markdown are also taken care of. They look like this: // // This sentence needs a bit of further explanation.[^note] // // [^note]: This is the explanation. // // Footnotes should be placed at the end of the document in an ordered list. // Finally, there are inline footnotes such as: // // Inline footnotes^[Also supported.] provide a quick inline explanation, // but are rendered at the bottom of the document. // // reference holds all information necessary for a reference-style links or // footnotes. // // Consider this markdown with reference-style links: // // [link][ref] // // [ref]: /url/ "tooltip title" // // It will be ultimately converted to this HTML: // //

    link

    // // And a reference structure will be populated as follows: // // p.refs["ref"] = &reference{ // link: "/url/", // title: "tooltip title", // } // // Alternatively, reference can contain information about a footnote. Consider // this markdown: // // Text needing a footnote.[^a] // // [^a]: This is the note // // A reference structure will be populated as follows: // // p.refs["a"] = &reference{ // link: "a", // title: "This is the note", // noteID: , // } // // TODO: As you can see, it begs for splitting into two dedicated structures // for refs and for footnotes. type reference struct { link []byte title []byte noteID int // 0 if not a footnote ref hasBlock bool footnote *Node // a link to the Item node within a list of footnotes text []byte // only gets populated by refOverride feature with Reference.Text } func (r *reference) String() string { return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}", r.link, r.title, r.text, r.noteID, r.hasBlock) } // Check whether or not data starts with a reference link. // If so, it is parsed and stored in the list of references // (in the render struct). // Returns the number of bytes to skip to move past it, // or zero if the first line is not a reference. func isReference(p *Markdown, data []byte, tabSize int) int { // up to 3 optional leading spaces if len(data) < 4 { return 0 } i := 0 for i < 3 && data[i] == ' ' { i++ } noteID := 0 // id part: anything but a newline between brackets if data[i] != '[' { return 0 } i++ if p.extensions&Footnotes != 0 { if i < len(data) && data[i] == '^' { // we can set it to anything here because the proper noteIds will // be assigned later during the second pass. It just has to be != 0 noteID = 1 i++ } } idOffset := i for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' { i++ } if i >= len(data) || data[i] != ']' { return 0 } idEnd := i // footnotes can have empty ID, like this: [^], but a reference can not be // empty like this: []. Break early if it's not a footnote and there's no ID if noteID == 0 && idOffset == idEnd { return 0 } // spacer: colon (space | tab)* newline? (space | tab)* i++ if i >= len(data) || data[i] != ':' { return 0 } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i < len(data) && (data[i] == '\n' || data[i] == '\r') { i++ if i < len(data) && data[i] == '\n' && data[i-1] == '\r' { i++ } } for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i >= len(data) { return 0 } var ( linkOffset, linkEnd int titleOffset, titleEnd int lineEnd int raw []byte hasBlock bool ) if p.extensions&Footnotes != 0 && noteID != 0 { linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize) lineEnd = linkEnd } else { linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i) } if lineEnd == 0 { return 0 } // a valid ref has been found ref := &reference{ noteID: noteID, hasBlock: hasBlock, } if noteID > 0 { // reusing the link field for the id since footnotes don't have links ref.link = data[idOffset:idEnd] // if footnote, it's not really a title, it's the contained text ref.title = raw } else { ref.link = data[linkOffset:linkEnd] ref.title = data[titleOffset:titleEnd] } // id matches are case-insensitive id := string(bytes.ToLower(data[idOffset:idEnd])) p.refs[id] = ref return lineEnd } func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) { // link: whitespace-free sequence, optionally between angle brackets if data[i] == '<' { i++ } linkOffset = i for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' { i++ } linkEnd = i if data[linkOffset] == '<' && data[linkEnd-1] == '>' { linkOffset++ linkEnd-- } // optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' { return } // compute end-of-line if i >= len(data) || data[i] == '\r' || data[i] == '\n' { lineEnd = i } if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' { lineEnd++ } // optional (space|tab)* spacer after a newline if lineEnd > 0 { i = lineEnd + 1 for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } } // optional title: any non-newline sequence enclosed in '"() alone on its line if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') { i++ titleOffset = i // look for EOL for i < len(data) && data[i] != '\n' && data[i] != '\r' { i++ } if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' { titleEnd = i + 1 } else { titleEnd = i } // step back i-- for i > titleOffset && (data[i] == ' ' || data[i] == '\t') { i-- } if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') { lineEnd = titleEnd titleEnd = i } } return } // The first bit of this logic is the same as Parser.listItem, but the rest // is much simpler. This function simply finds the entire block and shifts it // over by one tab if it is indeed a block (just returns the line if it's not). // blockEnd is the end of the section in the input buffer, and contents is the // extracted text that was shifted over one tab. It will need to be rendered at // the end of the document. func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) { if i == 0 || len(data) == 0 { return } // skip leading whitespace on first line for i < len(data) && data[i] == ' ' { i++ } blockStart = i // find the end of the line blockEnd = i for i < len(data) && data[i-1] != '\n' { i++ } // get working buffer var raw bytes.Buffer // put the first line into the working buffer raw.Write(data[blockEnd:i]) blockEnd = i // process the following lines containsBlankLine := false gatherLines: for blockEnd < len(data) { i++ // find the end of this line for i < len(data) && data[i-1] != '\n' { i++ } // if it is an empty line, guess that it is part of this item // and move on to the next line if p.isEmpty(data[blockEnd:i]) > 0 { containsBlankLine = true blockEnd = i continue } n := 0 if n = isIndented(data[blockEnd:i], indentSize); n == 0 { // this is the end of the block. // we don't want to include this last line in the index. break gatherLines } // if there were blank lines before this one, insert a new one now if containsBlankLine { raw.WriteByte('\n') containsBlankLine = false } // get rid of that first tab, write to buffer raw.Write(data[blockEnd+n : i]) hasBlock = true blockEnd = i } if data[blockEnd-1] != '\n' { raw.WriteByte('\n') } contents = raw.Bytes() return } // // // Miscellaneous helper functions // // // Test if a character is a punctuation symbol. // Taken from a private function in regexp in the stdlib. func ispunct(c byte) bool { for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") { if c == r { return true } } return false } // Test if a character is a whitespace character. func isspace(c byte) bool { return ishorizontalspace(c) || isverticalspace(c) } // Test if a character is a horizontal whitespace character. func ishorizontalspace(c byte) bool { return c == ' ' || c == '\t' } // Test if a character is a vertical character. func isverticalspace(c byte) bool { return c == '\n' || c == '\r' || c == '\f' || c == '\v' } // Test if a character is letter. func isletter(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } // Test if a character is a letter or a digit. // TODO: check when this is looking for ASCII alnum and when it should use unicode func isalnum(c byte) bool { return (c >= '0' && c <= '9') || isletter(c) } // Replace tab characters with spaces, aligning to the next TAB_SIZE column. // always ends output with a newline func expandTabs(out *bytes.Buffer, line []byte, tabSize int) { // first, check for common cases: no tabs, or only tabs at beginning of line i, prefix := 0, 0 slowcase := false for i = 0; i < len(line); i++ { if line[i] == '\t' { if prefix == i { prefix++ } else { slowcase = true break } } } // no need to decode runes if all tabs are at the beginning of the line if !slowcase { for i = 0; i < prefix*tabSize; i++ { out.WriteByte(' ') } out.Write(line[prefix:]) return } // the slow case: we need to count runes to figure out how // many spaces to insert for each tab column := 0 i = 0 for i < len(line) { start := i for i < len(line) && line[i] != '\t' { _, size := utf8.DecodeRune(line[i:]) i += size column++ } if i > start { out.Write(line[start:i]) } if i >= len(line) { break } for { out.WriteByte(' ') column++ if column%tabSize == 0 { break } } i++ } } // Find if a line counts as indented or not. // Returns number of characters the indent is (0 = not indented). func isIndented(data []byte, indentSize int) int { if len(data) == 0 { return 0 } if data[0] == '\t' { return 1 } if len(data) < indentSize { return 0 } for i := 0; i < indentSize; i++ { if data[i] != ' ' { return 0 } } return indentSize } // Create a url-safe slug for fragments func slugify(in []byte) []byte { if len(in) == 0 { return in } out := make([]byte, 0, len(in)) sym := false for _, ch := range in { if isalnum(ch) { sym = false out = append(out, ch) } else if sym { continue } else { out = append(out, '-') sym = true } } var a, b int var ch byte for a, ch = range out { if ch != '-' { break } } for b = len(out) - 1; b > 0; b-- { if out[b] != '-' { break } } return out[a : b+1] } blackfriday-2.1.0/markdown_test.go000066400000000000000000000012721374571415200172100ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Unit tests for full document parsing and rendering // package blackfriday import "testing" func TestDocument(t *testing.T) { t.Parallel() var tests = []string{ // Empty document. "", "", " ", "", // This shouldn't panic. // https://github.com/russross/blackfriday/issues/172 "[]:<", "

    []:<

    \n", // This shouldn't panic. // https://github.com/russross/blackfriday/issues/173 " [", "

    [

    \n", } doTests(t, tests) } blackfriday-2.1.0/node.go000066400000000000000000000212021374571415200152470ustar00rootroot00000000000000package blackfriday import ( "bytes" "fmt" ) // NodeType specifies a type of a single node of a syntax tree. Usually one // node (and its type) corresponds to a single markdown feature, e.g. emphasis // or code block. type NodeType int // Constants for identifying different types of nodes. See NodeType. const ( Document NodeType = iota BlockQuote List Item Paragraph Heading HorizontalRule Emph Strong Del Link Image Text HTMLBlock CodeBlock Softbreak Hardbreak Code HTMLSpan Table TableCell TableHead TableBody TableRow ) var nodeTypeNames = []string{ Document: "Document", BlockQuote: "BlockQuote", List: "List", Item: "Item", Paragraph: "Paragraph", Heading: "Heading", HorizontalRule: "HorizontalRule", Emph: "Emph", Strong: "Strong", Del: "Del", Link: "Link", Image: "Image", Text: "Text", HTMLBlock: "HTMLBlock", CodeBlock: "CodeBlock", Softbreak: "Softbreak", Hardbreak: "Hardbreak", Code: "Code", HTMLSpan: "HTMLSpan", Table: "Table", TableCell: "TableCell", TableHead: "TableHead", TableBody: "TableBody", TableRow: "TableRow", } func (t NodeType) String() string { return nodeTypeNames[t] } // ListData contains fields relevant to a List and Item node type. type ListData struct { ListFlags ListType Tight bool // Skip

    s around list item data if true BulletChar byte // '*', '+' or '-' in bullet lists Delimiter byte // '.' or ')' after the number in ordered lists RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering IsFootnotesList bool // This is a list of footnotes } // LinkData contains fields relevant to a Link node type. type LinkData struct { Destination []byte // Destination is what goes into a href Title []byte // Title is the tooltip thing that goes in a title attribute NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote Footnote *Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil. } // CodeBlockData contains fields relevant to a CodeBlock node type. type CodeBlockData struct { IsFenced bool // Specifies whether it's a fenced code block or an indented one Info []byte // This holds the info string FenceChar byte FenceLength int FenceOffset int } // TableCellData contains fields relevant to a TableCell node type. type TableCellData struct { IsHeader bool // This tells if it's under the header row Align CellAlignFlags // This holds the value for align attribute } // HeadingData contains fields relevant to a Heading node type. type HeadingData struct { Level int // This holds the heading level number HeadingID string // This might hold heading ID, if present IsTitleblock bool // Specifies whether it's a title block } // Node is a single element in the abstract syntax tree of the parsed document. // It holds connections to the structurally neighboring nodes and, for certain // types of nodes, additional information that might be needed when rendering. type Node struct { Type NodeType // Determines the type of the node Parent *Node // Points to the parent FirstChild *Node // Points to the first child, if any LastChild *Node // Points to the last child, if any Prev *Node // Previous sibling; nil if it's the first child Next *Node // Next sibling; nil if it's the last child Literal []byte // Text contents of the leaf nodes HeadingData // Populated if Type is Heading ListData // Populated if Type is List CodeBlockData // Populated if Type is CodeBlock LinkData // Populated if Type is Link TableCellData // Populated if Type is TableCell content []byte // Markdown content of the block nodes open bool // Specifies an open block node that has not been finished to process yet } // NewNode allocates a node of a specified type. func NewNode(typ NodeType) *Node { return &Node{ Type: typ, open: true, } } func (n *Node) String() string { ellipsis := "" snippet := n.Literal if len(snippet) > 16 { snippet = snippet[:16] ellipsis = "..." } return fmt.Sprintf("%s: '%s%s'", n.Type, snippet, ellipsis) } // Unlink removes node 'n' from the tree. // It panics if the node is nil. func (n *Node) Unlink() { if n.Prev != nil { n.Prev.Next = n.Next } else if n.Parent != nil { n.Parent.FirstChild = n.Next } if n.Next != nil { n.Next.Prev = n.Prev } else if n.Parent != nil { n.Parent.LastChild = n.Prev } n.Parent = nil n.Next = nil n.Prev = nil } // AppendChild adds a node 'child' as a child of 'n'. // It panics if either node is nil. func (n *Node) AppendChild(child *Node) { child.Unlink() child.Parent = n if n.LastChild != nil { n.LastChild.Next = child child.Prev = n.LastChild n.LastChild = child } else { n.FirstChild = child n.LastChild = child } } // InsertBefore inserts 'sibling' immediately before 'n'. // It panics if either node is nil. func (n *Node) InsertBefore(sibling *Node) { sibling.Unlink() sibling.Prev = n.Prev if sibling.Prev != nil { sibling.Prev.Next = sibling } sibling.Next = n n.Prev = sibling sibling.Parent = n.Parent if sibling.Prev == nil { sibling.Parent.FirstChild = sibling } } // IsContainer returns true if 'n' can contain children. func (n *Node) IsContainer() bool { switch n.Type { case Document: fallthrough case BlockQuote: fallthrough case List: fallthrough case Item: fallthrough case Paragraph: fallthrough case Heading: fallthrough case Emph: fallthrough case Strong: fallthrough case Del: fallthrough case Link: fallthrough case Image: fallthrough case Table: fallthrough case TableHead: fallthrough case TableBody: fallthrough case TableRow: fallthrough case TableCell: return true default: return false } } // IsLeaf returns true if 'n' is a leaf node. func (n *Node) IsLeaf() bool { return !n.IsContainer() } func (n *Node) canContain(t NodeType) bool { if n.Type == List { return t == Item } if n.Type == Document || n.Type == BlockQuote || n.Type == Item { return t != Item } if n.Type == Table { return t == TableHead || t == TableBody } if n.Type == TableHead || n.Type == TableBody { return t == TableRow } if n.Type == TableRow { return t == TableCell } return false } // WalkStatus allows NodeVisitor to have some control over the tree traversal. // It is returned from NodeVisitor and different values allow Node.Walk to // decide which node to go to next. type WalkStatus int const ( // GoToNext is the default traversal of every node. GoToNext WalkStatus = iota // SkipChildren tells walker to skip all children of current node. SkipChildren // Terminate tells walker to terminate the traversal. Terminate ) // NodeVisitor is a callback to be called when traversing the syntax tree. // Called twice for every node: once with entering=true when the branch is // first visited, then with entering=false after all the children are done. type NodeVisitor func(node *Node, entering bool) WalkStatus // Walk is a convenience method that instantiates a walker and starts a // traversal of subtree rooted at n. func (n *Node) Walk(visitor NodeVisitor) { w := newNodeWalker(n) for w.current != nil { status := visitor(w.current, w.entering) switch status { case GoToNext: w.next() case SkipChildren: w.entering = false w.next() case Terminate: return } } } type nodeWalker struct { current *Node root *Node entering bool } func newNodeWalker(root *Node) *nodeWalker { return &nodeWalker{ current: root, root: root, entering: true, } } func (nw *nodeWalker) next() { if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root { nw.current = nil return } if nw.entering && nw.current.IsContainer() { if nw.current.FirstChild != nil { nw.current = nw.current.FirstChild nw.entering = true } else { nw.entering = false } } else if nw.current.Next == nil { nw.current = nw.current.Parent nw.entering = false } else { nw.current = nw.current.Next nw.entering = true } } func dump(ast *Node) { fmt.Println(dumpString(ast)) } func dumpR(ast *Node, depth int) string { if ast == nil { return "" } indent := bytes.Repeat([]byte("\t"), depth) content := ast.Literal if content == nil { content = ast.content } result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content) for n := ast.FirstChild; n != nil; n = n.Next { result += dumpR(n, depth+1) } return result } func dumpString(ast *Node) string { return dumpR(ast, 0) } blackfriday-2.1.0/ref_test.go000066400000000000000000000060761374571415200161510ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Markdown 1.0.3 reference tests // package blackfriday import ( "io/ioutil" "path/filepath" "testing" ) func TestReference(t *testing.T) { t.Parallel() files := []string{ "Amps and angle encoding", "Auto links", "Backslash escapes", "Blockquotes with code blocks", "Code Blocks", "Code Spans", "Hard-wrapped paragraphs with list-like lines", "Horizontal rules", "Inline HTML (Advanced)", "Inline HTML (Simple)", "Inline HTML comments", "Links, inline style", "Links, reference style", "Links, shortcut references", "Literal quotes in titles", "Markdown Documentation - Basics", "Markdown Documentation - Syntax", "Nested blockquotes", "Ordered and unordered lists", "Strong and em together", "Tabs", "Tidyness", } doTestsReference(t, files, 0) } func TestReference_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { t.Parallel() files := []string{ "Amps and angle encoding", "Auto links", "Backslash escapes", "Blockquotes with code blocks", "Code Blocks", "Code Spans", "Hard-wrapped paragraphs with list-like lines no empty line before block", "Horizontal rules", "Inline HTML (Advanced)", "Inline HTML (Simple)", "Inline HTML comments", "Links, inline style", "Links, reference style", "Links, shortcut references", "Literal quotes in titles", "Markdown Documentation - Basics", "Markdown Documentation - Syntax", "Nested blockquotes", "Ordered and unordered lists", "Strong and em together", "Tabs", "Tidyness", } doTestsReference(t, files, NoEmptyLineBeforeBlock) } // benchResultAnchor is an anchor variable to store the result of a benchmarked // code so that compiler could never optimize away the call to runMarkdown() var benchResultAnchor string func BenchmarkReference(b *testing.B) { params := TestParams{extensions: CommonExtensions} files := []string{ "Amps and angle encoding", "Auto links", "Backslash escapes", "Blockquotes with code blocks", "Code Blocks", "Code Spans", "Hard-wrapped paragraphs with list-like lines", "Horizontal rules", "Inline HTML (Advanced)", "Inline HTML (Simple)", "Inline HTML comments", "Links, inline style", "Links, reference style", "Links, shortcut references", "Literal quotes in titles", "Markdown Documentation - Basics", "Markdown Documentation - Syntax", "Nested blockquotes", "Ordered and unordered lists", "Strong and em together", "Tabs", "Tidyness", } var tests []string for _, basename := range files { filename := filepath.Join("testdata", basename+".text") inputBytes, err := ioutil.ReadFile(filename) if err != nil { b.Errorf("Couldn't open '%s', error: %v\n", filename, err) continue } tests = append(tests, string(inputBytes)) } b.ResetTimer() for n := 0; n < b.N; n++ { for _, test := range tests { benchResultAnchor = runMarkdown(test, params) } } } blackfriday-2.1.0/smartypants.go000066400000000000000000000264341374571415200167230ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // // SmartyPants rendering // // package blackfriday import ( "bytes" "io" ) // SPRenderer is a struct containing state of a Smartypants renderer. type SPRenderer struct { inSingleQuote bool inDoubleQuote bool callbacks [256]smartCallback } func wordBoundary(c byte) bool { return c == 0 || isspace(c) || ispunct(c) } func tolower(c byte) byte { if c >= 'A' && c <= 'Z' { return c - 'A' + 'a' } return c } func isdigit(c byte) bool { return c >= '0' && c <= '9' } func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote byte, isOpen *bool, addNBSP bool) bool { // edge of the buffer is likely to be a tag that we don't get to see, // so we treat it like text sometimes // enumerate all sixteen possibilities for (previousChar, nextChar) // each can be one of {0, space, punct, other} switch { case previousChar == 0 && nextChar == 0: // context is not any help here, so toggle *isOpen = !*isOpen case isspace(previousChar) && nextChar == 0: // [ "] might be [ "foo...] *isOpen = true case ispunct(previousChar) && nextChar == 0: // [!"] hmm... could be [Run!"] or [("...] *isOpen = false case /* isnormal(previousChar) && */ nextChar == 0: // [a"] is probably a close *isOpen = false case previousChar == 0 && isspace(nextChar): // [" ] might be [...foo" ] *isOpen = false case isspace(previousChar) && isspace(nextChar): // [ " ] context is not any help here, so toggle *isOpen = !*isOpen case ispunct(previousChar) && isspace(nextChar): // [!" ] is probably a close *isOpen = false case /* isnormal(previousChar) && */ isspace(nextChar): // [a" ] this is one of the easy cases *isOpen = false case previousChar == 0 && ispunct(nextChar): // ["!] hmm... could be ["$1.95] or ["!...] *isOpen = false case isspace(previousChar) && ispunct(nextChar): // [ "!] looks more like [ "$1.95] *isOpen = true case ispunct(previousChar) && ispunct(nextChar): // [!"!] context is not any help here, so toggle *isOpen = !*isOpen case /* isnormal(previousChar) && */ ispunct(nextChar): // [a"!] is probably a close *isOpen = false case previousChar == 0 /* && isnormal(nextChar) */ : // ["a] is probably an open *isOpen = true case isspace(previousChar) /* && isnormal(nextChar) */ : // [ "a] this is one of the easy cases *isOpen = true case ispunct(previousChar) /* && isnormal(nextChar) */ : // [!"a] is probably an open *isOpen = true default: // [a'b] maybe a contraction? *isOpen = false } // Note that with the limited lookahead, this non-breaking // space will also be appended to single double quotes. if addNBSP && !*isOpen { out.WriteString(" ") } out.WriteByte('&') if *isOpen { out.WriteByte('l') } else { out.WriteByte('r') } out.WriteByte(quote) out.WriteString("quo;") if addNBSP && *isOpen { out.WriteString(" ") } return true } func (r *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 { t1 := tolower(text[1]) if t1 == '\'' { nextChar := byte(0) if len(text) >= 3 { nextChar = text[2] } if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) { return 1 } } if (t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (len(text) < 3 || wordBoundary(text[2])) { out.WriteString("’") return 0 } if len(text) >= 3 { t2 := tolower(text[2]) if ((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) && (len(text) < 4 || wordBoundary(text[3])) { out.WriteString("’") return 0 } } } nextChar := byte(0) if len(text) > 1 { nextChar = text[1] } if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote, false) { return 0 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 { t1 := tolower(text[1]) t2 := tolower(text[2]) if t1 == 'c' && t2 == ')' { out.WriteString("©") return 2 } if t1 == 'r' && t2 == ')' { out.WriteString("®") return 2 } if len(text) >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')' { out.WriteString("™") return 3 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 { if text[1] == '-' { out.WriteString("—") return 1 } if wordBoundary(previousChar) && wordBoundary(text[1]) { out.WriteString("–") return 0 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 && text[1] == '-' && text[2] == '-' { out.WriteString("—") return 2 } if len(text) >= 2 && text[1] == '-' { out.WriteString("–") return 1 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte, addNBSP bool) int { if bytes.HasPrefix(text, []byte(""")) { nextChar := byte(0) if len(text) >= 7 { nextChar = text[6] } if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, addNBSP) { return 5 } } if bytes.HasPrefix(text, []byte("�")) { return 3 } out.WriteByte('&') return 0 } func (r *SPRenderer) smartAmp(angledQuotes, addNBSP bool) func(*bytes.Buffer, byte, []byte) int { var quote byte = 'd' if angledQuotes { quote = 'a' } return func(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartAmpVariant(out, previousChar, text, quote, addNBSP) } } func (r *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 && text[1] == '.' && text[2] == '.' { out.WriteString("…") return 2 } if len(text) >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.' { out.WriteString("…") return 4 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 && text[1] == '`' { nextChar := byte(0) if len(text) >= 3 { nextChar = text[2] } if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) { return 1 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int { if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 { // is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b // note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8) // and avoid changing dates like 1/23/2005 into fractions. numEnd := 0 for len(text) > numEnd && isdigit(text[numEnd]) { numEnd++ } if numEnd == 0 { out.WriteByte(text[0]) return 0 } denStart := numEnd + 1 if len(text) > numEnd+3 && text[numEnd] == 0xe2 && text[numEnd+1] == 0x81 && text[numEnd+2] == 0x84 { denStart = numEnd + 3 } else if len(text) < numEnd+2 || text[numEnd] != '/' { out.WriteByte(text[0]) return 0 } denEnd := denStart for len(text) > denEnd && isdigit(text[denEnd]) { denEnd++ } if denEnd == denStart { out.WriteByte(text[0]) return 0 } if len(text) == denEnd || wordBoundary(text[denEnd]) && text[denEnd] != '/' { out.WriteString("") out.Write(text[:numEnd]) out.WriteString("") out.Write(text[denStart:denEnd]) out.WriteString("") return denEnd - 1 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int { if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 { if text[0] == '1' && text[1] == '/' && text[2] == '2' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' { out.WriteString("½") return 2 } } if text[0] == '1' && text[1] == '/' && text[2] == '4' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h') { out.WriteString("¼") return 2 } } if text[0] == '3' && text[1] == '/' && text[2] == '4' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's') { out.WriteString("¾") return 2 } } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int { nextChar := byte(0) if len(text) > 1 { nextChar = text[1] } if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, false) { out.WriteString(""") } return 0 } func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartDoubleQuoteVariant(out, previousChar, text, 'd') } func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartDoubleQuoteVariant(out, previousChar, text, 'a') } func (r *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int { i := 0 for i < len(text) && text[i] != '>' { i++ } out.Write(text[:i+1]) return i } type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int // NewSmartypantsRenderer constructs a Smartypants renderer object. func NewSmartypantsRenderer(flags HTMLFlags) *SPRenderer { var ( r SPRenderer smartAmpAngled = r.smartAmp(true, false) smartAmpAngledNBSP = r.smartAmp(true, true) smartAmpRegular = r.smartAmp(false, false) smartAmpRegularNBSP = r.smartAmp(false, true) addNBSP = flags&SmartypantsQuotesNBSP != 0 ) if flags&SmartypantsAngledQuotes == 0 { r.callbacks['"'] = r.smartDoubleQuote if !addNBSP { r.callbacks['&'] = smartAmpRegular } else { r.callbacks['&'] = smartAmpRegularNBSP } } else { r.callbacks['"'] = r.smartAngledDoubleQuote if !addNBSP { r.callbacks['&'] = smartAmpAngled } else { r.callbacks['&'] = smartAmpAngledNBSP } } r.callbacks['\''] = r.smartSingleQuote r.callbacks['('] = r.smartParens if flags&SmartypantsDashes != 0 { if flags&SmartypantsLatexDashes == 0 { r.callbacks['-'] = r.smartDash } else { r.callbacks['-'] = r.smartDashLatex } } r.callbacks['.'] = r.smartPeriod if flags&SmartypantsFractions == 0 { r.callbacks['1'] = r.smartNumber r.callbacks['3'] = r.smartNumber } else { for ch := '1'; ch <= '9'; ch++ { r.callbacks[ch] = r.smartNumberGeneric } } r.callbacks['<'] = r.smartLeftAngle r.callbacks['`'] = r.smartBacktick return &r } // Process is the entry point of the Smartypants renderer. func (r *SPRenderer) Process(w io.Writer, text []byte) { mark := 0 for i := 0; i < len(text); i++ { if action := r.callbacks[text[i]]; action != nil { if i > mark { w.Write(text[mark:i]) } previousChar := byte(0) if i > 0 { previousChar = text[i-1] } var tmp bytes.Buffer i += action(&tmp, previousChar, text[i:]) w.Write(tmp.Bytes()) mark = i + 1 } } if mark < len(text) { w.Write(text[mark:]) } } blackfriday-2.1.0/testdata/000077500000000000000000000000001374571415200156075ustar00rootroot00000000000000blackfriday-2.1.0/testdata/Amps and angle encoding.html000066400000000000000000000007741374571415200227460ustar00rootroot00000000000000

    AT&T has an ampersand in their name.

    AT&T is another way to write it.

    This & that.

    4 < 5.

    6 > 5.

    Here's a link with an ampersand in the URL.

    Here's a link with an amersand in the link text: AT&T.

    Here's an inline link.

    Here's an inline link.

    blackfriday-2.1.0/testdata/Amps and angle encoding.text000066400000000000000000000005751374571415200227650ustar00rootroot00000000000000AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Here's a [link] [1] with an ampersand in the URL. Here's a link with an amersand in the link text: [AT&T] [2]. Here's an inline [link](/script?foo=1&bar=2). Here's an inline [link](). [1]: http://example.com/?foo=1&bar=2 [2]: http://att.com/ "AT&T"blackfriday-2.1.0/testdata/Auto links.html000066400000000000000000000010401374571415200205010ustar00rootroot00000000000000

    Link: http://example.com/.

    With an ampersand: http://example.com/?foo=1&bar=2

    Blockquoted: http://example.com/

    Auto-links should not occur here: <http://example.com/>

    or here: <http://example.com/>
    
    blackfriday-2.1.0/testdata/Auto links.text000066400000000000000000000004071374571415200205270ustar00rootroot00000000000000Link: . With an ampersand: * In a list? * * It should. > Blockquoted: Auto-links should not occur here: `` or here: blackfriday-2.1.0/testdata/Backslash escapes.html000066400000000000000000000033561374571415200220030ustar00rootroot00000000000000

    These should all get escaped:

    Backslash: \

    Backtick: `

    Asterisk: *

    Underscore: _

    Left brace: {

    Right brace: }

    Left bracket: [

    Right bracket: ]

    Left paren: (

    Right paren: )

    Greater-than: >

    Hash: #

    Period: .

    Bang: !

    Plus: +

    Minus: -

    Tilde: ~

    These should not, because they occur within a code block:

    Backslash: \\
    
    Backtick: \`
    
    Asterisk: \*
    
    Underscore: \_
    
    Left brace: \{
    
    Right brace: \}
    
    Left bracket: \[
    
    Right bracket: \]
    
    Left paren: \(
    
    Right paren: \)
    
    Greater-than: \>
    
    Hash: \#
    
    Period: \.
    
    Bang: \!
    
    Plus: \+
    
    Minus: \-
    
    Tilde: \~
    

    Nor should these, which occur in code spans:

    Backslash: \\

    Backtick: \`

    Asterisk: \*

    Underscore: \_

    Left brace: \{

    Right brace: \}

    Left bracket: \[

    Right bracket: \]

    Left paren: \(

    Right paren: \)

    Greater-than: \>

    Hash: \#

    Period: \.

    Bang: \!

    Plus: \+

    Minus: \-

    Tilde: \~

    These should get escaped, even though they're matching pairs for other Markdown constructs:

    *asterisks*

    _underscores_

    `backticks`

    This is a code span with a literal backslash-backtick sequence: \`

    This is a tag with unescaped backticks bar.

    This is a tag with backslashes bar.

    blackfriday-2.1.0/testdata/Backslash escapes.text000066400000000000000000000024061374571415200220160ustar00rootroot00000000000000These should all get escaped: Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: \) Greater-than: \> Hash: \# Period: \. Bang: \! Plus: \+ Minus: \- Tilde: \~ These should not, because they occur within a code block: Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: \) Greater-than: \> Hash: \# Period: \. Bang: \! Plus: \+ Minus: \- Tilde: \~ Nor should these, which occur in code spans: Backslash: `\\` Backtick: `` \` `` Asterisk: `\*` Underscore: `\_` Left brace: `\{` Right brace: `\}` Left bracket: `\[` Right bracket: `\]` Left paren: `\(` Right paren: `\)` Greater-than: `\>` Hash: `\#` Period: `\.` Bang: `\!` Plus: `\+` Minus: `\-` Tilde: `\~` These should get escaped, even though they're matching pairs for other Markdown constructs: \*asterisks\* \_underscores\_ \`backticks\` This is a code span with a literal backslash-backtick sequence: `` \` `` This is a tag with unescaped backticks bar. This is a tag with backslashes bar. blackfriday-2.1.0/testdata/Blockquotes with code blocks.html000066400000000000000000000003101374571415200240470ustar00rootroot00000000000000

    Example:

    sub status {
        print "working";
    }
    

    Or:

    sub status {
        return "working";
    }
    
    blackfriday-2.1.0/testdata/Blockquotes with code blocks.text000066400000000000000000000002071374571415200240740ustar00rootroot00000000000000> Example: > > sub status { > print "working"; > } > > Or: > > sub status { > return "working"; > } blackfriday-2.1.0/testdata/Code Blocks.html000066400000000000000000000004701374571415200205460ustar00rootroot00000000000000
    code block on the first line
    

    Regular text.

    code block indented by spaces
    

    Regular text.

    the lines in this block  
    all contain trailing spaces  
    

    Regular Text.

    code block on the last line
    
    blackfriday-2.1.0/testdata/Code Blocks.text000066400000000000000000000003071374571415200205650ustar00rootroot00000000000000 code block on the first line Regular text. code block indented by spaces Regular text. the lines in this block all contain trailing spaces Regular Text. code block on the last lineblackfriday-2.1.0/testdata/Code Spans.html000066400000000000000000000003501374571415200204120ustar00rootroot00000000000000

    <test a=" content of attribute ">

    Fix for backticks within HTML tag: like this

    Here's how you put `backticks` in a code span.

    blackfriday-2.1.0/testdata/Code Spans.text000066400000000000000000000002471374571415200204370ustar00rootroot00000000000000`` Fix for backticks within HTML tag: like this Here's how you put `` `backticks` `` in a code span. Hard-wrapped paragraphs with list-like lines no empty line before block.html000066400000000000000000000003701374571415200336740ustar00rootroot00000000000000blackfriday-2.1.0/testdata

    In Markdown 1.0.0 and earlier. Version

    1. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.

    Here's one with a bullet.

    • criminey.
    Hard-wrapped paragraphs with list-like lines no empty line before block.text000066400000000000000000000003051374571415200337120ustar00rootroot00000000000000blackfriday-2.1.0/testdataIn Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item. Here's one with a bullet. * criminey. blackfriday-2.1.0/testdata/Hard-wrapped paragraphs with list-like lines.html000066400000000000000000000003231374571415200270270ustar00rootroot00000000000000

    In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.

    Here's one with a bullet. * criminey.

    blackfriday-2.1.0/testdata/Hard-wrapped paragraphs with list-like lines.text000066400000000000000000000003051374571415200270470ustar00rootroot00000000000000In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item. Here's one with a bullet. * criminey. blackfriday-2.1.0/testdata/Horizontal rules.html000066400000000000000000000006011374571415200217360ustar00rootroot00000000000000

    Dashes:





    ---
    




    - - -
    

    Asterisks:





    ***
    




    * * *
    

    Underscores:





    ___
    




    _ _ _
    
    blackfriday-2.1.0/testdata/Horizontal rules.text000066400000000000000000000004161374571415200217620ustar00rootroot00000000000000Dashes: --- --- --- --- --- - - - - - - - - - - - - - - - Asterisks: *** *** *** *** *** * * * * * * * * * * * * * * * Underscores: ___ ___ ___ ___ ___ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ blackfriday-2.1.0/testdata/Inline HTML (Advanced).html000066400000000000000000000002451374571415200222500ustar00rootroot00000000000000

    Simple block on one line:

    foo

    And nested without indentation:

    foo
    bar
    blackfriday-2.1.0/testdata/Inline HTML (Advanced).text000066400000000000000000000002271374571415200222700ustar00rootroot00000000000000Simple block on one line:
    foo
    And nested without indentation:
    foo
    bar
    blackfriday-2.1.0/testdata/Inline HTML (Simple).html000066400000000000000000000013571374571415200220010ustar00rootroot00000000000000

    Here's a simple block:

    foo

    This should be a code block, though:

    <div>
    	foo
    </div>
    

    As should this:

    <div>foo</div>
    

    Now, nested:

    foo

    This should just be an HTML comment:

    Multiline:

    Code block:

    <!-- Comment -->
    

    Just plain comment, with trailing spaces on the line:

    Code:

    <hr />
    

    Hr's:










    blackfriday-2.1.0/testdata/Inline HTML (Simple).text000066400000000000000000000010511374571415200220100ustar00rootroot00000000000000Here's a simple block:
    foo
    This should be a code block, though:
    foo
    As should this:
    foo
    Now, nested:
    foo
    This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
    Hr's:








    blackfriday-2.1.0/testdata/Inline HTML comments.html000066400000000000000000000002711374571415200222460ustar00rootroot00000000000000

    Paragraph one.

    Paragraph two.

    The end.

    blackfriday-2.1.0/testdata/Inline HTML comments.text000066400000000000000000000002441374571415200222660ustar00rootroot00000000000000Paragraph one. Paragraph two. The end. blackfriday-2.1.0/testdata/Links, inline style.html000066400000000000000000000006151374571415200222130ustar00rootroot00000000000000

    Just a URL.

    URL and title.

    URL and title.

    URL and title.

    URL and title.

    URL with backslash\.

    [Empty]().

    blackfriday-2.1.0/testdata/Links, inline style.text000066400000000000000000000004061374571415200222310ustar00rootroot00000000000000Just a [URL](/url/). [URL and title](/url/ "title"). [URL and title](/url/ "title preceded by two spaces"). [URL and title](/url/ "title preceded by a tab"). [URL and title](/url/ "title has spaces afterward" ). [URL with backslash\\](/url/). [Empty](). blackfriday-2.1.0/testdata/Links, reference style.html000066400000000000000000000021401374571415200226660ustar00rootroot00000000000000

    Foo bar.

    Foo bar.

    Foo bar.

    With embedded [brackets].

    Indented once.

    Indented twice.

    Indented thrice.

    Indented [four][] times.

    [four]: /url
    

    this should work

    So should this.

    And this.

    And this.

    And this.

    But not [that] [].

    Nor [that][].

    Nor [that].

    [Something in brackets like this should work]

    [Same with this.]

    In this case, this points to something else.

    Backslashing should suppress [this] and [this].


    Here's one where the link breaks across lines.

    Here's another where the link breaks across lines, but with a line-ending space.

    blackfriday-2.1.0/testdata/Links, reference style.text000066400000000000000000000014271374571415200227150ustar00rootroot00000000000000Foo [bar] [1]. Foo [bar][1]. Foo [bar] [1]. [1]: /url/ "Title" With [embedded [brackets]] [b]. Indented [once][]. Indented [twice][]. Indented [thrice][]. Indented [four][] times. [once]: /url [twice]: /url [thrice]: /url [four]: /url [b]: /url/ * * * [this] [this] should work So should [this][this]. And [this] []. And [this][]. And [this]. But not [that] []. Nor [that][]. Nor [that]. [Something in brackets like [this][] should work] [Same with [this].] In this case, [this](/somethingelse/) points to something else. Backslashing should suppress \[this] and [this\]. [this]: foo * * * Here's one where the [link breaks] across lines. Here's another where the [link breaks] across lines, but with a line-ending space. [link breaks]: /url/ blackfriday-2.1.0/testdata/Links, shortcut references.html000066400000000000000000000003771374571415200235760ustar00rootroot00000000000000

    This is the simple case.

    This one has a line break.

    This one has a line break with a line-ending space.

    this and the other

    blackfriday-2.1.0/testdata/Links, shortcut references.text000066400000000000000000000003541374571415200236110ustar00rootroot00000000000000This is the [simple case]. [simple case]: /simple This one has a [line break]. This one has a [line break] with a line-ending space. [line break]: /foo [this] [that] and the [other] [this]: /this [that]: /that [other]: /other blackfriday-2.1.0/testdata/Literal quotes in titles.html000066400000000000000000000002431374571415200232450ustar00rootroot00000000000000

    Foo bar.

    Foo bar.

    blackfriday-2.1.0/testdata/Literal quotes in titles.text000066400000000000000000000001541374571415200232660ustar00rootroot00000000000000Foo [bar][]. Foo [bar](/url/ "Title with "quotes" inside"). [bar]: /url/ "Title with "quotes" inside" blackfriday-2.1.0/testdata/Markdown Documentation - Basics.html000066400000000000000000000233361374571415200243620ustar00rootroot00000000000000

    Markdown: Basics

    Getting the Gist of Markdown's Formatting Syntax

    This page offers a brief overview of what it's like to use Markdown. The syntax page provides complete, detailed documentation for every feature, but Markdown should be very easy to pick up simply by looking at a few examples of it in action. The examples on this page are written in a before/after style, showing example syntax and the HTML output produced by Markdown.

    It's also helpful to simply try Markdown out; the Dingus is a web application that allows you type your own Markdown-formatted text and translate it to XHTML.

    Note: This document is itself written using Markdown; you can see the source for it by adding '.text' to the URL.

    Paragraphs, Headers, Blockquotes

    A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs.

    Markdown offers two styles of headers: Setext and atx. Setext-style headers for <h1> and <h2> are created by "underlining" with equal signs (=) and hyphens (-), respectively. To create an atx-style header, you put 1-6 hash marks (#) at the beginning of the line -- the number of hashes equals the resulting HTML header level.

    Blockquotes are indicated using email-style '>' angle brackets.

    Markdown:

    A First Level Header
    ====================
    
    A Second Level Header
    ---------------------
    
    Now is the time for all good men to come to
    the aid of their country. This is just a
    regular paragraph.
    
    The quick brown fox jumped over the lazy
    dog's back.
    
    ### Header 3
    
    > This is a blockquote.
    > 
    > This is the second paragraph in the blockquote.
    >
    > ## This is an H2 in a blockquote
    

    Output:

    <h1>A First Level Header</h1>
    
    <h2>A Second Level Header</h2>
    
    <p>Now is the time for all good men to come to
    the aid of their country. This is just a
    regular paragraph.</p>
    
    <p>The quick brown fox jumped over the lazy
    dog's back.</p>
    
    <h3>Header 3</h3>
    
    <blockquote>
        <p>This is a blockquote.</p>
    
        <p>This is the second paragraph in the blockquote.</p>
    
        <h2>This is an H2 in a blockquote</h2>
    </blockquote>
    

    Phrase Emphasis

    Markdown uses asterisks and underscores to indicate spans of emphasis.

    Markdown:

    Some of these words *are emphasized*.
    Some of these words _are emphasized also_.
    
    Use two asterisks for **strong emphasis**.
    Or, if you prefer, __use two underscores instead__.
    

    Output:

    <p>Some of these words <em>are emphasized</em>.
    Some of these words <em>are emphasized also</em>.</p>
    
    <p>Use two asterisks for <strong>strong emphasis</strong>.
    Or, if you prefer, <strong>use two underscores instead</strong>.</p>
    

    Lists

    Unordered (bulleted) lists use asterisks, pluses, and hyphens (*, +, and -) as list markers. These three markers are interchangable; this:

    *   Candy.
    *   Gum.
    *   Booze.
    

    this:

    +   Candy.
    +   Gum.
    +   Booze.
    

    and this:

    -   Candy.
    -   Gum.
    -   Booze.
    

    all produce the same output:

    <ul>
    <li>Candy.</li>
    <li>Gum.</li>
    <li>Booze.</li>
    </ul>
    

    Ordered (numbered) lists use regular numbers, followed by periods, as list markers:

    1.  Red
    2.  Green
    3.  Blue
    

    Output:

    <ol>
    <li>Red</li>
    <li>Green</li>
    <li>Blue</li>
    </ol>
    

    If you put blank lines between items, you'll get <p> tags for the list item text. You can create multi-paragraph list items by indenting the paragraphs by 4 spaces or 1 tab:

    *   A list item.
    
        With multiple paragraphs.
    
    *   Another item in the list.
    

    Output:

    <ul>
    <li><p>A list item.</p>
    <p>With multiple paragraphs.</p></li>
    <li><p>Another item in the list.</p></li>
    </ul>
    

    Links

    Markdown supports two styles for creating links: inline and reference. With both styles, you use square brackets to delimit the text you want to turn into a link.

    Inline-style links use parentheses immediately after the link text. For example:

    This is an [example link](http://example.com/).
    

    Output:

    <p>This is an <a href="http://example.com/">
    example link</a>.</p>
    

    Optionally, you may include a title attribute in the parentheses:

    This is an [example link](http://example.com/ "With a Title").
    

    Output:

    <p>This is an <a href="http://example.com/" title="With a Title">
    example link</a>.</p>
    

    Reference-style links allow you to refer to your links by names, which you define elsewhere in your document:

    I get 10 times more traffic from [Google][1] than from
    [Yahoo][2] or [MSN][3].
    
    [1]: http://google.com/        "Google"
    [2]: http://search.yahoo.com/  "Yahoo Search"
    [3]: http://search.msn.com/    "MSN Search"
    

    Output:

    <p>I get 10 times more traffic from <a href="http://google.com/"
    title="Google">Google</a> than from <a href="http://search.yahoo.com/"
    title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/"
    title="MSN Search">MSN</a>.</p>
    

    The title attribute is optional. Link names may contain letters, numbers and spaces, but are not case sensitive:

    I start my morning with a cup of coffee and
    [The New York Times][NY Times].
    
    [ny times]: http://www.nytimes.com/
    

    Output:

    <p>I start my morning with a cup of coffee and
    <a href="http://www.nytimes.com/">The New York Times</a>.</p>
    

    It is also common to find other protocols such as ftp used for links:

    Input:

    For example one may test download speeds [here](ftp://speedtest.tele2.net/)
    

    Output:

    <p>For example one may test download speeds <a href="ftp://speedtest.tele2.net/">here</a>.</p>
    

    Images

    Image syntax is very much like link syntax.

    Inline (titles are optional):

    ![alt text](/path/to/img.jpg "Title")
    

    Reference-style:

    ![alt text][id]
    
    [id]: /path/to/img.jpg "Title"
    

    Both of the above examples produce the same output:

    <img src="/path/to/img.jpg" alt="alt text" title="Title" />
    

    Code

    In a regular paragraph, you can create code span by wrapping text in backtick quotes. Any ampersands (&) and angle brackets (< or >) will automatically be translated into HTML entities. This makes it easy to use Markdown to write about HTML example code:

    I strongly recommend against using any `<blink>` tags.
    
    I wish SmartyPants used named entities like `&mdash;`
    instead of decimal-encoded entites like `&#8212;`.
    

    Output:

    <p>I strongly recommend against using any
    <code>&lt;blink&gt;</code> tags.</p>
    
    <p>I wish SmartyPants used named entities like
    <code>&amp;mdash;</code> instead of decimal-encoded
    entites like <code>&amp;#8212;</code>.</p>
    

    To specify an entire block of pre-formatted code, indent every line of the block by 4 spaces or 1 tab. Just like with code spans, &, <, and > characters will be escaped automatically.

    Markdown:

    If you want your page to validate under XHTML 1.0 Strict,
    you've got to put paragraph tags in your blockquotes:
    
        <blockquote>
            <p>For example.</p>
        </blockquote>
    

    Output:

    <p>If you want your page to validate under XHTML 1.0 Strict,
    you've got to put paragraph tags in your blockquotes:</p>
    
    <pre><code>&lt;blockquote&gt;
        &lt;p&gt;For example.&lt;/p&gt;
    &lt;/blockquote&gt;
    </code></pre>
    
    blackfriday-2.1.0/testdata/Markdown Documentation - Basics.text000066400000000000000000000202141374571415200243720ustar00rootroot00000000000000Markdown: Basics ================ Getting the Gist of Markdown's Formatting Syntax ------------------------------------------------ This page offers a brief overview of what it's like to use Markdown. The [syntax page] [s] provides complete, detailed documentation for every feature, but Markdown should be very easy to pick up simply by looking at a few examples of it in action. The examples on this page are written in a before/after style, showing example syntax and the HTML output produced by Markdown. It's also helpful to simply try Markdown out; the [Dingus] [d] is a web application that allows you type your own Markdown-formatted text and translate it to XHTML. **Note:** This document is itself written using Markdown; you can [see the source for it by adding '.text' to the URL] [src]. [s]: /projects/markdown/syntax "Markdown Syntax" [d]: /projects/markdown/dingus "Markdown Dingus" [src]: /projects/markdown/basics.text ## Paragraphs, Headers, Blockquotes ## A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs. Markdown offers two styles of headers: *Setext* and *atx*. Setext-style headers for `

    ` and `

    ` are created by "underlining" with equal signs (`=`) and hyphens (`-`), respectively. To create an atx-style header, you put 1-6 hash marks (`#`) at the beginning of the line -- the number of hashes equals the resulting HTML header level. Blockquotes are indicated using email-style '`>`' angle brackets. Markdown: A First Level Header ==================== A Second Level Header --------------------- Now is the time for all good men to come to the aid of their country. This is just a regular paragraph. The quick brown fox jumped over the lazy dog's back. ### Header 3 > This is a blockquote. > > This is the second paragraph in the blockquote. > > ## This is an H2 in a blockquote Output:

    A First Level Header

    A Second Level Header

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

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

    Header 3

    This is a blockquote.

    This is the second paragraph in the blockquote.

    This is an H2 in a blockquote

    ### Phrase Emphasis ### Markdown uses asterisks and underscores to indicate spans of emphasis. Markdown: Some of these words *are emphasized*. Some of these words _are emphasized also_. Use two asterisks for **strong emphasis**. Or, if you prefer, __use two underscores instead__. Output:

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

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

    ## Lists ## Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`, `+`, and `-`) as list markers. These three markers are interchangable; this: * Candy. * Gum. * Booze. this: + Candy. + Gum. + Booze. and this: - Candy. - Gum. - Booze. all produce the same output:
    • Candy.
    • Gum.
    • Booze.
    Ordered (numbered) lists use regular numbers, followed by periods, as list markers: 1. Red 2. Green 3. Blue Output:
    1. Red
    2. Green
    3. Blue
    If you put blank lines between items, you'll get `

    ` tags for the list item text. You can create multi-paragraph list items by indenting the paragraphs by 4 spaces or 1 tab: * A list item. With multiple paragraphs. * Another item in the list. Output:

    • A list item.

      With multiple paragraphs.

    • Another item in the list.

    ### Links ### Markdown supports two styles for creating links: *inline* and *reference*. With both styles, you use square brackets to delimit the text you want to turn into a link. Inline-style links use parentheses immediately after the link text. For example: This is an [example link](http://example.com/). Output:

    This is an example link.

    Optionally, you may include a title attribute in the parentheses: This is an [example link](http://example.com/ "With a Title"). Output:

    This is an example link.

    Reference-style links allow you to refer to your links by names, which you define elsewhere in your document: I get 10 times more traffic from [Google][1] than from [Yahoo][2] or [MSN][3]. [1]: http://google.com/ "Google" [2]: http://search.yahoo.com/ "Yahoo Search" [3]: http://search.msn.com/ "MSN Search" Output:

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

    The title attribute is optional. Link names may contain letters, numbers and spaces, but are *not* case sensitive: I start my morning with a cup of coffee and [The New York Times][NY Times]. [ny times]: http://www.nytimes.com/ Output:

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

    It is also common to find other protocols such as ftp used for links: Input: For example one may test download speeds [here](ftp://speedtest.tele2.net/) Output:

    For example one may test download speeds here.

    ### Images ### Image syntax is very much like link syntax. Inline (titles are optional): ![alt text](/path/to/img.jpg "Title") Reference-style: ![alt text][id] [id]: /path/to/img.jpg "Title" Both of the above examples produce the same output: alt text ### Code ### In a regular paragraph, you can create code span by wrapping text in backtick quotes. Any ampersands (`&`) and angle brackets (`<` or `>`) will automatically be translated into HTML entities. This makes it easy to use Markdown to write about HTML example code: I strongly recommend against using any `` tags. I wish SmartyPants used named entities like `—` instead of decimal-encoded entites like `—`. Output:

    I strongly recommend against using any <blink> tags.

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

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

    For example.

    Output:

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

    <blockquote>
            <p>For example.</p>
        </blockquote>
        
    blackfriday-2.1.0/testdata/Markdown Documentation - Syntax.html000066400000000000000000000767051374571415200244540ustar00rootroot00000000000000

    Markdown: Syntax

    Note: This document is itself written using Markdown; you can see the source for it by adding '.text' to the URL.


    Overview

    Philosophy

    Markdown is intended to be as easy-to-read and easy-to-write as is feasible.

    Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it's been marked up with tags or formatting instructions. While Markdown's syntax has been influenced by several existing text-to-HTML filters -- including Setext, atx, Textile, reStructuredText, Grutatext, and EtText -- the single biggest source of inspiration for Markdown's syntax is the format of plain text email.

    To this end, Markdown's syntax is comprised entirely of punctuation characters, which punctuation characters have been carefully chosen so as to look like what they mean. E.g., asterisks around a word actually look like *emphasis*. Markdown lists look like, well, lists. Even blockquotes look like quoted passages of text, assuming you've ever used email.

    Inline HTML

    Markdown's syntax is intended for one purpose: to be used as a format for writing for the web.

    Markdown is not a replacement for HTML, or even close to it. Its syntax is very small, corresponding only to a very small subset of HTML tags. The idea is not to create a syntax that makes it easier to insert HTML tags. In my opinion, HTML tags are already easy to insert. The idea for Markdown is to make it easy to read, write, and edit prose. HTML is a publishing format; Markdown is a writing format. Thus, Markdown's formatting syntax only addresses issues that can be conveyed in plain text.

    For any markup that is not covered by Markdown's syntax, you simply use HTML itself. There's no need to preface it or delimit it to indicate that you're switching from Markdown to HTML; you just use the tags.

    The only restrictions are that block-level HTML elements -- e.g. <div>, <table>, <pre>, <p>, etc. -- must be separated from surrounding content by blank lines, and the start and end tags of the block should not be indented with tabs or spaces. Markdown is smart enough not to add extra (unwanted) <p> tags around HTML block-level tags.

    For example, to add an HTML table to a Markdown article:

    This is a regular paragraph.
    
    <table>
        <tr>
            <td>Foo</td>
        </tr>
    </table>
    
    This is another regular paragraph.
    

    Note that Markdown formatting syntax is not processed within block-level HTML tags. E.g., you can't use Markdown-style *emphasis* inside an HTML block.

    Span-level HTML tags -- e.g. <span>, <cite>, or <del> -- can be used anywhere in a Markdown paragraph, list item, or header. If you want, you can even use HTML tags instead of Markdown formatting; e.g. if you'd prefer to use HTML <a> or <img> tags instead of Markdown's link or image syntax, go right ahead.

    Unlike block-level HTML tags, Markdown syntax is processed within span-level tags.

    Automatic Escaping for Special Characters

    In HTML, there are two characters that demand special treatment: < and &. Left angle brackets are used to start tags; ampersands are used to denote HTML entities. If you want to use them as literal characters, you must escape them as entities, e.g. &lt;, and &amp;.

    Ampersands in particular are bedeviling for web writers. If you want to write about 'AT&T', you need to write 'AT&amp;T'. You even need to escape ampersands within URLs. Thus, if you want to link to:

    http://images.google.com/images?num=30&q=larry+bird
    

    you need to encode the URL as:

    http://images.google.com/images?num=30&amp;q=larry+bird
    

    in your anchor tag href attribute. Needless to say, this is easy to forget, and is probably the single most common source of HTML validation errors in otherwise well-marked-up web sites.

    Markdown allows you to use these characters naturally, taking care of all the necessary escaping for you. If you use an ampersand as part of an HTML entity, it remains unchanged; otherwise it will be translated into &amp;.

    So, if you want to include a copyright symbol in your article, you can write:

    &copy;
    

    and Markdown will leave it alone. But if you write:

    AT&T
    

    Markdown will translate it to:

    AT&amp;T
    

    Similarly, because Markdown supports inline HTML, if you use angle brackets as delimiters for HTML tags, Markdown will treat them as such. But if you write:

    4 < 5
    

    Markdown will translate it to:

    4 &lt; 5
    

    However, inside Markdown code spans and blocks, angle brackets and ampersands are always encoded automatically. This makes it easy to use Markdown to write about HTML code. (As opposed to raw HTML, which is a terrible format for writing about HTML syntax, because every single < and & in your example code needs to be escaped.)


    Block Elements

    Paragraphs and Line Breaks

    A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs.

    The implication of the "one or more consecutive lines of text" rule is that Markdown supports "hard-wrapped" text paragraphs. This differs significantly from most other text-to-HTML formatters (including Movable Type's "Convert Line Breaks" option) which translate every line break character in a paragraph into a <br /> tag.

    When you do want to insert a <br /> break tag using Markdown, you end a line with two or more spaces, then type return.

    Yes, this takes a tad more effort to create a <br />, but a simplistic "every line break is a <br />" rule wouldn't work for Markdown. Markdown's email-style blockquoting and multi-paragraph list items work best -- and look better -- when you format them with hard breaks.

    Markdown supports two styles of headers, Setext and atx.

    Setext-style headers are "underlined" using equal signs (for first-level headers) and dashes (for second-level headers). For example:

    This is an H1
    =============
    
    This is an H2
    -------------
    

    Any number of underlining ='s or -'s will work.

    Atx-style headers use 1-6 hash characters at the start of the line, corresponding to header levels 1-6. For example:

    # This is an H1
    
    ## This is an H2
    
    ###### This is an H6
    

    Optionally, you may "close" atx-style headers. This is purely cosmetic -- you can use this if you think it looks better. The closing hashes don't even need to match the number of hashes used to open the header. (The number of opening hashes determines the header level.) :

    # This is an H1 #
    
    ## This is an H2 ##
    
    ### This is an H3 ######
    

    Blockquotes

    Markdown uses email-style > characters for blockquoting. If you're familiar with quoting passages of text in an email message, then you know how to create a blockquote in Markdown. It looks best if you hard wrap the text and put a > before every line:

    > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
    > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
    > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
    > 
    > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
    > id sem consectetuer libero luctus adipiscing.
    

    Markdown allows you to be lazy and only put the > before the first line of a hard-wrapped paragraph:

    > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
    consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
    Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
    
    > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
    id sem consectetuer libero luctus adipiscing.
    

    Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by adding additional levels of >:

    > This is the first level of quoting.
    >
    > > This is nested blockquote.
    >
    > Back to the first level.
    

    Blockquotes can contain other Markdown elements, including headers, lists, and code blocks:

    > ## This is a header.
    > 
    > 1.   This is the first list item.
    > 2.   This is the second list item.
    > 
    > Here's some example code:
    > 
    >     return shell_exec("echo $input | $markdown_script");
    

    Any decent text editor should make email-style quoting easy. For example, with BBEdit, you can make a selection and choose Increase Quote Level from the Text menu.

    Lists

    Markdown supports ordered (numbered) and unordered (bulleted) lists.

    Unordered lists use asterisks, pluses, and hyphens -- interchangably -- as list markers:

    *   Red
    *   Green
    *   Blue
    

    is equivalent to:

    +   Red
    +   Green
    +   Blue
    

    and:

    -   Red
    -   Green
    -   Blue
    

    Ordered lists use numbers followed by periods:

    1.  Bird
    2.  McHale
    3.  Parish
    

    It's important to note that the actual numbers you use to mark the list have no effect on the HTML output Markdown produces. The HTML Markdown produces from the above list is:

    <ol>
    <li>Bird</li>
    <li>McHale</li>
    <li>Parish</li>
    </ol>
    

    If you instead wrote the list in Markdown like this:

    1.  Bird
    1.  McHale
    1.  Parish
    

    or even:

    3. Bird
    1. McHale
    8. Parish
    

    you'd get the exact same HTML output. The point is, if you want to, you can use ordinal numbers in your ordered Markdown lists, so that the numbers in your source match the numbers in your published HTML. But if you want to be lazy, you don't have to.

    If you do use lazy list numbering, however, you should still start the list with the number 1. At some point in the future, Markdown may support starting ordered lists at an arbitrary number.

    List markers typically start at the left margin, but may be indented by up to three spaces. List markers must be followed by one or more spaces or a tab.

    To make lists look nice, you can wrap items with hanging indents:

    *   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
        Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
        viverra nec, fringilla in, laoreet vitae, risus.
    *   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
        Suspendisse id sem consectetuer libero luctus adipiscing.
    

    But if you want to be lazy, you don't have to:

    *   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
    viverra nec, fringilla in, laoreet vitae, risus.
    *   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
    Suspendisse id sem consectetuer libero luctus adipiscing.
    

    If list items are separated by blank lines, Markdown will wrap the items in <p> tags in the HTML output. For example, this input:

    *   Bird
    *   Magic
    

    will turn into:

    <ul>
    <li>Bird</li>
    <li>Magic</li>
    </ul>
    

    But this:

    *   Bird
    
    *   Magic
    

    will turn into:

    <ul>
    <li><p>Bird</p></li>
    <li><p>Magic</p></li>
    </ul>
    

    List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be intended by either 4 spaces or one tab:

    1.  This is a list item with two paragraphs. Lorem ipsum dolor
        sit amet, consectetuer adipiscing elit. Aliquam hendrerit
        mi posuere lectus.
    
        Vestibulum enim wisi, viverra nec, fringilla in, laoreet
        vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
        sit amet velit.
    
    2.  Suspendisse id sem consectetuer libero luctus adipiscing.
    

    It looks nice if you indent every line of the subsequent paragraphs, but here again, Markdown will allow you to be lazy:

    *   This is a list item with two paragraphs.
    
        This is the second paragraph in the list item. You're
    only required to indent the first line. Lorem ipsum dolor
    sit amet, consectetuer adipiscing elit.
    
    *   Another item in the same list.
    

    To put a blockquote within a list item, the blockquote's > delimiters need to be indented:

    *   A list item with a blockquote:
    
        > This is a blockquote
        > inside a list item.
    

    To put a code block within a list item, the code block needs to be indented twice -- 8 spaces or two tabs:

    *   A list item with a code block:
    
            <code goes here>
    

    It's worth noting that it's possible to trigger an ordered list by accident, by writing something like this:

    1986. What a great season.
    

    In other words, a number-period-space sequence at the beginning of a line. To avoid this, you can backslash-escape the period:

    1986\. What a great season.
    

    Code Blocks

    Pre-formatted code blocks are used for writing about programming or markup source code. Rather than forming normal paragraphs, the lines of a code block are interpreted literally. Markdown wraps a code block in both <pre> and <code> tags.

    To produce a code block in Markdown, simply indent every line of the block by at least 4 spaces or 1 tab. For example, given this input:

    This is a normal paragraph:
    
        This is a code block.
    

    Markdown will generate:

    <p>This is a normal paragraph:</p>
    
    <pre><code>This is a code block.
    </code></pre>
    

    One level of indentation -- 4 spaces or 1 tab -- is removed from each line of the code block. For example, this:

    Here is an example of AppleScript:
    
        tell application "Foo"
            beep
        end tell
    

    will turn into:

    <p>Here is an example of AppleScript:</p>
    
    <pre><code>tell application "Foo"
        beep
    end tell
    </code></pre>
    

    A code block continues until it reaches a line that is not indented (or the end of the article).

    Within a code block, ampersands (&) and angle brackets (< and >) are automatically converted into HTML entities. This makes it very easy to include example HTML source code using Markdown -- just paste it and indent it, and Markdown will handle the hassle of encoding the ampersands and angle brackets. For example, this:

        <div class="footer">
            &copy; 2004 Foo Corporation
        </div>
    

    will turn into:

    <pre><code>&lt;div class="footer"&gt;
        &amp;copy; 2004 Foo Corporation
    &lt;/div&gt;
    </code></pre>
    

    Regular Markdown syntax is not processed within code blocks. E.g., asterisks are just literal asterisks within a code block. This means it's also easy to use Markdown to write about Markdown's own syntax.

    Horizontal Rules

    You can produce a horizontal rule tag (<hr />) by placing three or more hyphens, asterisks, or underscores on a line by themselves. If you wish, you may use spaces between the hyphens or asterisks. Each of the following lines will produce a horizontal rule:

    * * *
    
    ***
    
    *****
    
    - - -
    
    ---------------------------------------
    
    _ _ _
    

    Span Elements

    Markdown supports two style of links: inline and reference.

    In both styles, the link text is delimited by [square brackets].

    To create an inline link, use a set of regular parentheses immediately after the link text's closing square bracket. Inside the parentheses, put the URL where you want the link to point, along with an optional title for the link, surrounded in quotes. For example:

    This is [an example](http://example.com/ "Title") inline link.
    
    [This link](http://example.net/) has no title attribute.
    

    Will produce:

    <p>This is <a href="http://example.com/" title="Title">
    an example</a> inline link.</p>
    
    <p><a href="http://example.net/">This link</a> has no
    title attribute.</p>
    

    If you're referring to a local resource on the same server, you can use relative paths:

    See my [About](/about/) page for details.
    

    Reference-style links use a second set of square brackets, inside which you place a label of your choosing to identify the link:

    This is [an example][id] reference-style link.
    

    You can optionally use a space to separate the sets of brackets:

    This is [an example] [id] reference-style link.
    

    Then, anywhere in the document, you define your link label like this, on a line by itself:

    [id]: http://example.com/  "Optional Title Here"
    

    That is:

    • Square brackets containing the link identifier (optionally indented from the left margin using up to three spaces);
    • followed by a colon;
    • followed by one or more spaces (or tabs);
    • followed by the URL for the link;
    • optionally followed by a title attribute for the link, enclosed in double or single quotes.

    The link URL may, optionally, be surrounded by angle brackets:

    [id]: <http://example.com/>  "Optional Title Here"
    

    You can put the title attribute on the next line and use extra spaces or tabs for padding, which tends to look better with longer URLs:

    [id]: http://example.com/longish/path/to/resource/here
        "Optional Title Here"
    

    Link definitions are only used for creating links during Markdown processing, and are stripped from your document in the HTML output.

    Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are not case sensitive. E.g. these two links:

    [link text][a]
    [link text][A]
    

    are equivalent.

    The implicit link name shortcut allows you to omit the name of the link, in which case the link text itself is used as the name. Just use an empty set of square brackets -- e.g., to link the word "Google" to the google.com web site, you could simply write:

    [Google][]
    

    And then define the link:

    [Google]: http://google.com/
    

    Because link names may contain spaces, this shortcut even works for multiple words in the link text:

    Visit [Daring Fireball][] for more information.
    

    And then define the link:

    [Daring Fireball]: http://daringfireball.net/
    

    Link definitions can be placed anywhere in your Markdown document. I tend to put them immediately after each paragraph in which they're used, but if you want, you can put them all at the end of your document, sort of like footnotes.

    Here's an example of reference links in action:

    I get 10 times more traffic from [Google] [1] than from
    [Yahoo] [2] or [MSN] [3].
    
      [1]: http://google.com/        "Google"
      [2]: http://search.yahoo.com/  "Yahoo Search"
      [3]: http://search.msn.com/    "MSN Search"
    

    Using the implicit link name shortcut, you could instead write:

    I get 10 times more traffic from [Google][] than from
    [Yahoo][] or [MSN][].
    
      [google]: http://google.com/        "Google"
      [yahoo]:  http://search.yahoo.com/  "Yahoo Search"
      [msn]:    http://search.msn.com/    "MSN Search"
    

    Both of the above examples will produce the following HTML output:

    <p>I get 10 times more traffic from <a href="http://google.com/"
    title="Google">Google</a> than from
    <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
    or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
    

    For comparison, here is the same paragraph written using Markdown's inline link style:

    I get 10 times more traffic from [Google](http://google.com/ "Google")
    than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
    [MSN](http://search.msn.com/ "MSN Search").
    

    The point of reference-style links is not that they're easier to write. The point is that with reference-style links, your document source is vastly more readable. Compare the above examples: using reference-style links, the paragraph itself is only 81 characters long; with inline-style links, it's 176 characters; and as raw HTML, it's 234 characters. In the raw HTML, there's more markup than there is text.

    With Markdown's reference-style links, a source document much more closely resembles the final output, as rendered in a browser. By allowing you to move the markup-related metadata out of the paragraph, you can add links without interrupting the narrative flow of your prose.

    Emphasis

    Markdown treats asterisks (*) and underscores (_) as indicators of emphasis. Text wrapped with one * or _ will be wrapped with an HTML <em> tag; double *'s or _'s will be wrapped with an HTML <strong> tag. E.g., this input:

    *single asterisks*
    
    _single underscores_
    
    **double asterisks**
    
    __double underscores__
    

    will produce:

    <em>single asterisks</em>
    
    <em>single underscores</em>
    
    <strong>double asterisks</strong>
    
    <strong>double underscores</strong>
    

    You can use whichever style you prefer; the lone restriction is that the same character must be used to open and close an emphasis span.

    Emphasis can be used in the middle of a word:

    un*fucking*believable
    

    But if you surround an * or _ with spaces, it'll be treated as a literal asterisk or underscore.

    To produce a literal asterisk or underscore at a position where it would otherwise be used as an emphasis delimiter, you can backslash escape it:

    \*this text is surrounded by literal asterisks\*
    

    Code

    To indicate a span of code, wrap it with backtick quotes (`). Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example:

    Use the `printf()` function.
    

    will produce:

    <p>Use the <code>printf()</code> function.</p>
    

    To include a literal backtick character within a code span, you can use multiple backticks as the opening and closing delimiters:

    ``There is a literal backtick (`) here.``
    

    which will produce this:

    <p><code>There is a literal backtick (`) here.</code></p>
    

    The backtick delimiters surrounding a code span may include spaces -- one after the opening, one before the closing. This allows you to place literal backtick characters at the beginning or end of a code span:

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

    will produce:

    <p>A single backtick in a code span: <code>`</code></p>
    
    <p>A backtick-delimited string in a code span: <code>`foo`</code></p>
    

    With a code span, ampersands and angle brackets are encoded as HTML entities automatically, which makes it easy to include example HTML tags. Markdown will turn this:

    Please don't use any `<blink>` tags.
    

    into:

    <p>Please don't use any <code>&lt;blink&gt;</code> tags.</p>
    

    You can write this:

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

    to produce:

    <p><code>&amp;#8212;</code> is the decimal-encoded
    equivalent of <code>&amp;mdash;</code>.</p>
    

    Images

    Admittedly, it's fairly difficult to devise a "natural" syntax for placing images into a plain text document format.

    Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: inline and reference.

    Inline image syntax looks like this:

    ![Alt text](/path/to/img.jpg)
    
    ![Alt text](/path/to/img.jpg "Optional title")
    

    That is:

    • An exclamation mark: !;
    • followed by a set of square brackets, containing the alt attribute text for the image;
    • followed by a set of parentheses, containing the URL or path to the image, and an optional title attribute enclosed in double or single quotes.

    Reference-style image syntax looks like this:

    ![Alt text][id]
    

    Where "id" is the name of a defined image reference. Image references are defined using syntax identical to link references:

    [id]: url/to/image  "Optional title attribute"
    

    As of this writing, Markdown has no syntax for specifying the dimensions of an image; if this is important to you, you can simply use regular HTML <img> tags.


    Miscellaneous

    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:

    <http://example.com/>
    

    Markdown will turn this into:

    <a href="http://example.com/">http://example.com/</a>
    

    Automatic links for email addresses work similarly, except that Markdown will also perform a bit of randomized decimal and hex entity-encoding to help obscure your address from address-harvesting spambots. For example, Markdown will turn this:

    <address@example.com>
    

    into something like this:

    <a href="&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:&#x61;&#x64;&#x64;&#x72;&#x65;
    &#115;&#115;&#64;&#101;&#120;&#x61;&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;
    &#109;">&#x61;&#x64;&#x64;&#x72;&#x65;&#115;&#115;&#64;&#101;&#120;&#x61;
    &#109;&#x70;&#x6C;e&#x2E;&#99;&#111;&#109;</a>
    

    which will render in a browser as a clickable link to "address@example.com".

    (This sort of entity-encoding trick will indeed fool many, if not most, address-harvesting bots, but it definitely won't fool all of them. It's better than nothing, but an address published in this way will probably eventually start receiving spam.)

    Backslash Escapes

    Markdown allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown's formatting syntax. For example, if you wanted to surround a word with literal asterisks (instead of an HTML <em> tag), you can backslashes before the asterisks, like this:

    \*literal asterisks\*
    

    Markdown provides backslash escapes for the following characters:

    \   backslash
    `   backtick
    *   asterisk
    _   underscore
    {}  curly braces
    []  square brackets
    ()  parentheses
    #   hash mark
    +	plus sign
    -	minus sign (hyphen)
    .   dot
    !   exclamation mark
    
    blackfriday-2.1.0/testdata/Markdown Documentation - Syntax.text000066400000000000000000000654441374571415200244720ustar00rootroot00000000000000Markdown: Syntax ================ * [Overview](#overview) * [Philosophy](#philosophy) * [Inline HTML](#html) * [Automatic Escaping for Special Characters](#autoescape) * [Block Elements](#block) * [Paragraphs and Line Breaks](#p) * [Headers](#header) * [Blockquotes](#blockquote) * [Lists](#list) * [Code Blocks](#precode) * [Horizontal Rules](#hr) * [Span Elements](#span) * [Links](#link) * [Emphasis](#em) * [Code](#code) * [Images](#img) * [Miscellaneous](#misc) * [Backslash Escapes](#backslash) * [Automatic Links](#autolink) **Note:** This document is itself written using Markdown; you can [see the source for it by adding '.text' to the URL][src]. [src]: /projects/markdown/syntax.text * * *

    Overview

    Philosophy

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

    Inline HTML

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

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

    ` tags around HTML block-level tags. For example, to add an HTML table to a Markdown article: This is a regular paragraph.

    Foo
    This is another regular paragraph. Note that Markdown formatting syntax is not processed within block-level HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an HTML block. Span-level HTML tags -- e.g. ``, ``, or `` -- can be used anywhere in a Markdown paragraph, list item, or header. If you want, you can even use HTML tags instead of Markdown formatting; e.g. if you'd prefer to use HTML `` or `` tags instead of Markdown's link or image syntax, go right ahead. Unlike block-level HTML tags, Markdown syntax *is* processed within span-level tags.

    Automatic Escaping for Special Characters

    In HTML, there are two characters that demand special treatment: `<` and `&`. Left angle brackets are used to start tags; ampersands are used to denote HTML entities. If you want to use them as literal characters, you must escape them as entities, e.g. `<`, and `&`. Ampersands in particular are bedeviling for web writers. If you want to write about 'AT&T', you need to write '`AT&T`'. You even need to escape ampersands within URLs. Thus, if you want to link to: http://images.google.com/images?num=30&q=larry+bird you need to encode the URL as: http://images.google.com/images?num=30&q=larry+bird in your anchor tag `href` attribute. Needless to say, this is easy to forget, and is probably the single most common source of HTML validation errors in otherwise well-marked-up web sites. Markdown allows you to use these characters naturally, taking care of all the necessary escaping for you. If you use an ampersand as part of an HTML entity, it remains unchanged; otherwise it will be translated into `&`. So, if you want to include a copyright symbol in your article, you can write: © and Markdown will leave it alone. But if you write: AT&T Markdown will translate it to: AT&T Similarly, because Markdown supports [inline HTML](#html), if you use angle brackets as delimiters for HTML tags, Markdown will treat them as such. But if you write: 4 < 5 Markdown will translate it to: 4 < 5 However, inside Markdown code spans and blocks, angle brackets and ampersands are *always* encoded automatically. This makes it easy to use Markdown to write about HTML code. (As opposed to raw HTML, which is a terrible format for writing about HTML syntax, because every single `<` and `&` in your example code needs to be escaped.) * * *

    Block Elements

    Paragraphs and Line Breaks

    A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs. The implication of the "one or more consecutive lines of text" rule is that Markdown supports "hard-wrapped" text paragraphs. This differs significantly from most other text-to-HTML formatters (including Movable Type's "Convert Line Breaks" option) which translate every line break character in a paragraph into a `
    ` tag. When you *do* want to insert a `
    ` break tag using Markdown, you end a line with two or more spaces, then type return. Yes, this takes a tad more effort to create a `
    `, but a simplistic "every line break is a `
    `" rule wouldn't work for Markdown. Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] work best -- and look better -- when you format them with hard breaks. [bq]: #blockquote [l]: #list Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. Setext-style headers are "underlined" using equal signs (for first-level headers) and dashes (for second-level headers). For example: This is an H1 ============= This is an H2 ------------- Any number of underlining `=`'s or `-`'s will work. Atx-style headers use 1-6 hash characters at the start of the line, corresponding to header levels 1-6. For example: # This is an H1 ## This is an H2 ###### This is an H6 Optionally, you may "close" atx-style headers. This is purely cosmetic -- you can use this if you think it looks better. The closing hashes don't even need to match the number of hashes used to open the header. (The number of opening hashes determines the header level.) : # This is an H1 # ## This is an H2 ## ### This is an H3 ######

    Blockquotes

    Markdown uses email-style `>` characters for blockquoting. If you're familiar with quoting passages of text in an email message, then you know how to create a blockquote in Markdown. It looks best if you hard wrap the text and put a `>` before every line: > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. > > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse > id sem consectetuer libero luctus adipiscing. Markdown allows you to be lazy and only put the `>` before the first line of a hard-wrapped paragraph: > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by adding additional levels of `>`: > This is the first level of quoting. > > > This is nested blockquote. > > Back to the first level. Blockquotes can contain other Markdown elements, including headers, lists, and code blocks: > ## This is a header. > > 1. This is the first list item. > 2. This is the second list item. > > Here's some example code: > > return shell_exec("echo $input | $markdown_script"); Any decent text editor should make email-style quoting easy. For example, with BBEdit, you can make a selection and choose Increase Quote Level from the Text menu.

    Lists

    Markdown supports ordered (numbered) and unordered (bulleted) lists. Unordered lists use asterisks, pluses, and hyphens -- interchangably -- as list markers: * Red * Green * Blue is equivalent to: + Red + Green + Blue and: - Red - Green - Blue Ordered lists use numbers followed by periods: 1. Bird 2. McHale 3. Parish It's important to note that the actual numbers you use to mark the list have no effect on the HTML output Markdown produces. The HTML Markdown produces from the above list is:
    1. Bird
    2. McHale
    3. Parish
    If you instead wrote the list in Markdown like this: 1. Bird 1. McHale 1. Parish or even: 3. Bird 1. McHale 8. Parish you'd get the exact same HTML output. The point is, if you want to, you can use ordinal numbers in your ordered Markdown lists, so that the numbers in your source match the numbers in your published HTML. But if you want to be lazy, you don't have to. If you do use lazy list numbering, however, you should still start the list with the number 1. At some point in the future, Markdown may support starting ordered lists at an arbitrary number. List markers typically start at the left margin, but may be indented by up to three spaces. List markers must be followed by one or more spaces or a tab. To make lists look nice, you can wrap items with hanging indents: * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. But if you want to be lazy, you don't have to: * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. If list items are separated by blank lines, Markdown will wrap the items in `

    ` tags in the HTML output. For example, this input: * Bird * Magic will turn into:

    • Bird
    • Magic
    But this: * Bird * Magic will turn into:
    • Bird

    • Magic

    List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be intended by either 4 spaces or one tab: 1. This is a list item with two paragraphs. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 2. Suspendisse id sem consectetuer libero luctus adipiscing. It looks nice if you indent every line of the subsequent paragraphs, but here again, Markdown will allow you to be lazy: * This is a list item with two paragraphs. This is the second paragraph in the list item. You're only required to indent the first line. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. * Another item in the same list. To put a blockquote within a list item, the blockquote's `>` delimiters need to be indented: * A list item with a blockquote: > This is a blockquote > inside a list item. To put a code block within a list item, the code block needs to be indented *twice* -- 8 spaces or two tabs: * A list item with a code block: It's worth noting that it's possible to trigger an ordered list by accident, by writing something like this: 1986. What a great season. In other words, a *number-period-space* sequence at the beginning of a line. To avoid this, you can backslash-escape the period: 1986\. What a great season.

    Code Blocks

    Pre-formatted code blocks are used for writing about programming or markup source code. Rather than forming normal paragraphs, the lines of a code block are interpreted literally. Markdown wraps a code block in both `
    ` and `` tags.
    
    To produce a code block in Markdown, simply indent every line of the
    block by at least 4 spaces or 1 tab. For example, given this input:
    
        This is a normal paragraph:
    
            This is a code block.
    
    Markdown will generate:
    
        

    This is a normal paragraph:

    This is a code block.
        
    One level of indentation -- 4 spaces or 1 tab -- is removed from each line of the code block. For example, this: Here is an example of AppleScript: tell application "Foo" beep end tell will turn into:

    Here is an example of AppleScript:

    tell application "Foo"
            beep
        end tell
        
    A code block continues until it reaches a line that is not indented (or the end of the article). Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) are automatically converted into HTML entities. This makes it very easy to include example HTML source code using Markdown -- just paste it and indent it, and Markdown will handle the hassle of encoding the ampersands and angle brackets. For example, this: will turn into:
    <div class="footer">
            &copy; 2004 Foo Corporation
        </div>
        
    Regular Markdown syntax is not processed within code blocks. E.g., asterisks are just literal asterisks within a code block. This means it's also easy to use Markdown to write about Markdown's own syntax.

    Horizontal Rules

    You can produce a horizontal rule tag (`
    `) by placing three or more hyphens, asterisks, or underscores on a line by themselves. If you wish, you may use spaces between the hyphens or asterisks. Each of the following lines will produce a horizontal rule: * * * *** ***** - - - --------------------------------------- _ _ _ * * *

    Span Elements

    Markdown supports two style of links: *inline* and *reference*. In both styles, the link text is delimited by [square brackets]. To create an inline link, use a set of regular parentheses immediately after the link text's closing square bracket. Inside the parentheses, put the URL where you want the link to point, along with an *optional* title for the link, surrounded in quotes. For example: This is [an example](http://example.com/ "Title") inline link. [This link](http://example.net/) has no title attribute. Will produce:

    This is an example inline link.

    This link has no title attribute.

    If you're referring to a local resource on the same server, you can use relative paths: See my [About](/about/) page for details. Reference-style links use a second set of square brackets, inside which you place a label of your choosing to identify the link: This is [an example][id] reference-style link. You can optionally use a space to separate the sets of brackets: This is [an example] [id] reference-style link. Then, anywhere in the document, you define your link label like this, on a line by itself: [id]: http://example.com/ "Optional Title Here" That is: * Square brackets containing the link identifier (optionally indented from the left margin using up to three spaces); * followed by a colon; * followed by one or more spaces (or tabs); * followed by the URL for the link; * optionally followed by a title attribute for the link, enclosed in double or single quotes. The link URL may, optionally, be surrounded by angle brackets: [id]: "Optional Title Here" You can put the title attribute on the next line and use extra spaces or tabs for padding, which tends to look better with longer URLs: [id]: http://example.com/longish/path/to/resource/here "Optional Title Here" Link definitions are only used for creating links during Markdown processing, and are stripped from your document in the HTML output. Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links: [link text][a] [link text][A] are equivalent. The *implicit link name* shortcut allows you to omit the name of the link, in which case the link text itself is used as the name. Just use an empty set of square brackets -- e.g., to link the word "Google" to the google.com web site, you could simply write: [Google][] And then define the link: [Google]: http://google.com/ Because link names may contain spaces, this shortcut even works for multiple words in the link text: Visit [Daring Fireball][] for more information. And then define the link: [Daring Fireball]: http://daringfireball.net/ Link definitions can be placed anywhere in your Markdown document. I tend to put them immediately after each paragraph in which they're used, but if you want, you can put them all at the end of your document, sort of like footnotes. Here's an example of reference links in action: I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]. [1]: http://google.com/ "Google" [2]: http://search.yahoo.com/ "Yahoo Search" [3]: http://search.msn.com/ "MSN Search" Using the implicit link name shortcut, you could instead write: I get 10 times more traffic from [Google][] than from [Yahoo][] or [MSN][]. [google]: http://google.com/ "Google" [yahoo]: http://search.yahoo.com/ "Yahoo Search" [msn]: http://search.msn.com/ "MSN Search" Both of the above examples will produce the following HTML output:

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

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

    Emphasis

    Markdown treats asterisks (`*`) and underscores (`_`) as indicators of emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML `` tag. E.g., this input: *single asterisks* _single underscores_ **double asterisks** __double underscores__ will produce: single asterisks single underscores double asterisks double underscores You can use whichever style you prefer; the lone restriction is that the same character must be used to open and close an emphasis span. Emphasis can be used in the middle of a word: un*fucking*believable But if you surround an `*` or `_` with spaces, it'll be treated as a literal asterisk or underscore. To produce a literal asterisk or underscore at a position where it would otherwise be used as an emphasis delimiter, you can backslash escape it: \*this text is surrounded by literal asterisks\*

    Code

    To indicate a span of code, wrap it with backtick quotes (`` ` ``). Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example: Use the `printf()` function. will produce:

    Use the printf() function.

    To include a literal backtick character within a code span, you can use multiple backticks as the opening and closing delimiters: ``There is a literal backtick (`) here.`` which will produce this:

    There is a literal backtick (`) here.

    The backtick delimiters surrounding a code span may include spaces -- one after the opening, one before the closing. This allows you to place literal backtick characters at the beginning or end of a code span: A single backtick in a code span: `` ` `` A backtick-delimited string in a code span: `` `foo` `` will produce:

    A single backtick in a code span: `

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

    With a code span, ampersands and angle brackets are encoded as HTML entities automatically, which makes it easy to include example HTML tags. Markdown will turn this: Please don't use any `` tags. into:

    Please don't use any <blink> tags.

    You can write this: `—` is the decimal-encoded equivalent of `—`. to produce:

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

    Images

    Admittedly, it's fairly difficult to devise a "natural" syntax for placing images into a plain text document format. Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: *inline* and *reference*. Inline image syntax looks like this: ![Alt text](/path/to/img.jpg) ![Alt text](/path/to/img.jpg "Optional title") That is: * An exclamation mark: `!`; * followed by a set of square brackets, containing the `alt` attribute text for the image; * followed by a set of parentheses, containing the URL or path to the image, and an optional `title` attribute enclosed in double or single quotes. Reference-style image syntax looks like this: ![Alt text][id] Where "id" is the name of a defined image reference. Image references are defined using syntax identical to link references: [id]: url/to/image "Optional title attribute" As of this writing, Markdown has no syntax for specifying the dimensions of an image; if this is important to you, you can simply use regular HTML `` tags. * * *

    Miscellaneous

    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: Markdown will turn this into: http://example.com/ Automatic links for email addresses work similarly, except that Markdown will also perform a bit of randomized decimal and hex entity-encoding to help obscure your address from address-harvesting spambots. For example, Markdown will turn this: into something like this: address@exa mple.com which will render in a browser as a clickable link to "address@example.com". (This sort of entity-encoding trick will indeed fool many, if not most, address-harvesting bots, but it definitely won't fool all of them. It's better than nothing, but an address published in this way will probably eventually start receiving spam.)

    Backslash Escapes

    Markdown allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown's formatting syntax. For example, if you wanted to surround a word with literal asterisks (instead of an HTML `` tag), you can backslashes before the asterisks, like this: \*literal asterisks\* Markdown provides backslash escapes for the following characters: \ backslash ` backtick * asterisk _ underscore {} curly braces [] square brackets () parentheses # hash mark + plus sign - minus sign (hyphen) . dot ! exclamation mark blackfriday-2.1.0/testdata/Nested blockquotes.html000066400000000000000000000001311374571415200222260ustar00rootroot00000000000000

    foo

    bar

    foo

    blackfriday-2.1.0/testdata/Nested blockquotes.text000066400000000000000000000000301374571415200222440ustar00rootroot00000000000000> foo > > > bar > > foo blackfriday-2.1.0/testdata/Ordered and unordered lists.html000066400000000000000000000032631374571415200236770ustar00rootroot00000000000000

    Unordered

    Asterisks tight:

    • asterisk 1
    • asterisk 2
    • asterisk 3

    Asterisks loose:

    • asterisk 1

    • asterisk 2

    • asterisk 3


    Pluses tight:

    • Plus 1
    • Plus 2
    • Plus 3

    Pluses loose:

    • Plus 1

    • Plus 2

    • Plus 3


    Minuses tight:

    • Minus 1
    • Minus 2
    • Minus 3

    Minuses loose:

    • Minus 1

    • Minus 2

    • Minus 3

    Ordered

    Tight:

    1. First
    2. Second
    3. Third

    and:

    1. One
    2. Two
    3. Three

    Loose using tabs:

    1. First

    2. Second

    3. Third

    and using spaces:

    1. One

    2. Two

    3. Three

    Multiple paragraphs:

    1. Item 1, graf one.

      Item 2. graf two. The quick brown fox jumped over the lazy dog's back.

    2. Item 2.

    3. Item 3.

    Nested

    • Tab
      • Tab
        • Tab

    Here's another:

    1. First
    2. Second:
      • Fee
      • Fie
      • Foe
    3. Third

    Same thing but with paragraphs:

    1. First

    2. Second:

      • Fee
      • Fie
      • Foe
    3. Third

    This was an error in Markdown 1.0.1:

    • this

      • sub

      that

    blackfriday-2.1.0/testdata/Ordered and unordered lists.text000066400000000000000000000016071374571415200237170ustar00rootroot00000000000000## Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 * * * Pluses tight: + Plus 1 + Plus 2 + Plus 3 Pluses loose: + Plus 1 + Plus 2 + Plus 3 * * * Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 2. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. ## Nested * Tab * Tab * Tab Here's another: 1. First 2. Second: * Fee * Fie * Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: * Fee * Fie * Foe 3. Third This was an error in Markdown 1.0.1: * this * sub that blackfriday-2.1.0/testdata/Strong and em together.html000066400000000000000000000003271374571415200226620ustar00rootroot00000000000000

    This is strong and em.

    So is this word.

    This is strong and em.

    So is this word.

    blackfriday-2.1.0/testdata/Strong and em together.text000066400000000000000000000001531374571415200226770ustar00rootroot00000000000000***This is strong and em.*** So is ***this*** word. ___This is strong and em.___ So is ___this___ word. blackfriday-2.1.0/testdata/Tabs.html000066400000000000000000000006601374571415200173700ustar00rootroot00000000000000
    • this is a list item indented with tabs

    • this is a list item indented with spaces

    Code:

    this code block is indented by one tab
    

    And:

    	this code block is indented by two tabs
    

    And:

    +	this is an example list item
    	indented with tabs
    
    +   this is an example list item
        indented with spaces
    
    blackfriday-2.1.0/testdata/Tabs.text000066400000000000000000000004671374571415200174150ustar00rootroot00000000000000+ this is a list item indented with tabs + this is a list item indented with spaces Code: this code block is indented by one tab And: this code block is indented by two tabs And: + this is an example list item indented with tabs + this is an example list item indented with spaces blackfriday-2.1.0/testdata/Tidyness.html000066400000000000000000000002061374571415200202750ustar00rootroot00000000000000

    A list within a blockquote:

    • asterisk 1
    • asterisk 2
    • asterisk 3
    blackfriday-2.1.0/testdata/Tidyness.text000066400000000000000000000001161374571415200203150ustar00rootroot00000000000000> A list within a blockquote: > > * asterisk 1 > * asterisk 2 > * asterisk 3