index.js 3.0 KB

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