index.js 5.0 KB

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