index.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * @typedef {import('micromark-util-types').Effects} Effects
  3. * @typedef {import('micromark-util-types').State} State
  4. * @typedef {import('micromark-util-types').TokenType} TokenType
  5. */
  6. import {
  7. asciiControl,
  8. markdownLineEndingOrSpace,
  9. markdownLineEnding
  10. } from 'micromark-util-character'
  11. /**
  12. * Parse destinations.
  13. *
  14. * ###### Examples
  15. *
  16. * ```markdown
  17. * <a>
  18. * <a\>b>
  19. * <a b>
  20. * <a)>
  21. * a
  22. * a\)b
  23. * a(b)c
  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 for whole (`<a>` or `b`).
  35. * @param {TokenType} literalType
  36. * Type when enclosed (`<a>`).
  37. * @param {TokenType} literalMarkerType
  38. * Type for enclosing (`<` and `>`).
  39. * @param {TokenType} rawType
  40. * Type when not enclosed (`b`).
  41. * @param {TokenType} stringType
  42. * Type for the value (`a` or `b`).
  43. * @param {number | undefined} [max=Infinity]
  44. * Depth of nested parens (inclusive).
  45. * @returns {State}
  46. * Start state.
  47. */ // eslint-disable-next-line max-params
  48. export function factoryDestination(
  49. effects,
  50. ok,
  51. nok,
  52. type,
  53. literalType,
  54. literalMarkerType,
  55. rawType,
  56. stringType,
  57. max
  58. ) {
  59. const limit = max || Number.POSITIVE_INFINITY
  60. let balance = 0
  61. return start
  62. /**
  63. * Start of destination.
  64. *
  65. * ```markdown
  66. * > | <aa>
  67. * ^
  68. * > | aa
  69. * ^
  70. * ```
  71. *
  72. * @type {State}
  73. */
  74. function start(code) {
  75. if (code === 60) {
  76. effects.enter(type)
  77. effects.enter(literalType)
  78. effects.enter(literalMarkerType)
  79. effects.consume(code)
  80. effects.exit(literalMarkerType)
  81. return enclosedBefore
  82. }
  83. // ASCII control, space, closing paren.
  84. if (code === null || code === 32 || code === 41 || asciiControl(code)) {
  85. return nok(code)
  86. }
  87. effects.enter(type)
  88. effects.enter(rawType)
  89. effects.enter(stringType)
  90. effects.enter('chunkString', {
  91. contentType: 'string'
  92. })
  93. return raw(code)
  94. }
  95. /**
  96. * After `<`, at an enclosed destination.
  97. *
  98. * ```markdown
  99. * > | <aa>
  100. * ^
  101. * ```
  102. *
  103. * @type {State}
  104. */
  105. function enclosedBefore(code) {
  106. if (code === 62) {
  107. effects.enter(literalMarkerType)
  108. effects.consume(code)
  109. effects.exit(literalMarkerType)
  110. effects.exit(literalType)
  111. effects.exit(type)
  112. return ok
  113. }
  114. effects.enter(stringType)
  115. effects.enter('chunkString', {
  116. contentType: 'string'
  117. })
  118. return enclosed(code)
  119. }
  120. /**
  121. * In enclosed destination.
  122. *
  123. * ```markdown
  124. * > | <aa>
  125. * ^
  126. * ```
  127. *
  128. * @type {State}
  129. */
  130. function enclosed(code) {
  131. if (code === 62) {
  132. effects.exit('chunkString')
  133. effects.exit(stringType)
  134. return enclosedBefore(code)
  135. }
  136. if (code === null || code === 60 || markdownLineEnding(code)) {
  137. return nok(code)
  138. }
  139. effects.consume(code)
  140. return code === 92 ? enclosedEscape : enclosed
  141. }
  142. /**
  143. * After `\`, at a special character.
  144. *
  145. * ```markdown
  146. * > | <a\*a>
  147. * ^
  148. * ```
  149. *
  150. * @type {State}
  151. */
  152. function enclosedEscape(code) {
  153. if (code === 60 || code === 62 || code === 92) {
  154. effects.consume(code)
  155. return enclosed
  156. }
  157. return enclosed(code)
  158. }
  159. /**
  160. * In raw destination.
  161. *
  162. * ```markdown
  163. * > | aa
  164. * ^
  165. * ```
  166. *
  167. * @type {State}
  168. */
  169. function raw(code) {
  170. if (
  171. !balance &&
  172. (code === null || code === 41 || markdownLineEndingOrSpace(code))
  173. ) {
  174. effects.exit('chunkString')
  175. effects.exit(stringType)
  176. effects.exit(rawType)
  177. effects.exit(type)
  178. return ok(code)
  179. }
  180. if (balance < limit && code === 40) {
  181. effects.consume(code)
  182. balance++
  183. return raw
  184. }
  185. if (code === 41) {
  186. effects.consume(code)
  187. balance--
  188. return raw
  189. }
  190. // ASCII control (but *not* `\0`) and space and `(`.
  191. // Note: in `markdown-rs`, `\0` exists in codes, in `micromark-js` it
  192. // doesn’t.
  193. if (code === null || code === 32 || code === 40 || asciiControl(code)) {
  194. return nok(code)
  195. }
  196. effects.consume(code)
  197. return code === 92 ? rawEscape : raw
  198. }
  199. /**
  200. * After `\`, at special character.
  201. *
  202. * ```markdown
  203. * > | a\*a
  204. * ^
  205. * ```
  206. *
  207. * @type {State}
  208. */
  209. function rawEscape(code) {
  210. if (code === 40 || code === 41 || code === 92) {
  211. effects.consume(code)
  212. return raw
  213. }
  214. return raw(code)
  215. }
  216. }