nameInScratch
The name at offset in a scratch file, as references and renaming see it: the identifier, its symbol here, where the
file uses it (offsets, its own declaration or import included), and, for a name the codebase has (one it imports, or
a definition the file holds that's saved), that name and its hash.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { classify } from "#codebase/tcmts/cli/main"; import type { Analysis } from "#codebase/tcmts/core/analyze"; import { cacheRefOf } from "#codebase/tcmts/core/shape"; import { declarationOf } from "#codebase/tcmts/core/ts"; import { identifierAt } from "#codebase/tcmts/studio"; import { isImportSpecifier as ts_isImportSpecifier } from "typescript/ast"; import type { Node as ts_Node } from "typescript/ast"; import { SymbolFlags as ts_SymbolFlags } from "typescript/sync"; export function nameInScratch(cb: Codebase, a: Analysis, abs: string, offset: number) { const sf = a.program.getSourceFile(abs); const id = identifierAt(a, abs, offset); if (!sf || !id) return undefined; const checker = a.checker; const sym = checker.getSymbolAtLocation(id); if (!sym) return undefined; const nodes = new Map<number, ts_Node>(); try { for (const h of checker.getReferencesToSymbolInFile(abs, sym)) { const n = h.resolve() as ts_Node | undefined; if (n) nodes.set(n.getStart(sf), n); } } catch { // (the compiler has let this program go: the identifier itself, then) } nodes.set(id.getStart(sf), id); const decl = declarationOf(sym); const named = decl && (decl as ts_Node & { name?: ts_Node }).name; if (named && named.getSourceFile() === sf && named.getText(sf) === id.text) nodes.set(named.getStart(sf), named); const uses = [...nodes.values()] .filter((n) => n.getText(sf) === id.text) .map((n) => ({ from: n.getStart(sf), to: n.end })) .sort((x, y) => x.from - y.from); const target = sym.flags & ts_SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym; let hash = cacheRefOf(checker)(target); let name = hash ? cb.store.pathsOf(cb.store.patched(hash))[0] : undefined; // (a definition of the file's own that's saved: the codebase has its name) if (!name && named && !a.diagnostics.length && !a.errors.length) { const def = a.defs.find((d) => d.range.nameStart === named.getStart(sf)); const row = def && classify(cb, a.defs, a.namespace ?? "").find((r) => r.def === def); if (row && (row.status === "identical" || row.status === "update")) { name = row.path; hash = cb.lookup(row.path)[0]; } } const spec = decl && ts_isImportSpecifier(decl) ? decl : undefined; return { id, sf, sym, uses, spec, imported: !!(sym.flags & ts_SymbolFlags.Alias), ...(name && hash ? { name, hash } : {}), }; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.main.classify
- tcmts.core.analyze.Analysis
- tcmts.core.shape.cacheRefOf
- tcmts.core.ts.declarationOf
- tcmts.studio.identifierAt