citation.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package parser
  2. import (
  3. "bytes"
  4. "github.com/gomarkdown/markdown/ast"
  5. )
  6. // citation parses a citation. In its most simple form [@ref], we allow multiple
  7. // being separated by semicolons and a sub reference inside ala pandoc: [@ref, p. 23].
  8. // Each citation can have a modifier: !, ? or - wich mean:
  9. //
  10. // ! - normative
  11. // ? - formative
  12. // - - suppressed
  13. //
  14. // The suffix starts after a comma, we strip any whitespace before and after. If the output
  15. // allows for it, this can be rendered.
  16. func citation(p *Parser, data []byte, offset int) (int, ast.Node) {
  17. // look for the matching closing bracket
  18. i := offset + 1
  19. for level := 1; level > 0 && i < len(data); i++ {
  20. switch {
  21. case data[i] == '\n':
  22. // no newlines allowed.
  23. return 0, nil
  24. case data[i-1] == '\\':
  25. continue
  26. case data[i] == '[':
  27. level++
  28. case data[i] == ']':
  29. level--
  30. if level <= 0 {
  31. i-- // compensate for extra i++ in for loop
  32. }
  33. }
  34. }
  35. if i >= len(data) {
  36. return 0, nil
  37. }
  38. node := &ast.Citation{}
  39. citations := bytes.Split(data[1:i], []byte(";"))
  40. for _, citation := range citations {
  41. var suffix []byte
  42. citation = bytes.TrimSpace(citation)
  43. j := 0
  44. if citation[j] != '@' {
  45. // not a citation, drop out entirely.
  46. return 0, nil
  47. }
  48. if c := bytes.Index(citation, []byte(",")); c > 0 {
  49. part := citation[:c]
  50. suff := citation[c+1:]
  51. part = bytes.TrimSpace(part)
  52. suff = bytes.TrimSpace(suff)
  53. citation = part
  54. suffix = suff
  55. }
  56. citeType := ast.CitationTypeInformative
  57. if len(citation) < 2 {
  58. continue
  59. }
  60. j = 1
  61. switch citation[j] {
  62. case '!':
  63. citeType = ast.CitationTypeNormative
  64. j++
  65. case '?':
  66. citeType = ast.CitationTypeInformative
  67. j++
  68. case '-':
  69. citeType = ast.CitationTypeSuppressed
  70. j++
  71. }
  72. node.Destination = append(node.Destination, citation[j:])
  73. node.Type = append(node.Type, citeType)
  74. node.Suffix = append(node.Suffix, suffix)
  75. }
  76. return i + 1, node
  77. }