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;
};