index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /**
  2. * @typedef {import('micromark-util-types').Code} Code
  3. * @typedef {import('micromark-util-types').Effects} Effects
  4. * @typedef {import('micromark-util-types').State} State
  5. * @typedef {import('micromark-util-types').TokenType} TokenType
  6. */
  7. import {factorySpace} from 'micromark-factory-space'
  8. import {markdownLineEnding} from 'micromark-util-character'
  9. import {codes, constants, types} from 'micromark-util-symbol'
  10. /**
  11. * Parse titles.
  12. *
  13. * ###### Examples
  14. *
  15. * ```markdown
  16. * "a"
  17. * 'b'
  18. * (c)
  19. * "a
  20. * b"
  21. * 'a
  22. * b'
  23. * (a\)b)
  24. * ```
  25. *
  26. * @param {Effects} effects
  27. * Context.
  28. * @param {State} ok
  29. * State switched to when successful.
  30. * @param {State} nok
  31. * State switched to when unsuccessful.
  32. * @param {TokenType} type
  33. * Type of the whole title (`"a"`, `'b'`, `(c)`).
  34. * @param {TokenType} markerType
  35. * Type for the markers (`"`, `'`, `(`, and `)`).
  36. * @param {TokenType} stringType
  37. * Type for the value (`a`).
  38. * @returns {State}
  39. * Start state.
  40. */
  41. // eslint-disable-next-line max-params
  42. export function factoryTitle(effects, ok, nok, type, markerType, stringType) {
  43. /** @type {NonNullable<Code>} */
  44. let marker
  45. return start
  46. /**
  47. * Start of title.
  48. *
  49. * ```markdown
  50. * > | "a"
  51. * ^
  52. * ```
  53. *
  54. * @type {State}
  55. */
  56. function start(code) {
  57. if (
  58. code === codes.quotationMark ||
  59. code === codes.apostrophe ||
  60. code === codes.leftParenthesis
  61. ) {
  62. effects.enter(type)
  63. effects.enter(markerType)
  64. effects.consume(code)
  65. effects.exit(markerType)
  66. marker = code === codes.leftParenthesis ? codes.rightParenthesis : code
  67. return begin
  68. }
  69. return nok(code)
  70. }
  71. /**
  72. * After opening marker.
  73. *
  74. * This is also used at the closing marker.
  75. *
  76. * ```markdown
  77. * > | "a"
  78. * ^
  79. * ```
  80. *
  81. * @type {State}
  82. */
  83. function begin(code) {
  84. if (code === marker) {
  85. effects.enter(markerType)
  86. effects.consume(code)
  87. effects.exit(markerType)
  88. effects.exit(type)
  89. return ok
  90. }
  91. effects.enter(stringType)
  92. return atBreak(code)
  93. }
  94. /**
  95. * At something, before something else.
  96. *
  97. * ```markdown
  98. * > | "a"
  99. * ^
  100. * ```
  101. *
  102. * @type {State}
  103. */
  104. function atBreak(code) {
  105. if (code === marker) {
  106. effects.exit(stringType)
  107. return begin(marker)
  108. }
  109. if (code === codes.eof) {
  110. return nok(code)
  111. }
  112. // Note: blank lines can’t exist in content.
  113. if (markdownLineEnding(code)) {
  114. // To do: use `space_or_tab_eol_with_options`, connect.
  115. effects.enter(types.lineEnding)
  116. effects.consume(code)
  117. effects.exit(types.lineEnding)
  118. return factorySpace(effects, atBreak, types.linePrefix)
  119. }
  120. effects.enter(types.chunkString, {contentType: constants.contentTypeString})
  121. return inside(code)
  122. }
  123. /**
  124. *
  125. *
  126. * @type {State}
  127. */
  128. function inside(code) {
  129. if (code === marker || code === codes.eof || markdownLineEnding(code)) {
  130. effects.exit(types.chunkString)
  131. return atBreak(code)
  132. }
  133. effects.consume(code)
  134. return code === codes.backslash ? escape : inside
  135. }
  136. /**
  137. * After `\`, at a special character.
  138. *
  139. * ```markdown
  140. * > | "a\*b"
  141. * ^
  142. * ```
  143. *
  144. * @type {State}
  145. */
  146. function escape(code) {
  147. if (code === marker || code === codes.backslash) {
  148. effects.consume(code)
  149. return inside
  150. }
  151. return inside(code)
  152. }
  153. }