thematic-break.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /**
  2. * @typedef {import('micromark-util-types').Code} Code
  3. * @typedef {import('micromark-util-types').Construct} Construct
  4. * @typedef {import('micromark-util-types').State} State
  5. * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
  6. * @typedef {import('micromark-util-types').Tokenizer} Tokenizer
  7. */
  8. import {factorySpace} from 'micromark-factory-space'
  9. import {markdownLineEnding, markdownSpace} from 'micromark-util-character'
  10. import {codes, constants, types} from 'micromark-util-symbol'
  11. import {ok as assert} from 'devlop'
  12. /** @type {Construct} */
  13. export const thematicBreak = {
  14. name: 'thematicBreak',
  15. tokenize: tokenizeThematicBreak
  16. }
  17. /**
  18. * @this {TokenizeContext}
  19. * @type {Tokenizer}
  20. */
  21. function tokenizeThematicBreak(effects, ok, nok) {
  22. let size = 0
  23. /** @type {NonNullable<Code>} */
  24. let marker
  25. return start
  26. /**
  27. * Start of thematic break.
  28. *
  29. * ```markdown
  30. * > | ***
  31. * ^
  32. * ```
  33. *
  34. * @type {State}
  35. */
  36. function start(code) {
  37. effects.enter(types.thematicBreak)
  38. // To do: parse indent like `markdown-rs`.
  39. return before(code)
  40. }
  41. /**
  42. * After optional whitespace, at marker.
  43. *
  44. * ```markdown
  45. * > | ***
  46. * ^
  47. * ```
  48. *
  49. * @type {State}
  50. */
  51. function before(code) {
  52. assert(
  53. code === codes.asterisk ||
  54. code === codes.dash ||
  55. code === codes.underscore,
  56. 'expected `*`, `-`, or `_`'
  57. )
  58. marker = code
  59. return atBreak(code)
  60. }
  61. /**
  62. * After something, before something else.
  63. *
  64. * ```markdown
  65. * > | ***
  66. * ^
  67. * ```
  68. *
  69. * @type {State}
  70. */
  71. function atBreak(code) {
  72. if (code === marker) {
  73. effects.enter(types.thematicBreakSequence)
  74. return sequence(code)
  75. }
  76. if (
  77. size >= constants.thematicBreakMarkerCountMin &&
  78. (code === codes.eof || markdownLineEnding(code))
  79. ) {
  80. effects.exit(types.thematicBreak)
  81. return ok(code)
  82. }
  83. return nok(code)
  84. }
  85. /**
  86. * In sequence.
  87. *
  88. * ```markdown
  89. * > | ***
  90. * ^
  91. * ```
  92. *
  93. * @type {State}
  94. */
  95. function sequence(code) {
  96. if (code === marker) {
  97. effects.consume(code)
  98. size++
  99. return sequence
  100. }
  101. effects.exit(types.thematicBreakSequence)
  102. return markdownSpace(code)
  103. ? factorySpace(effects, atBreak, types.whitespace)(code)
  104. : atBreak(code)
  105. }
  106. }