spelledAlone
uses tcmts.loader.render, which uses node:path
A definition saved from a file holding others too (rows, analyzed from text), spelled as it reads alone, as
tcm view shows it: what writing them together made the file call otherwise (find_1, Move_1, join_1, a
name another there has) is stored with the name it has alone. A reference to the codebase is written with its
name, as alone; an import from outside it keeps the name it's imported as, unless that's another's name with a
number (join_1 where the file has a join), which alone it doesn't need. Only names change, so its hash doesn't
(see respelled); a function's or class's own name, part of its hash, stays.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { importBindings, respelled, sameNames } from "#codebase/tcmts/cli/main"; import type { Row } from "#codebase/tcmts/cli/main"; import { declarations } from "#codebase/tcmts/cli/mirror"; import { parse } from "#codebase/tcmts/core/ts"; import { render } from "#codebase/tcmts/loader"; import { SyntaxKind as ts_SyntaxKind } from "typescript/ast"; import type { Identifier as ts_Identifier, Node as ts_Node } from "typescript/ast"; export function spelledAlone(cb: Codebase, file: string, text: string, row: Row, rows: Row[]): Row { const def = row.def; if (def.kind === "file") return row; const paths = new Map(rows.map((r) => [r.def.hash, r.path])); const src = cb.source( rows.map((r) => r.def), (h) => paths.get(h) ?? cb.store.pathsOf(h)[0], ); let alone: ReturnType<typeof render>; try { alone = render([def], src, cb.lay, { style: "scratch", omitImports: true }); } catch { return row; } const start = alone.starts.get(def.hash); if (start === undefined) return row; const body = alone.text.slice(start).trimEnd().split("\n"); const was = def.sourceText.split("\n"); if (body.length !== was.length) return row; const names = new Map<string, string>(); for (const [i, l] of was.entries()) if (l !== body[i]) sameNames(l, body[i]!, names); // an import the file numbered, as it has another by the name const bound = new Set([...importBindings(file, text).keys(), ...declarations(file, text).keys()]); // (the names it has alone: identifiers, not a comment's or a string's words) const taken = new Set<string>(); const walk = (n: ts_Node) => { if (n.kind === ts_SyntaxKind.Identifier) taken.add((n as ts_Identifier).text); n.forEachChild(walk, (arr) => void arr.forEach(walk)); }; walk(parse(file, body.join("\n"))); for (const s of def.spans) { if (!s.ext || s.literal) continue; const written = def.sourceText.slice(s.start, s.end); const base = /^(.+)_\d+$/.exec(written)?.[1]; if (base && written !== s.ext.export && bound.has(base) && !taken.has(base)) names.set(written, base); } for (const [from, to] of names) if (from === to) names.delete(from); if (def.nominal) names.delete(def.nameHint); return names.size ? { ...row, def: respelled(def, names) } : row; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.main.Row
- tcmts.cli.main.importBindings
- tcmts.cli.main.respelled
- tcmts.cli.main.sameNames
- tcmts.cli.mirror.declarations
- tcmts.core.ts.parse
- tcmts.loader.render