core/converters/code.converter.ts

core/converters/code.converter.ts is a file in Bane's Lab Site. 134 lines of code and 32 definitions.

import type { CodeTarget, ReferenceLookups, SourceReferences } from "#types/code.types";
import { DIGITS, LETTERS } from "#configuration/constants/syntax.constants";
import type { DefinitionIndex } from "#types/definition.types";
import { LINE_BREAK } from "#configuration/constants/code.constants";
import type { MarkdownNode } from "#types/markdown.types";
import { QUOTE_MARKS } from "#configuration/constants/anatomy.constants";
import { bindingSitesOf } from "#core/converters/binding.converter";
import { inlineCode } from "#core/normalizers/markdown.normalizer";
import { literalTarget } from "#core/converters/literal.converter";
import { parseMarkdown } from "#core/converters/markdown.converter";

const WORD_CHARS = LETTERS + DIGITS;

export const wordsOf = function wordsOf(text: string): string[] {
    const parts: string[] = [];
    let current = "";
    let word = false;
    for (const char of text) {
        const isWord = WORD_CHARS.includes(char);
        if (current.length > 0 && isWord !== word) {
            parts.push(current);
            current = "";
        }
        current += char;
        word = isWord;
    }
    return current.length === 0 ? parts : [...parts, current];
};

const quotedIn = function quotedIn(line: string): string[] {
    const found: string[] = [];
    let at = 0;
    while (at < line.length) {
        const mark = line.charAt(at);
        const close = QUOTE_MARKS.includes(mark) ? line.indexOf(mark, at + 1) : -1;
        if (close !== -1) {
            found.push(line.slice(at + 1, close));
        }
        at = close === -1 ? at + 1 : close + 1;
    }
    return found;
};

export const unquoted = function unquoted(text: string): string {
    const trimmed = text.trim();
    const first = trimmed.charAt(0);
    const last = trimmed.slice(-1);
    return first.length > 0 && QUOTE_MARKS.includes(first) && QUOTE_MARKS.includes(last)
        ? trimmed.slice(1, -1)
        : trimmed;
};

const proseOf = function proseOf(node: MarkdownNode): readonly string[] {
    switch (node.kind) {
        case "list": {
            return node.items;
        }
        case "paragraph":
        case "quote": {
            return [node.text];
        }
        case "table": {
            return [...node.headers, ...node.rows.flat()];
        }
        case "code":
        case "heading":
        case "rule": {
            return [];
        }
        default: {
            return [];
        }
    }
};

const nameTarget = function nameTarget(index: DefinitionIndex, name: string, path: string): CodeTarget | null {
    const own = index.moduleDefinitionsNamedFrom(name, path);
    const locations = own.length > 0 ? own : index.moduleDefinitionsNamed(name);
    const [only] = locations;
    if (locations.length === 1 && only !== undefined) {
        return { kind: "definition", location: only };
    }
    return locations.length > 1 ? { kind: "candidates", locations } : null;
};

const spanTarget = function spanTarget(index: DefinitionIndex, text: string, path: string): CodeTarget | null {
    const file = index.resolvePath(text, path) ?? index.resolveSpecifier(text, path);
    if (file !== null) {
        return { kind: "file", path: file };
    }
    const folder = index.resolveFolder(text, path);
    return folder === null ? nameTarget(index, text, path) : { kind: "folder", path: folder };
};

const targetsOf = function targetsOf(
    texts: Iterable<string>,
    target: (text: string) => CodeTarget | null,
): Record<string, CodeTarget> {
    const out = new Map<string, CodeTarget>();
    for (const text of new Set(texts)) {
        const found = target(text);
        if (found !== null) {
            out.set(text, found);
        }
    }
    return Object.fromEntries(out);
};

const NO_LOOKUPS: ReferenceLookups = {
    bindings: () => null,
    records: new Set(),
    ruleRecord: () => null,
    slotDeclaration: () => null,
};

export const codeReferencesOf = function codeReferencesOf(
    index: DefinitionIndex,
    path: string,
    text: string,
    document: boolean,
    lookups: ReferenceLookups = NO_LOOKUPS,
): SourceReferences {
    const bindings = document ? null : lookups.bindings(path);
    const words = bindings === null ? wordsOf(text).filter((part) => WORD_CHARS.includes(part.charAt(0))) : [];
    const spans = document ? parseMarkdown(text).flatMap(proseOf).flatMap(inlineCode) : [];
    const { sites, targets } = bindingSitesOf(index, bindings ?? []);
    return {
        sites,
        spans: targetsOf(spans, (span) =>
            lookups.records.has(span) ? { kind: "record", ref: span } : spanTarget(index, span, path),
        ),
        strings: targetsOf(text.split(LINE_BREAK).flatMap(quotedIn), (quoted) =>
            literalTarget(index, lookups, quoted, path, text),
        ),
        targets,
        words: targetsOf(words, (word) => nameTarget(index, word, path)),
    };
};

export const targetIn = function targetIn(
    targets: Readonly<Record<string, CodeTarget>>,
    text: string,
): CodeTarget | null {
    return Object.hasOwn(targets, text) ? (targets[text] ?? null) : null;
};