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

94.54% Statements 52/55
87.5% Branches 21/24
100% Functions 15/15
94% Lines 47/50

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
    10723x

    23x
    23x
    23x
    23x

    23x

    24x

    8x
    8x
    8x
    8x
    20x
    20x

    34x
    34x
    34x
    34x
    190x
    26x
    26x

    190x

    34x
    34x

    34x

    24x
    36x

    36x

    4x

    20x

    16x
    8x
    8x
    8x
    8x

    22x
    22x

    24x
    4x

    20x
    10x

    2x

    8x

    8x

    16x
    4x

    12x

    34x

     import { HighlightRuleComponent } from './HighlightRuleComponent';

    export enum HIGHLIGHT_TYPES {
      WholeLine,
      WholeText,
      PartialText,
    }

    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');
        strArray.pop(); // removes the last empty string
        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) {
          if (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;
          }
        }
        if (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));

        if (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));
        if (this.isLineRange()) {
          const withinRangeStart = compares[0] <= 0;
          const withinRangeEnd = compares[1] >= 0;
          return withinRangeStart && withinRangeEnd;
        }

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

      getHighlightType(lineNumber: number): {
        highlightType: HIGHLIGHT_TYPES,
        bounds: Array<[number, number]> | null
      } {
        // Applied rule is the first component until deduced otherwise
        let [appliedRule] = this.ruleComponents;
        if (this.isLineRange()) {
          // For cases like 2[:]-3 (or 2-3[:]), the highlight would be line highlight
          // across all the ranges
          if (this.ruleComponents.some(comp => comp.isUnboundedSlice())) {
            return { highlightType: HIGHLIGHT_TYPES.WholeLine, bounds: null };
          }

          const [startCompare, endCompare] = this.ruleComponents.map(comp => comp.compareLine(lineNumber));
          if (startCompare < 0 && endCompare > 0) {
            // In-between range
            return { highlightType: HIGHLIGHT_TYPES.WholeText, bounds: null };
          }

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

        if (appliedRule.isSlice) {
          return appliedRule.isUnboundedSlice()
            ? { highlightType: HIGHLIGHT_TYPES.WholeLine, bounds: null }
            : { highlightType: HIGHLIGHT_TYPES.PartialText, bounds: appliedRule.bounds };
        }
        // Line number only
        return { highlightType: HIGHLIGHT_TYPES.WholeText, bounds: null };
      }

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