# All files / src/lib/markdown-it/highlight HighlightRule.ts

5.17% Statements 3/58
0% Branches 0/22
0% Functions 0/18
5.76% Lines 3/52

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    12020x
    20x

    20x

     import { HighlightRuleComponent } from './HighlightRuleComponent';
    import { splitCodeAndIndentation } from './helper';

    export class HighlightRule {
      ruleComponents: HighlightRuleComponent[];
      constructor(ruleComponents: HighlightRuleComponent[]) {
        this.ruleComponents = ruleComponents;
      }

      static parseAllRules(allRules: string, lineOffset: number, tokenContent: string) {
        const highlightLines = this.splitByChar(allRules, ',');
        const strArray = tokenContent.split('\n');
        return highlightLines
          .map(ruleStr => HighlightRule.parseRule(ruleStr, lineOffset, strArray))
          .filter(rule => rule) as HighlightRule[]; // discards invalid rules
      }

      // this function splits allRules by a splitter while ignoring the splitter if it is within quotes
      static splitByChar(allRules: string, splitter: string) {
        const highlightRules = [];
        let isWithinQuote = false;
        let currentPosition = 0;
        for (let i = 0; i < allRules.length; i += 1) {
          Iif (allRules.charAt(i) === splitter && !isWithinQuote) {
            highlightRules.push(allRules.substring(currentPosition, i));
            currentPosition = i + 1;
          }
          // Checks if the current character is not an unescaped quotation mark
          Iif (allRules.charAt(i) === '\'' && (i === 0 || allRules.charAt(i - 1) !== '\\')) {
            isWithinQuote = !isWithinQuote;
          }
        }
        Iif (currentPosition !== allRules.length) {
          highlightRules.push(allRules.substring(currentPosition));
        }
        return highlightRules;
      }

      static parseRule(ruleString: string, lineOffset: number, lines: string[]) {
        const components = this.splitByChar(ruleString, '-')
          .map(compString => HighlightRuleComponent.parseRuleComponent(compString, lineOffset, lines));

        Iif (components.some(c => !c)) {
          // Not all components are properly parsed, which means
          // the rule itself is not proper
          return null;
        }

        return new HighlightRule(components as HighlightRuleComponent[]);
      }

      shouldApplyHighlight(lineNumber: number) {
        const compares = this.ruleComponents.map(comp => comp.compareLine(lineNumber));
        Iif (this.isLineRange()) {
          const withinRangeStart = compares[0] <= 0;
          const withinRangeEnd = compares[1] >= 0;
          return withinRangeStart && withinRangeEnd;
        }

        const atLineNumber = compares[0] === 0;
        return atLineNumber;
      }

      applyHighlight(line: string, lineNumber: number) {
        // Applied rule is the first component until deduced otherwise
        let [appliedRule] = this.ruleComponents;

        Iif (this.isLineRange()) {
          // For cases like 2[:]-3 (or 2-3[:]), the highlight would be line highlight
          // across all the ranges
          const shouldWholeLine = this.ruleComponents.some(comp => comp.isUnboundedSlice());
          Iif (shouldWholeLine) {
            return HighlightRule._highlightWholeLine(line);
          }

          const [startCompare, endCompare] = this.ruleComponents.map(comp => comp.compareLine(lineNumber));
          Iif (startCompare < 0 && endCompare > 0) {
            // In-between range
            return HighlightRule._highlightWholeText(line);
          }

          // At the range boundaries
          const [startRule, endRule] = this.ruleComponents;
          appliedRule = startCompare === 0 ? startRule : endRule;
        }

        Iif (appliedRule.isSlice) {
          return appliedRule.isUnboundedSlice()
            ? HighlightRule._highlightWholeLine(line)
            : HighlightRule._highlightPartOfText(line, appliedRule.bounds);
        }

        // Line number only
        return HighlightRule._highlightWholeText(line);
      }

      static _highlightWholeLine(codeStr: string) {
        return `<span class="highlighted">${codeStr}\n</span>`;
      }

      static _highlightWholeText(codeStr: string) {
        const [indents, content] = splitCodeAndIndentation(codeStr);
        return `<span>${indents}<span class="highlighted">${content}</span>\n</span>`;
      }

      static _highlightPartOfText(codeStr: string, bounds: number[][]) {
        /*
         * Note: As part-of-text highlighting requires walking over the node of the generated
         * html by highlight.js, highlighting will be applied in NodeProcessor instead.
         * hl-data is used to pass over the bounds.
         */
        const dataStr = bounds.map(bound => bound.join('-')).join(',');
        return `<span hl-data=${dataStr}>${codeStr}\n</span>`;
      }

      isLineRange() {
        return this.ruleComponents.length === 2;
      }
    }