figures.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package parser
  2. import (
  3. "bytes"
  4. "github.com/gomarkdown/markdown/ast"
  5. )
  6. // sFigureLine checks if there's a figure line (e.g., !--- ) at the beginning of data,
  7. // and returns the end index if so, or 0 otherwise.
  8. func sFigureLine(data []byte, oldmarker string) (end int, marker string) {
  9. i, size := 0, 0
  10. n := len(data)
  11. // skip up to three spaces
  12. for i < n && i < 3 && data[i] == ' ' {
  13. i++
  14. }
  15. // check for the marker characters: !
  16. if i+1 >= n {
  17. return 0, ""
  18. }
  19. if data[i] != '!' || data[i+1] != '-' {
  20. return 0, ""
  21. }
  22. i++
  23. c := data[i] // i.e. the -
  24. // the whole line must be the same char or whitespace
  25. for i < n && data[i] == c {
  26. size++
  27. i++
  28. }
  29. // the marker char must occur at least 3 times
  30. if size < 3 {
  31. return 0, ""
  32. }
  33. marker = string(data[i-size : i])
  34. // if this is the end marker, it must match the beginning marker
  35. if oldmarker != "" && marker != oldmarker {
  36. return 0, ""
  37. }
  38. // there is no syntax modifier although it might be an idea to re-use this space for something?
  39. i = skipChar(data, i, ' ')
  40. if i >= n || data[i] != '\n' {
  41. if i == n {
  42. return i, marker
  43. }
  44. return 0, ""
  45. }
  46. return i + 1, marker // Take newline into account.
  47. }
  48. // figureBlock returns the end index if data contains a figure block at the beginning,
  49. // or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
  50. // If doRender is true, a final newline is mandatory to recognize the figure block.
  51. func (p *Parser) figureBlock(data []byte, doRender bool) int {
  52. beg, marker := sFigureLine(data, "")
  53. if beg == 0 || beg >= len(data) {
  54. return 0
  55. }
  56. var raw bytes.Buffer
  57. for {
  58. // safe to assume beg < len(data)
  59. // check for the end of the code block
  60. figEnd, _ := sFigureLine(data[beg:], marker)
  61. if figEnd != 0 {
  62. beg += figEnd
  63. break
  64. }
  65. // copy the current line
  66. end := skipUntilChar(data, beg, '\n') + 1
  67. // did we reach the end of the buffer without a closing marker?
  68. if end >= len(data) {
  69. return 0
  70. }
  71. // verbatim copy to the working buffer
  72. if doRender {
  73. raw.Write(data[beg:end])
  74. }
  75. beg = end
  76. }
  77. if !doRender {
  78. return beg
  79. }
  80. figure := &ast.CaptionFigure{}
  81. p.AddBlock(figure)
  82. p.Block(raw.Bytes())
  83. defer p.Finalize(figure)
  84. if captionContent, id, consumed := p.caption(data[beg:], []byte("Figure: ")); consumed > 0 {
  85. caption := &ast.Caption{}
  86. p.Inline(caption, captionContent)
  87. figure.HeadingID = id
  88. p.addChild(caption)
  89. beg += consumed
  90. }
  91. return beg
  92. }