index.js 4.8 KB

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