markdown.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. // Blackfriday Markdown Processor
  2. // Available at http://github.com/russross/blackfriday
  3. //
  4. // Copyright © 2011 Russ Ross <russ@russross.com>.
  5. // Distributed under the Simplified BSD License.
  6. // See README.md for details.
  7. package blackfriday
  8. import (
  9. "bytes"
  10. "fmt"
  11. "io"
  12. "strings"
  13. "unicode/utf8"
  14. )
  15. //
  16. // Markdown parsing and processing
  17. //
  18. // Version string of the package. Appears in the rendered document when
  19. // CompletePage flag is on.
  20. const Version = "2.0"
  21. // Extensions is a bitwise or'ed collection of enabled Blackfriday's
  22. // extensions.
  23. type Extensions int
  24. // These are the supported markdown parsing extensions.
  25. // OR these values together to select multiple extensions.
  26. const (
  27. NoExtensions Extensions = 0
  28. NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words
  29. Tables // Render tables
  30. FencedCode // Render fenced code blocks
  31. Autolink // Detect embedded URLs that are not explicitly marked
  32. Strikethrough // Strikethrough text using ~~test~~
  33. LaxHTMLBlocks // Loosen up HTML block parsing rules
  34. SpaceHeadings // Be strict about prefix heading rules
  35. HardLineBreak // Translate newlines into line breaks
  36. TabSizeEight // Expand tabs to eight spaces instead of four
  37. Footnotes // Pandoc-style footnotes
  38. NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block
  39. HeadingIDs // specify heading IDs with {#id}
  40. Titleblock // Titleblock ala pandoc
  41. AutoHeadingIDs // Create the heading ID from the text
  42. BackslashLineBreak // Translate trailing backslashes into line breaks
  43. DefinitionLists // Render definition lists
  44. CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants |
  45. SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
  46. CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
  47. Autolink | Strikethrough | SpaceHeadings | HeadingIDs |
  48. BackslashLineBreak | DefinitionLists
  49. )
  50. // ListType contains bitwise or'ed flags for list and list item objects.
  51. type ListType int
  52. // These are the possible flag values for the ListItem renderer.
  53. // Multiple flag values may be ORed together.
  54. // These are mostly of interest if you are writing a new output format.
  55. const (
  56. ListTypeOrdered ListType = 1 << iota
  57. ListTypeDefinition
  58. ListTypeTerm
  59. ListItemContainsBlock
  60. ListItemBeginningOfList // TODO: figure out if this is of any use now
  61. ListItemEndOfList
  62. )
  63. // CellAlignFlags holds a type of alignment in a table cell.
  64. type CellAlignFlags int
  65. // These are the possible flag values for the table cell renderer.
  66. // Only a single one of these values will be used; they are not ORed together.
  67. // These are mostly of interest if you are writing a new output format.
  68. const (
  69. TableAlignmentLeft CellAlignFlags = 1 << iota
  70. TableAlignmentRight
  71. TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight)
  72. )
  73. // The size of a tab stop.
  74. const (
  75. TabSizeDefault = 4
  76. TabSizeDouble = 8
  77. )
  78. // blockTags is a set of tags that are recognized as HTML block tags.
  79. // Any of these can be included in markdown text without special escaping.
  80. var blockTags = map[string]struct{}{
  81. "blockquote": {},
  82. "del": {},
  83. "div": {},
  84. "dl": {},
  85. "fieldset": {},
  86. "form": {},
  87. "h1": {},
  88. "h2": {},
  89. "h3": {},
  90. "h4": {},
  91. "h5": {},
  92. "h6": {},
  93. "iframe": {},
  94. "ins": {},
  95. "math": {},
  96. "noscript": {},
  97. "ol": {},
  98. "pre": {},
  99. "p": {},
  100. "script": {},
  101. "style": {},
  102. "table": {},
  103. "ul": {},
  104. // HTML5
  105. "address": {},
  106. "article": {},
  107. "aside": {},
  108. "canvas": {},
  109. "figcaption": {},
  110. "figure": {},
  111. "footer": {},
  112. "header": {},
  113. "hgroup": {},
  114. "main": {},
  115. "nav": {},
  116. "output": {},
  117. "progress": {},
  118. "section": {},
  119. "video": {},
  120. }
  121. // Renderer is the rendering interface. This is mostly of interest if you are
  122. // implementing a new rendering format.
  123. //
  124. // Only an HTML implementation is provided in this repository, see the README
  125. // for external implementations.
  126. type Renderer interface {
  127. // RenderNode is the main rendering method. It will be called once for
  128. // every leaf node and twice for every non-leaf node (first with
  129. // entering=true, then with entering=false). The method should write its
  130. // rendition of the node to the supplied writer w.
  131. RenderNode(w io.Writer, node *Node, entering bool) WalkStatus
  132. // RenderHeader is a method that allows the renderer to produce some
  133. // content preceding the main body of the output document. The header is
  134. // understood in the broad sense here. For example, the default HTML
  135. // renderer will write not only the HTML document preamble, but also the
  136. // table of contents if it was requested.
  137. //
  138. // The method will be passed an entire document tree, in case a particular
  139. // implementation needs to inspect it to produce output.
  140. //
  141. // The output should be written to the supplied writer w. If your
  142. // implementation has no header to write, supply an empty implementation.
  143. RenderHeader(w io.Writer, ast *Node)
  144. // RenderFooter is a symmetric counterpart of RenderHeader.
  145. RenderFooter(w io.Writer, ast *Node)
  146. }
  147. // Callback functions for inline parsing. One such function is defined
  148. // for each character that triggers a response when parsing inline data.
  149. type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node)
  150. // Markdown is a type that holds extensions and the runtime state used by
  151. // Parse, and the renderer. You can not use it directly, construct it with New.
  152. type Markdown struct {
  153. renderer Renderer
  154. referenceOverride ReferenceOverrideFunc
  155. refs map[string]*reference
  156. inlineCallback [256]inlineParser
  157. extensions Extensions
  158. nesting int
  159. maxNesting int
  160. insideLink bool
  161. // Footnotes need to be ordered as well as available to quickly check for
  162. // presence. If a ref is also a footnote, it's stored both in refs and here
  163. // in notes. Slice is nil if footnotes not enabled.
  164. notes []*reference
  165. doc *Node
  166. tip *Node // = doc
  167. oldTip *Node
  168. lastMatchedContainer *Node // = doc
  169. allClosed bool
  170. }
  171. func (p *Markdown) getRef(refid string) (ref *reference, found bool) {
  172. if p.referenceOverride != nil {
  173. r, overridden := p.referenceOverride(refid)
  174. if overridden {
  175. if r == nil {
  176. return nil, false
  177. }
  178. return &reference{
  179. link: []byte(r.Link),
  180. title: []byte(r.Title),
  181. noteID: 0,
  182. hasBlock: false,
  183. text: []byte(r.Text)}, true
  184. }
  185. }
  186. // refs are case insensitive
  187. ref, found = p.refs[strings.ToLower(refid)]
  188. return ref, found
  189. }
  190. func (p *Markdown) finalize(block *Node) {
  191. above := block.Parent
  192. block.open = false
  193. p.tip = above
  194. }
  195. func (p *Markdown) addChild(node NodeType, offset uint32) *Node {
  196. return p.addExistingChild(NewNode(node), offset)
  197. }
  198. func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node {
  199. for !p.tip.canContain(node.Type) {
  200. p.finalize(p.tip)
  201. }
  202. p.tip.AppendChild(node)
  203. p.tip = node
  204. return node
  205. }
  206. func (p *Markdown) closeUnmatchedBlocks() {
  207. if !p.allClosed {
  208. for p.oldTip != p.lastMatchedContainer {
  209. parent := p.oldTip.Parent
  210. p.finalize(p.oldTip)
  211. p.oldTip = parent
  212. }
  213. p.allClosed = true
  214. }
  215. }
  216. //
  217. //
  218. // Public interface
  219. //
  220. //
  221. // Reference represents the details of a link.
  222. // See the documentation in Options for more details on use-case.
  223. type Reference struct {
  224. // Link is usually the URL the reference points to.
  225. Link string
  226. // Title is the alternate text describing the link in more detail.
  227. Title string
  228. // Text is the optional text to override the ref with if the syntax used was
  229. // [refid][]
  230. Text string
  231. }
  232. // ReferenceOverrideFunc is expected to be called with a reference string and
  233. // return either a valid Reference type that the reference string maps to or
  234. // nil. If overridden is false, the default reference logic will be executed.
  235. // See the documentation in Options for more details on use-case.
  236. type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool)
  237. // New constructs a Markdown processor. You can use the same With* functions as
  238. // for Run() to customize parser's behavior and the renderer.
  239. func New(opts ...Option) *Markdown {
  240. var p Markdown
  241. for _, opt := range opts {
  242. opt(&p)
  243. }
  244. p.refs = make(map[string]*reference)
  245. p.maxNesting = 16
  246. p.insideLink = false
  247. docNode := NewNode(Document)
  248. p.doc = docNode
  249. p.tip = docNode
  250. p.oldTip = docNode
  251. p.lastMatchedContainer = docNode
  252. p.allClosed = true
  253. // register inline parsers
  254. p.inlineCallback[' '] = maybeLineBreak
  255. p.inlineCallback['*'] = emphasis
  256. p.inlineCallback['_'] = emphasis
  257. if p.extensions&Strikethrough != 0 {
  258. p.inlineCallback['~'] = emphasis
  259. }
  260. p.inlineCallback['`'] = codeSpan
  261. p.inlineCallback['\n'] = lineBreak
  262. p.inlineCallback['['] = link
  263. p.inlineCallback['<'] = leftAngle
  264. p.inlineCallback['\\'] = escape
  265. p.inlineCallback['&'] = entity
  266. p.inlineCallback['!'] = maybeImage
  267. p.inlineCallback['^'] = maybeInlineFootnote
  268. if p.extensions&Autolink != 0 {
  269. p.inlineCallback['h'] = maybeAutoLink
  270. p.inlineCallback['m'] = maybeAutoLink
  271. p.inlineCallback['f'] = maybeAutoLink
  272. p.inlineCallback['H'] = maybeAutoLink
  273. p.inlineCallback['M'] = maybeAutoLink
  274. p.inlineCallback['F'] = maybeAutoLink
  275. }
  276. if p.extensions&Footnotes != 0 {
  277. p.notes = make([]*reference, 0)
  278. }
  279. return &p
  280. }
  281. // Option customizes the Markdown processor's default behavior.
  282. type Option func(*Markdown)
  283. // WithRenderer allows you to override the default renderer.
  284. func WithRenderer(r Renderer) Option {
  285. return func(p *Markdown) {
  286. p.renderer = r
  287. }
  288. }
  289. // WithExtensions allows you to pick some of the many extensions provided by
  290. // Blackfriday. You can bitwise OR them.
  291. func WithExtensions(e Extensions) Option {
  292. return func(p *Markdown) {
  293. p.extensions = e
  294. }
  295. }
  296. // WithNoExtensions turns off all extensions and custom behavior.
  297. func WithNoExtensions() Option {
  298. return func(p *Markdown) {
  299. p.extensions = NoExtensions
  300. p.renderer = NewHTMLRenderer(HTMLRendererParameters{
  301. Flags: HTMLFlagsNone,
  302. })
  303. }
  304. }
  305. // WithRefOverride sets an optional function callback that is called every
  306. // time a reference is resolved.
  307. //
  308. // In Markdown, the link reference syntax can be made to resolve a link to
  309. // a reference instead of an inline URL, in one of the following ways:
  310. //
  311. // * [link text][refid]
  312. // * [refid][]
  313. //
  314. // Usually, the refid is defined at the bottom of the Markdown document. If
  315. // this override function is provided, the refid is passed to the override
  316. // function first, before consulting the defined refids at the bottom. If
  317. // the override function indicates an override did not occur, the refids at
  318. // the bottom will be used to fill in the link details.
  319. func WithRefOverride(o ReferenceOverrideFunc) Option {
  320. return func(p *Markdown) {
  321. p.referenceOverride = o
  322. }
  323. }
  324. // Run is the main entry point to Blackfriday. It parses and renders a
  325. // block of markdown-encoded text.
  326. //
  327. // The simplest invocation of Run takes one argument, input:
  328. // output := Run(input)
  329. // This will parse the input with CommonExtensions enabled and render it with
  330. // the default HTMLRenderer (with CommonHTMLFlags).
  331. //
  332. // Variadic arguments opts can customize the default behavior. Since Markdown
  333. // type does not contain exported fields, you can not use it directly. Instead,
  334. // use the With* functions. For example, this will call the most basic
  335. // functionality, with no extensions:
  336. // output := Run(input, WithNoExtensions())
  337. //
  338. // You can use any number of With* arguments, even contradicting ones. They
  339. // will be applied in order of appearance and the latter will override the
  340. // former:
  341. // output := Run(input, WithNoExtensions(), WithExtensions(exts),
  342. // WithRenderer(yourRenderer))
  343. func Run(input []byte, opts ...Option) []byte {
  344. r := NewHTMLRenderer(HTMLRendererParameters{
  345. Flags: CommonHTMLFlags,
  346. })
  347. optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)}
  348. optList = append(optList, opts...)
  349. parser := New(optList...)
  350. ast := parser.Parse(input)
  351. var buf bytes.Buffer
  352. parser.renderer.RenderHeader(&buf, ast)
  353. ast.Walk(func(node *Node, entering bool) WalkStatus {
  354. return parser.renderer.RenderNode(&buf, node, entering)
  355. })
  356. parser.renderer.RenderFooter(&buf, ast)
  357. return buf.Bytes()
  358. }
  359. // Parse is an entry point to the parsing part of Blackfriday. It takes an
  360. // input markdown document and produces a syntax tree for its contents. This
  361. // tree can then be rendered with a default or custom renderer, or
  362. // analyzed/transformed by the caller to whatever non-standard needs they have.
  363. // The return value is the root node of the syntax tree.
  364. func (p *Markdown) Parse(input []byte) *Node {
  365. p.block(input)
  366. // Walk the tree and finish up some of unfinished blocks
  367. for p.tip != nil {
  368. p.finalize(p.tip)
  369. }
  370. // Walk the tree again and process inline markdown in each block
  371. p.doc.Walk(func(node *Node, entering bool) WalkStatus {
  372. if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell {
  373. p.inline(node, node.content)
  374. node.content = nil
  375. }
  376. return GoToNext
  377. })
  378. p.parseRefsToAST()
  379. return p.doc
  380. }
  381. func (p *Markdown) parseRefsToAST() {
  382. if p.extensions&Footnotes == 0 || len(p.notes) == 0 {
  383. return
  384. }
  385. p.tip = p.doc
  386. block := p.addBlock(List, nil)
  387. block.IsFootnotesList = true
  388. block.ListFlags = ListTypeOrdered
  389. flags := ListItemBeginningOfList
  390. // Note: this loop is intentionally explicit, not range-form. This is
  391. // because the body of the loop will append nested footnotes to p.notes and
  392. // we need to process those late additions. Range form would only walk over
  393. // the fixed initial set.
  394. for i := 0; i < len(p.notes); i++ {
  395. ref := p.notes[i]
  396. p.addExistingChild(ref.footnote, 0)
  397. block := ref.footnote
  398. block.ListFlags = flags | ListTypeOrdered
  399. block.RefLink = ref.link
  400. if ref.hasBlock {
  401. flags |= ListItemContainsBlock
  402. p.block(ref.title)
  403. } else {
  404. p.inline(block, ref.title)
  405. }
  406. flags &^= ListItemBeginningOfList | ListItemContainsBlock
  407. }
  408. above := block.Parent
  409. finalizeList(block)
  410. p.tip = above
  411. block.Walk(func(node *Node, entering bool) WalkStatus {
  412. if node.Type == Paragraph || node.Type == Heading {
  413. p.inline(node, node.content)
  414. node.content = nil
  415. }
  416. return GoToNext
  417. })
  418. }
  419. //
  420. // Link references
  421. //
  422. // This section implements support for references that (usually) appear
  423. // as footnotes in a document, and can be referenced anywhere in the document.
  424. // The basic format is:
  425. //
  426. // [1]: http://www.google.com/ "Google"
  427. // [2]: http://www.github.com/ "Github"
  428. //
  429. // Anywhere in the document, the reference can be linked by referring to its
  430. // label, i.e., 1 and 2 in this example, as in:
  431. //
  432. // This library is hosted on [Github][2], a git hosting site.
  433. //
  434. // Actual footnotes as specified in Pandoc and supported by some other Markdown
  435. // libraries such as php-markdown are also taken care of. They look like this:
  436. //
  437. // This sentence needs a bit of further explanation.[^note]
  438. //
  439. // [^note]: This is the explanation.
  440. //
  441. // Footnotes should be placed at the end of the document in an ordered list.
  442. // Finally, there are inline footnotes such as:
  443. //
  444. // Inline footnotes^[Also supported.] provide a quick inline explanation,
  445. // but are rendered at the bottom of the document.
  446. //
  447. // reference holds all information necessary for a reference-style links or
  448. // footnotes.
  449. //
  450. // Consider this markdown with reference-style links:
  451. //
  452. // [link][ref]
  453. //
  454. // [ref]: /url/ "tooltip title"
  455. //
  456. // It will be ultimately converted to this HTML:
  457. //
  458. // <p><a href=\"/url/\" title=\"title\">link</a></p>
  459. //
  460. // And a reference structure will be populated as follows:
  461. //
  462. // p.refs["ref"] = &reference{
  463. // link: "/url/",
  464. // title: "tooltip title",
  465. // }
  466. //
  467. // Alternatively, reference can contain information about a footnote. Consider
  468. // this markdown:
  469. //
  470. // Text needing a footnote.[^a]
  471. //
  472. // [^a]: This is the note
  473. //
  474. // A reference structure will be populated as follows:
  475. //
  476. // p.refs["a"] = &reference{
  477. // link: "a",
  478. // title: "This is the note",
  479. // noteID: <some positive int>,
  480. // }
  481. //
  482. // TODO: As you can see, it begs for splitting into two dedicated structures
  483. // for refs and for footnotes.
  484. type reference struct {
  485. link []byte
  486. title []byte
  487. noteID int // 0 if not a footnote ref
  488. hasBlock bool
  489. footnote *Node // a link to the Item node within a list of footnotes
  490. text []byte // only gets populated by refOverride feature with Reference.Text
  491. }
  492. func (r *reference) String() string {
  493. return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}",
  494. r.link, r.title, r.text, r.noteID, r.hasBlock)
  495. }
  496. // Check whether or not data starts with a reference link.
  497. // If so, it is parsed and stored in the list of references
  498. // (in the render struct).
  499. // Returns the number of bytes to skip to move past it,
  500. // or zero if the first line is not a reference.
  501. func isReference(p *Markdown, data []byte, tabSize int) int {
  502. // up to 3 optional leading spaces
  503. if len(data) < 4 {
  504. return 0
  505. }
  506. i := 0
  507. for i < 3 && data[i] == ' ' {
  508. i++
  509. }
  510. noteID := 0
  511. // id part: anything but a newline between brackets
  512. if data[i] != '[' {
  513. return 0
  514. }
  515. i++
  516. if p.extensions&Footnotes != 0 {
  517. if i < len(data) && data[i] == '^' {
  518. // we can set it to anything here because the proper noteIds will
  519. // be assigned later during the second pass. It just has to be != 0
  520. noteID = 1
  521. i++
  522. }
  523. }
  524. idOffset := i
  525. for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' {
  526. i++
  527. }
  528. if i >= len(data) || data[i] != ']' {
  529. return 0
  530. }
  531. idEnd := i
  532. // footnotes can have empty ID, like this: [^], but a reference can not be
  533. // empty like this: []. Break early if it's not a footnote and there's no ID
  534. if noteID == 0 && idOffset == idEnd {
  535. return 0
  536. }
  537. // spacer: colon (space | tab)* newline? (space | tab)*
  538. i++
  539. if i >= len(data) || data[i] != ':' {
  540. return 0
  541. }
  542. i++
  543. for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
  544. i++
  545. }
  546. if i < len(data) && (data[i] == '\n' || data[i] == '\r') {
  547. i++
  548. if i < len(data) && data[i] == '\n' && data[i-1] == '\r' {
  549. i++
  550. }
  551. }
  552. for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
  553. i++
  554. }
  555. if i >= len(data) {
  556. return 0
  557. }
  558. var (
  559. linkOffset, linkEnd int
  560. titleOffset, titleEnd int
  561. lineEnd int
  562. raw []byte
  563. hasBlock bool
  564. )
  565. if p.extensions&Footnotes != 0 && noteID != 0 {
  566. linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
  567. lineEnd = linkEnd
  568. } else {
  569. linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i)
  570. }
  571. if lineEnd == 0 {
  572. return 0
  573. }
  574. // a valid ref has been found
  575. ref := &reference{
  576. noteID: noteID,
  577. hasBlock: hasBlock,
  578. }
  579. if noteID > 0 {
  580. // reusing the link field for the id since footnotes don't have links
  581. ref.link = data[idOffset:idEnd]
  582. // if footnote, it's not really a title, it's the contained text
  583. ref.title = raw
  584. } else {
  585. ref.link = data[linkOffset:linkEnd]
  586. ref.title = data[titleOffset:titleEnd]
  587. }
  588. // id matches are case-insensitive
  589. id := string(bytes.ToLower(data[idOffset:idEnd]))
  590. p.refs[id] = ref
  591. return lineEnd
  592. }
  593. func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) {
  594. // link: whitespace-free sequence, optionally between angle brackets
  595. if data[i] == '<' {
  596. i++
  597. }
  598. linkOffset = i
  599. for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' {
  600. i++
  601. }
  602. linkEnd = i
  603. if data[linkOffset] == '<' && data[linkEnd-1] == '>' {
  604. linkOffset++
  605. linkEnd--
  606. }
  607. // optional spacer: (space | tab)* (newline | '\'' | '"' | '(' )
  608. for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
  609. i++
  610. }
  611. if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' {
  612. return
  613. }
  614. // compute end-of-line
  615. if i >= len(data) || data[i] == '\r' || data[i] == '\n' {
  616. lineEnd = i
  617. }
  618. if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' {
  619. lineEnd++
  620. }
  621. // optional (space|tab)* spacer after a newline
  622. if lineEnd > 0 {
  623. i = lineEnd + 1
  624. for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
  625. i++
  626. }
  627. }
  628. // optional title: any non-newline sequence enclosed in '"() alone on its line
  629. if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') {
  630. i++
  631. titleOffset = i
  632. // look for EOL
  633. for i < len(data) && data[i] != '\n' && data[i] != '\r' {
  634. i++
  635. }
  636. if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' {
  637. titleEnd = i + 1
  638. } else {
  639. titleEnd = i
  640. }
  641. // step back
  642. i--
  643. for i > titleOffset && (data[i] == ' ' || data[i] == '\t') {
  644. i--
  645. }
  646. if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') {
  647. lineEnd = titleEnd
  648. titleEnd = i
  649. }
  650. }
  651. return
  652. }
  653. // The first bit of this logic is the same as Parser.listItem, but the rest
  654. // is much simpler. This function simply finds the entire block and shifts it
  655. // over by one tab if it is indeed a block (just returns the line if it's not).
  656. // blockEnd is the end of the section in the input buffer, and contents is the
  657. // extracted text that was shifted over one tab. It will need to be rendered at
  658. // the end of the document.
  659. func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) {
  660. if i == 0 || len(data) == 0 {
  661. return
  662. }
  663. // skip leading whitespace on first line
  664. for i < len(data) && data[i] == ' ' {
  665. i++
  666. }
  667. blockStart = i
  668. // find the end of the line
  669. blockEnd = i
  670. for i < len(data) && data[i-1] != '\n' {
  671. i++
  672. }
  673. // get working buffer
  674. var raw bytes.Buffer
  675. // put the first line into the working buffer
  676. raw.Write(data[blockEnd:i])
  677. blockEnd = i
  678. // process the following lines
  679. containsBlankLine := false
  680. gatherLines:
  681. for blockEnd < len(data) {
  682. i++
  683. // find the end of this line
  684. for i < len(data) && data[i-1] != '\n' {
  685. i++
  686. }
  687. // if it is an empty line, guess that it is part of this item
  688. // and move on to the next line
  689. if p.isEmpty(data[blockEnd:i]) > 0 {
  690. containsBlankLine = true
  691. blockEnd = i
  692. continue
  693. }
  694. n := 0
  695. if n = isIndented(data[blockEnd:i], indentSize); n == 0 {
  696. // this is the end of the block.
  697. // we don't want to include this last line in the index.
  698. break gatherLines
  699. }
  700. // if there were blank lines before this one, insert a new one now
  701. if containsBlankLine {
  702. raw.WriteByte('\n')
  703. containsBlankLine = false
  704. }
  705. // get rid of that first tab, write to buffer
  706. raw.Write(data[blockEnd+n : i])
  707. hasBlock = true
  708. blockEnd = i
  709. }
  710. if data[blockEnd-1] != '\n' {
  711. raw.WriteByte('\n')
  712. }
  713. contents = raw.Bytes()
  714. return
  715. }
  716. //
  717. //
  718. // Miscellaneous helper functions
  719. //
  720. //
  721. // Test if a character is a punctuation symbol.
  722. // Taken from a private function in regexp in the stdlib.
  723. func ispunct(c byte) bool {
  724. for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") {
  725. if c == r {
  726. return true
  727. }
  728. }
  729. return false
  730. }
  731. // Test if a character is a whitespace character.
  732. func isspace(c byte) bool {
  733. return ishorizontalspace(c) || isverticalspace(c)
  734. }
  735. // Test if a character is a horizontal whitespace character.
  736. func ishorizontalspace(c byte) bool {
  737. return c == ' ' || c == '\t'
  738. }
  739. // Test if a character is a vertical character.
  740. func isverticalspace(c byte) bool {
  741. return c == '\n' || c == '\r' || c == '\f' || c == '\v'
  742. }
  743. // Test if a character is letter.
  744. func isletter(c byte) bool {
  745. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
  746. }
  747. // Test if a character is a letter or a digit.
  748. // TODO: check when this is looking for ASCII alnum and when it should use unicode
  749. func isalnum(c byte) bool {
  750. return (c >= '0' && c <= '9') || isletter(c)
  751. }
  752. // Replace tab characters with spaces, aligning to the next TAB_SIZE column.
  753. // always ends output with a newline
  754. func expandTabs(out *bytes.Buffer, line []byte, tabSize int) {
  755. // first, check for common cases: no tabs, or only tabs at beginning of line
  756. i, prefix := 0, 0
  757. slowcase := false
  758. for i = 0; i < len(line); i++ {
  759. if line[i] == '\t' {
  760. if prefix == i {
  761. prefix++
  762. } else {
  763. slowcase = true
  764. break
  765. }
  766. }
  767. }
  768. // no need to decode runes if all tabs are at the beginning of the line
  769. if !slowcase {
  770. for i = 0; i < prefix*tabSize; i++ {
  771. out.WriteByte(' ')
  772. }
  773. out.Write(line[prefix:])
  774. return
  775. }
  776. // the slow case: we need to count runes to figure out how
  777. // many spaces to insert for each tab
  778. column := 0
  779. i = 0
  780. for i < len(line) {
  781. start := i
  782. for i < len(line) && line[i] != '\t' {
  783. _, size := utf8.DecodeRune(line[i:])
  784. i += size
  785. column++
  786. }
  787. if i > start {
  788. out.Write(line[start:i])
  789. }
  790. if i >= len(line) {
  791. break
  792. }
  793. for {
  794. out.WriteByte(' ')
  795. column++
  796. if column%tabSize == 0 {
  797. break
  798. }
  799. }
  800. i++
  801. }
  802. }
  803. // Find if a line counts as indented or not.
  804. // Returns number of characters the indent is (0 = not indented).
  805. func isIndented(data []byte, indentSize int) int {
  806. if len(data) == 0 {
  807. return 0
  808. }
  809. if data[0] == '\t' {
  810. return 1
  811. }
  812. if len(data) < indentSize {
  813. return 0
  814. }
  815. for i := 0; i < indentSize; i++ {
  816. if data[i] != ' ' {
  817. return 0
  818. }
  819. }
  820. return indentSize
  821. }
  822. // Create a url-safe slug for fragments
  823. func slugify(in []byte) []byte {
  824. if len(in) == 0 {
  825. return in
  826. }
  827. out := make([]byte, 0, len(in))
  828. sym := false
  829. for _, ch := range in {
  830. if isalnum(ch) {
  831. sym = false
  832. out = append(out, ch)
  833. } else if sym {
  834. continue
  835. } else {
  836. out = append(out, '-')
  837. sym = true
  838. }
  839. }
  840. var a, b int
  841. var ch byte
  842. for a, ch = range out {
  843. if ch != '-' {
  844. break
  845. }
  846. }
  847. for b = len(out) - 1; b > 0; b-- {
  848. if out[b] != '-' {
  849. break
  850. }
  851. }
  852. return out[a : b+1]
  853. }