All files / validation/rules bracket-rules.ts

100% Statements 34/34
100% Branches 12/12
100% Functions 2/2
100% Lines 32/32

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                    76x 76x   76x 1710x 1710x 26x 1684x 25x 22x   3x                 76x 4x             76x     2x       27x 27x 27x   27x 38x                                   38x     27x 27x 866x 866x 12x 854x 12x 11x   1x                 27x 1x             27x      
import { RuleMatch, ValidationRule } from '../models/validation-rule.model';
 
function findUnpairedInLine(
    line: string,
    lineStart: number,
    openChar: string,
    closeChar: string,
    openMessage: string,
    closeMessage: string,
): readonly RuleMatch[] {
    const matches: RuleMatch[] = [];
    const stack: number[] = [];
 
    for (let i = 0; i < line.length; i++) {
        const ch = line[i];
        if (ch === openChar) {
            stack.push(i);
        } else if (ch === closeChar) {
            if (stack.length > 0) {
                stack.pop();
            } else {
                matches.push({
                    from: lineStart + i,
                    to: lineStart + i + 1,
                    message: closeMessage,
                });
            }
        }
    }
 
    for (const pos of stack) {
        matches.push({
            from: lineStart + pos,
            to: lineStart + pos + 1,
            message: openMessage,
        });
    }
 
    return matches;
}
 
export const pairedBrackets: ValidationRule = {
    id: 'paired-brackets',
    defaultSeverity: 'warning',
    validate(text: string): readonly RuleMatch[] {
        const matches: RuleMatch[] = [];
        const lines = text.split('\n');
        let offset = 0;
 
        for (const line of lines) {
            matches.push(
                ...findUnpairedInLine(
                    line,
                    offset,
                    '(',
                    ')',
                    'Нет закрывающей скобки )',
                    'Нет открывающей скобки (',
                ),
                ...findUnpairedInLine(
                    line,
                    offset,
                    '{',
                    '}',
                    'Нет закрывающей скобки }',
                    'Нет открывающей скобки {',
                ),
            );
            offset += line.length + 1;
        }
 
        const squareStack: number[] = [];
        for (let i = 0; i < text.length; i++) {
            const ch = text[i];
            if (ch === '[') {
                squareStack.push(i);
            } else if (ch === ']') {
                if (squareStack.length > 0) {
                    squareStack.pop();
                } else {
                    matches.push({
                        from: i,
                        to: i + 1,
                        message: 'Нет открывающей скобки [',
                    });
                }
            }
        }
 
        for (const pos of squareStack) {
            matches.push({
                from: pos,
                to: pos + 1,
                message: 'Нет закрывающей скобки ]',
            });
        }
 
        return matches;
    },
};