tools/core/analyzers/marker.analyzer.ts

tools/core/analyzers/marker.analyzer.ts is a file in Coordination Surface. 109 lines of code and 28 definitions.

import { isWordCharacter } from "../predicates/token.predicate.ts";

const QUOTES = ["`", '"'];

const normalizedWords = function normalizedWords(text: string): string[] {
    const out: string[] = [];
    let word = "";

    for (let index = 0; index < text.length; index += 1) {
        const character = text.charAt(index).toLowerCase();
        const isLetter = character >= "a" && character <= "z";
        const isDigit = character >= "0" && character <= "9";

        if (isLetter || isDigit) {
            word += character;
            continue;
        }
        if (word.length > 0) {
            out.push(word);
        }
        word = "";
    }

    if (word.length > 0) {
        out.push(word);
    }
    return out;
};

export const repeatedSpan = function repeatedSpan(text: string, span: number): string | null {
    const words = normalizedWords(text);
    if (words.length < span * 2) {
        return null;
    }

    const seen = new Set<string>();

    for (let start = 0; start + span <= words.length; start += 1) {
        const run = words.slice(start, start + span).join(" ");
        if (seen.has(run)) {
            return run;
        }
        seen.add(run);
    }

    return null;
};

const balanced = function balanced(line: string, delimiter: string): boolean {
    let count = 0;
    for (let index = 0; index < line.length; index += 1) {
        if (line.charAt(index) === delimiter) {
            count += 1;
        }
    }

    return count > 0 && count % 2 === 0;
};

const spansFor = function spansFor(line: string, delimiter: string, quoted: boolean[]): void {
    let inside = false;

    for (let index = 0; index < line.length; index += 1) {
        if (line.charAt(index) === delimiter) {
            inside = !inside;
            quoted[index] = true;
            continue;
        }
        if (inside) {
            quoted[index] = true;
        }
    }
};

const quotedSpans = function quotedSpans(line: string): boolean[] {
    const quoted = Array.from({ length: line.length }, () => false);

    for (const delimiter of QUOTES) {
        if (!balanced(line, delimiter)) {
            continue;
        }
        spansFor(line, delimiter, quoted);
    }

    return quoted;
};

const joinedToWord = function joinedToWord(line: string, at: number): boolean {
    if (at === 0) {
        return false;
    }
    if (line.charAt(at - 1) !== "-") {
        return false;
    }

    return isWordCharacter(at < 2 ? "" : line.charAt(at - 2));
};

const terminatesClause = function terminatesClause(line: string, after: number): boolean {
    let cursor = after;
    while (cursor < line.length && line.charAt(cursor) === " ") {
        cursor += 1;
    }
    if (cursor >= line.length) {
        return true;
    }

    return !isWordCharacter(line.charAt(cursor));
};

const standsAlone = function standsAlone(line: string, at: number, marker: string): boolean {
    const before = at === 0 ? "" : line.charAt(at - 1);
    const after = line.charAt(at + marker.length);
    return !isWordCharacter(before) && !isWordCharacter(after) && !joinedToWord(line, at);
};

export const carriesMarker = function carriesMarker(line: string, marker: string): boolean {
    const quoted = quotedSpans(line);
    let from = 0;

    while (from <= line.length - marker.length) {
        const at = line.indexOf(marker, from);
        if (at === -1) {
            return false;
        }

        if (standsAlone(line, at, marker) && quoted[at] !== true && terminatesClause(line, at + marker.length)) {
            return true;
        }

        from = at + 1;
    }

    return false;
};