CoolFace
Apppublic

AK-21/Graphite-Industrial-Intelligence

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
attention.js241 linesDownload Raw Back to lib
1/**2 * @import {3 *   Code,4 *   Construct,5 *   Event,6 *   Point,7 *   Resolver,8 *   State,9 *   TokenizeContext,10 *   Tokenizer,11 *   Token12 * } from 'micromark-util-types'13 */14 15import { push, splice } from 'micromark-util-chunked';16import { classifyCharacter } from 'micromark-util-classify-character';17import { resolveAll } from 'micromark-util-resolve-all';18/** @type {Construct} */19export const attention = {20  name: 'attention',21  resolveAll: resolveAllAttention,22  tokenize: tokenizeAttention23};24 25/**26 * Take all events and resolve attention to emphasis or strong.27 *28 * @type {Resolver}29 */30// eslint-disable-next-line complexity31function resolveAllAttention(events, context) {32  let index = -1;33  /** @type {number} */34  let open;35  /** @type {Token} */36  let group;37  /** @type {Token} */38  let text;39  /** @type {Token} */40  let openingSequence;41  /** @type {Token} */42  let closingSequence;43  /** @type {number} */44  let use;45  /** @type {Array<Event>} */46  let nextEvents;47  /** @type {number} */48  let offset;49 50  // Walk through all events.51  //52  // Note: performance of this is fine on an mb of normal markdown, but it’s53  // a bottleneck for malicious stuff.54  while (++index < events.length) {55    // Find a token that can close.56    if (events[index][0] === 'enter' && events[index][1].type === 'attentionSequence' && events[index][1]._close) {57      open = index;58 59      // Now walk back to find an opener.60      while (open--) {61        // Find a token that can open the closer.62        if (events[open][0] === 'exit' && events[open][1].type === 'attentionSequence' && events[open][1]._open &&63        // If the markers are the same:64        context.sliceSerialize(events[open][1]).charCodeAt(0) === context.sliceSerialize(events[index][1]).charCodeAt(0)) {65          // If the opening can close or the closing can open,66          // and the close size *is not* a multiple of three,67          // but the sum of the opening and closing size *is* multiple of three,68          // then don’t match.69          if ((events[open][1]._close || events[index][1]._open) && (events[index][1].end.offset - events[index][1].start.offset) % 3 && !((events[open][1].end.offset - events[open][1].start.offset + events[index][1].end.offset - events[index][1].start.offset) % 3)) {70            continue;71          }72 73          // Number of markers to use from the sequence.74          use = events[open][1].end.offset - events[open][1].start.offset > 1 && events[index][1].end.offset - events[index][1].start.offset > 1 ? 2 : 1;75          const start = {76            ...events[open][1].end77          };78          const end = {79            ...events[index][1].start80          };81          movePoint(start, -use);82          movePoint(end, use);83          openingSequence = {84            type: use > 1 ? "strongSequence" : "emphasisSequence",85            start,86            end: {87              ...events[open][1].end88            }89          };90          closingSequence = {91            type: use > 1 ? "strongSequence" : "emphasisSequence",92            start: {93              ...events[index][1].start94            },95            end96          };97          text = {98            type: use > 1 ? "strongText" : "emphasisText",99            start: {100              ...events[open][1].end101            },102            end: {103              ...events[index][1].start104            }105          };106          group = {107            type: use > 1 ? "strong" : "emphasis",108            start: {109              ...openingSequence.start110            },111            end: {112              ...closingSequence.end113            }114          };115          events[open][1].end = {116            ...openingSequence.start117          };118          events[index][1].start = {119            ...closingSequence.end120          };121          nextEvents = [];122 123          // If there are more markers in the opening, add them before.124          if (events[open][1].end.offset - events[open][1].start.offset) {125            nextEvents = push(nextEvents, [['enter', events[open][1], context], ['exit', events[open][1], context]]);126          }127 128          // Opening.129          nextEvents = push(nextEvents, [['enter', group, context], ['enter', openingSequence, context], ['exit', openingSequence, context], ['enter', text, context]]);130 131          // Always populated by defaults.132 133          // Between.134          nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open + 1, index), context));135 136          // Closing.137          nextEvents = push(nextEvents, [['exit', text, context], ['enter', closingSequence, context], ['exit', closingSequence, context], ['exit', group, context]]);138 139          // If there are more markers in the closing, add them after.140          if (events[index][1].end.offset - events[index][1].start.offset) {141            offset = 2;142            nextEvents = push(nextEvents, [['enter', events[index][1], context], ['exit', events[index][1], context]]);143          } else {144            offset = 0;145          }146          splice(events, open - 1, index - open + 3, nextEvents);147          index = open + nextEvents.length - offset - 2;148          break;149        }150      }151    }152  }153 154  // Remove remaining sequences.155  index = -1;156  while (++index < events.length) {157    if (events[index][1].type === 'attentionSequence') {158      events[index][1].type = 'data';159    }160  }161  return events;162}163 164/**165 * @this {TokenizeContext}166 *   Context.167 * @type {Tokenizer}168 */169function tokenizeAttention(effects, ok) {170  const attentionMarkers = this.parser.constructs.attentionMarkers.null;171  const previous = this.previous;172  const before = classifyCharacter(previous);173 174  /** @type {NonNullable<Code>} */175  let marker;176  return start;177 178  /**179   * Before a sequence.180   *181   * ```markdown182   * > | **183   *     ^184   * ```185   *186   * @type {State}187   */188  function start(code) {189    marker = code;190    effects.enter('attentionSequence');191    return inside(code);192  }193 194  /**195   * In a sequence.196   *197   * ```markdown198   * > | **199   *     ^^200   * ```201   *202   * @type {State}203   */204  function inside(code) {205    if (code === marker) {206      effects.consume(code);207      return inside;208    }209    const token = effects.exit('attentionSequence');210 211    // To do: next major: move this to resolver, just like `markdown-rs`.212    const after = classifyCharacter(code);213 214    // Always populated by defaults.215 216    const open = !after || after === 2 && before || attentionMarkers.includes(code);217    const close = !before || before === 2 && after || attentionMarkers.includes(previous);218    token._open = Boolean(marker === 42 ? open : open && (before || !close));219    token._close = Boolean(marker === 42 ? close : close && (after || !open));220    return ok(code);221  }222}223 224/**225 * Move a point a bit.226 *227 * Note: `move` only works inside lines! It’s not possible to move past other228 * chunks (replacement characters, tabs, or line endings).229 *230 * @param {Point} point231 *   Point.232 * @param {number} offset233 *   Amount to move.234 * @returns {undefined}235 *   Nothing.236 */237function movePoint(point, offset) {238  point.column += offset;239  point.offset += offset;240  point._bufferIndex += offset;241}