setext-underline.mjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. var setextUnderline = {
  2. name: 'setextUnderline',
  3. tokenize: tokenizeSetextUnderline,
  4. resolveTo: resolveToSetextUnderline
  5. }
  6. export default setextUnderline
  7. import assert from 'assert'
  8. import codes from '../character/codes.mjs'
  9. import markdownLineEnding from '../character/markdown-line-ending.mjs'
  10. import types from '../constant/types.mjs'
  11. import shallow from '../util/shallow.mjs'
  12. import spaceFactory from './factory-space.mjs'
  13. function resolveToSetextUnderline(events, context) {
  14. var index = events.length
  15. var content
  16. var text
  17. var definition
  18. var heading
  19. // Find the opening of the content.
  20. // It’ll always exist: we don’t tokenize if it isn’t there.
  21. while (index--) {
  22. if (events[index][0] === 'enter') {
  23. if (events[index][1].type === types.content) {
  24. content = index
  25. break
  26. }
  27. if (events[index][1].type === types.paragraph) {
  28. text = index
  29. }
  30. }
  31. // Exit
  32. else {
  33. if (events[index][1].type === types.content) {
  34. // Remove the content end (if needed we’ll add it later)
  35. events.splice(index, 1)
  36. }
  37. if (!definition && events[index][1].type === types.definition) {
  38. definition = index
  39. }
  40. }
  41. }
  42. heading = {
  43. type: types.setextHeading,
  44. start: shallow(events[text][1].start),
  45. end: shallow(events[events.length - 1][1].end)
  46. }
  47. // Change the paragraph to setext heading text.
  48. events[text][1].type = types.setextHeadingText
  49. // If we have definitions in the content, we’ll keep on having content,
  50. // but we need move it.
  51. if (definition) {
  52. events.splice(text, 0, ['enter', heading, context])
  53. events.splice(definition + 1, 0, ['exit', events[content][1], context])
  54. events[content][1].end = shallow(events[definition][1].end)
  55. } else {
  56. events[content][1] = heading
  57. }
  58. // Add the heading exit at the end.
  59. events.push(['exit', heading, context])
  60. return events
  61. }
  62. function tokenizeSetextUnderline(effects, ok, nok) {
  63. var self = this
  64. var index = self.events.length
  65. var marker
  66. var paragraph
  67. // Find an opening.
  68. while (index--) {
  69. // Skip enter/exit of line ending, line prefix, and content.
  70. // We can now either have a definition or a paragraph.
  71. if (
  72. self.events[index][1].type !== types.lineEnding &&
  73. self.events[index][1].type !== types.linePrefix &&
  74. self.events[index][1].type !== types.content
  75. ) {
  76. paragraph = self.events[index][1].type === types.paragraph
  77. break
  78. }
  79. }
  80. return start
  81. function start(code) {
  82. assert(
  83. code === codes.dash || code === codes.equalsTo,
  84. 'expected `=` or `-`'
  85. )
  86. if (!self.lazy && (self.interrupt || paragraph)) {
  87. effects.enter(types.setextHeadingLine)
  88. effects.enter(types.setextHeadingLineSequence)
  89. marker = code
  90. return closingSequence(code)
  91. }
  92. return nok(code)
  93. }
  94. function closingSequence(code) {
  95. if (code === marker) {
  96. effects.consume(code)
  97. return closingSequence
  98. }
  99. effects.exit(types.setextHeadingLineSequence)
  100. return spaceFactory(effects, closingSequenceEnd, types.lineSuffix)(code)
  101. }
  102. function closingSequenceEnd(code) {
  103. if (code === codes.eof || markdownLineEnding(code)) {
  104. effects.exit(types.setextHeadingLine)
  105. return ok(code)
  106. }
  107. return nok(code)
  108. }
  109. }