AK-21/Graphite-Industrial-Intelligence
0
1/**2 * @import {3 * Code,4 * Construct,5 * State,6 * TokenizeContext,7 * Tokenizer8 * } from 'micromark-util-types'9 */10 11import { factorySpace } from 'micromark-factory-space';12import { markdownLineEnding, markdownSpace } from 'micromark-util-character';13/** @type {Construct} */14const nonLazyContinuation = {15 partial: true,16 tokenize: tokenizeNonLazyContinuation17};18 19/** @type {Construct} */20export const codeFenced = {21 concrete: true,22 name: 'codeFenced',23 tokenize: tokenizeCodeFenced24};25 26/**27 * @this {TokenizeContext}28 * Context.29 * @type {Tokenizer}30 */31function tokenizeCodeFenced(effects, ok, nok) {32 const self = this;33 /** @type {Construct} */34 const closeStart = {35 partial: true,36 tokenize: tokenizeCloseStart37 };38 let initialPrefix = 0;39 let sizeOpen = 0;40 /** @type {NonNullable<Code>} */41 let marker;42 return start;43 44 /**45 * Start of code.46 *47 * ```markdown48 * > | ~~~js49 * ^50 * | alert(1)51 * | ~~~52 * ```53 *54 * @type {State}55 */56 function start(code) {57 // To do: parse whitespace like `markdown-rs`.58 return beforeSequenceOpen(code);59 }60 61 /**62 * In opening fence, after prefix, at sequence.63 *64 * ```markdown65 * > | ~~~js66 * ^67 * | alert(1)68 * | ~~~69 * ```70 *71 * @type {State}72 */73 function beforeSequenceOpen(code) {74 const tail = self.events[self.events.length - 1];75 initialPrefix = tail && tail[1].type === "linePrefix" ? tail[2].sliceSerialize(tail[1], true).length : 0;76 marker = code;77 effects.enter("codeFenced");78 effects.enter("codeFencedFence");79 effects.enter("codeFencedFenceSequence");80 return sequenceOpen(code);81 }82 83 /**84 * In opening fence sequence.85 *86 * ```markdown87 * > | ~~~js88 * ^89 * | alert(1)90 * | ~~~91 * ```92 *93 * @type {State}94 */95 function sequenceOpen(code) {96 if (code === marker) {97 sizeOpen++;98 effects.consume(code);99 return sequenceOpen;100 }101 if (sizeOpen < 3) {102 return nok(code);103 }104 effects.exit("codeFencedFenceSequence");105 return markdownSpace(code) ? factorySpace(effects, infoBefore, "whitespace")(code) : infoBefore(code);106 }107 108 /**109 * In opening fence, after the sequence (and optional whitespace), before info.110 *111 * ```markdown112 * > | ~~~js113 * ^114 * | alert(1)115 * | ~~~116 * ```117 *118 * @type {State}119 */120 function infoBefore(code) {121 if (code === null || markdownLineEnding(code)) {122 effects.exit("codeFencedFence");123 return self.interrupt ? ok(code) : effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);124 }125 effects.enter("codeFencedFenceInfo");126 effects.enter("chunkString", {127 contentType: "string"128 });129 return info(code);130 }131 132 /**133 * In info.134 *135 * ```markdown136 * > | ~~~js137 * ^138 * | alert(1)139 * | ~~~140 * ```141 *142 * @type {State}143 */144 function info(code) {145 if (code === null || markdownLineEnding(code)) {146 effects.exit("chunkString");147 effects.exit("codeFencedFenceInfo");148 return infoBefore(code);149 }150 if (markdownSpace(code)) {151 effects.exit("chunkString");152 effects.exit("codeFencedFenceInfo");153 return factorySpace(effects, metaBefore, "whitespace")(code);154 }155 if (code === 96 && code === marker) {156 return nok(code);157 }158 effects.consume(code);159 return info;160 }161 162 /**163 * In opening fence, after info and whitespace, before meta.164 *165 * ```markdown166 * > | ~~~js eval167 * ^168 * | alert(1)169 * | ~~~170 * ```171 *172 * @type {State}173 */174 function metaBefore(code) {175 if (code === null || markdownLineEnding(code)) {176 return infoBefore(code);177 }178 effects.enter("codeFencedFenceMeta");179 effects.enter("chunkString", {180 contentType: "string"181 });182 return meta(code);183 }184 185 /**186 * In meta.187 *188 * ```markdown189 * > | ~~~js eval190 * ^191 * | alert(1)192 * | ~~~193 * ```194 *195 * @type {State}196 */197 function meta(code) {198 if (code === null || markdownLineEnding(code)) {199 effects.exit("chunkString");200 effects.exit("codeFencedFenceMeta");201 return infoBefore(code);202 }203 if (code === 96 && code === marker) {204 return nok(code);205 }206 effects.consume(code);207 return meta;208 }209 210 /**211 * At eol/eof in code, before a non-lazy closing fence or content.212 *213 * ```markdown214 * > | ~~~js215 * ^216 * > | alert(1)217 * ^218 * | ~~~219 * ```220 *221 * @type {State}222 */223 function atNonLazyBreak(code) {224 return effects.attempt(closeStart, after, contentBefore)(code);225 }226 227 /**228 * Before code content, not a closing fence, at eol.229 *230 * ```markdown231 * | ~~~js232 * > | alert(1)233 * ^234 * | ~~~235 * ```236 *237 * @type {State}238 */239 function contentBefore(code) {240 effects.enter("lineEnding");241 effects.consume(code);242 effects.exit("lineEnding");243 return contentStart;244 }245 246 /**247 * Before code content, not a closing fence.248 *249 * ```markdown250 * | ~~~js251 * > | alert(1)252 * ^253 * | ~~~254 * ```255 *256 * @type {State}257 */258 function contentStart(code) {259 return initialPrefix > 0 && markdownSpace(code) ? factorySpace(effects, beforeContentChunk, "linePrefix", initialPrefix + 1)(code) : beforeContentChunk(code);260 }261 262 /**263 * Before code content, after optional prefix.264 *265 * ```markdown266 * | ~~~js267 * > | alert(1)268 * ^269 * | ~~~270 * ```271 *272 * @type {State}273 */274 function beforeContentChunk(code) {275 if (code === null || markdownLineEnding(code)) {276 return effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);277 }278 effects.enter("codeFlowValue");279 return contentChunk(code);280 }281 282 /**283 * In code content.284 *285 * ```markdown286 * | ~~~js287 * > | alert(1)288 * ^^^^^^^^289 * | ~~~290 * ```291 *292 * @type {State}293 */294 function contentChunk(code) {295 if (code === null || markdownLineEnding(code)) {296 effects.exit("codeFlowValue");297 return beforeContentChunk(code);298 }299 effects.consume(code);300 return contentChunk;301 }302 303 /**304 * After code.305 *306 * ```markdown307 * | ~~~js308 * | alert(1)309 * > | ~~~310 * ^311 * ```312 *313 * @type {State}314 */315 function after(code) {316 effects.exit("codeFenced");317 return ok(code);318 }319 320 /**321 * @this {TokenizeContext}322 * Context.323 * @type {Tokenizer}324 */325 function tokenizeCloseStart(effects, ok, nok) {326 let size = 0;327 return startBefore;328 329 /**330 *331 *332 * @type {State}333 */334 function startBefore(code) {335 effects.enter("lineEnding");336 effects.consume(code);337 effects.exit("lineEnding");338 return start;339 }340 341 /**342 * Before closing fence, at optional whitespace.343 *344 * ```markdown345 * | ~~~js346 * | alert(1)347 * > | ~~~348 * ^349 * ```350 *351 * @type {State}352 */353 function start(code) {354 // Always populated by defaults.355 356 // To do: `enter` here or in next state?357 effects.enter("codeFencedFence");358 return markdownSpace(code) ? factorySpace(effects, beforeSequenceClose, "linePrefix", self.parser.constructs.disable.null.includes('codeIndented') ? undefined : 4)(code) : beforeSequenceClose(code);359 }360 361 /**362 * In closing fence, after optional whitespace, at sequence.363 *364 * ```markdown365 * | ~~~js366 * | alert(1)367 * > | ~~~368 * ^369 * ```370 *371 * @type {State}372 */373 function beforeSequenceClose(code) {374 if (code === marker) {375 effects.enter("codeFencedFenceSequence");376 return sequenceClose(code);377 }378 return nok(code);379 }380 381 /**382 * In closing fence sequence.383 *384 * ```markdown385 * | ~~~js386 * | alert(1)387 * > | ~~~388 * ^389 * ```390 *391 * @type {State}392 */393 function sequenceClose(code) {394 if (code === marker) {395 size++;396 effects.consume(code);397 return sequenceClose;398 }399 if (size >= sizeOpen) {400 effects.exit("codeFencedFenceSequence");401 return markdownSpace(code) ? factorySpace(effects, sequenceCloseAfter, "whitespace")(code) : sequenceCloseAfter(code);402 }403 return nok(code);404 }405 406 /**407 * After closing fence sequence, after optional whitespace.408 *409 * ```markdown410 * | ~~~js411 * | alert(1)412 * > | ~~~413 * ^414 * ```415 *416 * @type {State}417 */418 function sequenceCloseAfter(code) {419 if (code === null || markdownLineEnding(code)) {420 effects.exit("codeFencedFence");421 return ok(code);422 }423 return nok(code);424 }425 }426}427 428/**429 * @this {TokenizeContext}430 * Context.431 * @type {Tokenizer}432 */433function tokenizeNonLazyContinuation(effects, ok, nok) {434 const self = this;435 return start;436 437 /**438 *439 *440 * @type {State}441 */442 function start(code) {443 if (code === null) {444 return nok(code);445 }446 effects.enter("lineEnding");447 effects.consume(code);448 effects.exit("lineEnding");449 return lineStart;450 }451 452 /**453 *454 *455 * @type {State}456 */457 function lineStart(code) {458 return self.parser.lazy[self.now().line] ? nok(code) : ok(code);459 }460}