view
uses process
Show definitions, with current names. part shows only some of each one's code, numbered: lines ("120-180",
or several: "1-20,90-"), or the lines with grep in them and two lines either side (a large definition's one
method, say): its text, or a regular expression written /…/ (flags after it: /a|b/i), as [textPattern]
takes it. A name that isn't found is said first, with the names meant most likely (a namespace's, with
what's in it), and the rest are shown: 1 if any wasn't.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { fileContent, formatOf, isBytes } from "#codebase/tcmts/cli/files"; import { header, lineSpans } from "#codebase/tcmts/cli/main"; import { namesElsewhere } from "#codebase/tcmts/cli/merge"; import { noLines, textPattern } from "#codebase/tcmts/cli/pattern"; import { bold, dim, red } from "#codebase/tcmts/cli/style"; import { UsageError } from "#codebase/tcmts/cli/ui"; import { shortHash } from "#codebase/tcmts/core/hash"; import { render } from "#codebase/tcmts/loader"; export function view( cb: Codebase, targets: string[], part: { lines?: string; grep?: string } = {}, ): number { const spans = part.lines === undefined ? undefined : lineSpans(part.lines); if (part.lines !== undefined && !spans) throw new UsageError( `--lines ${part.lines}: give a line (120), lines (120-180), from a line on (120-), or several (1-20,90-)`, ); // (text matched in case, as written) const pattern = part.grep === undefined ? undefined : textPattern(part.grep, { caseSensitive: true }); if (pattern?.error !== undefined) throw new UsageError(`--grep ${part.grep}: ${pattern.error}`); const has = (line: string) => pattern!.find(line).length > 0; const show = (text: string) => { if (!spans && !part.grep) return process.stdout.write(text); const lines = text.split("\n"); if (lines.at(-1) === "") lines.pop(); const keep = new Set<number>(); for (const [from, to] of spans ?? []) for (let i = from - 1; i < Math.min(lines.length, to); i++) keep.add(i); if (part.grep) lines.forEach( (l, i) => has(l) && [-2, -1, 0, 1, 2].forEach((d) => i + d >= 0 && i + d < lines.length && keep.add(i + d)), ); const width = String(lines.length).length; let last = -2; for (const i of [...keep].sort((a, b) => a - b)) { if (i !== last + 1 && last >= 0) console.log(dim("…")); console.log(`${dim(String(i + 1).padStart(width))} ${lines[i]}`); last = i; } if (!keep.size) console.log( dim(part.grep && !spans ? noLines(part.grep) : `no such lines: it has ${lines.length}`), ); }; // what's found and what isn't, before anything is shown const found: { t: string; h: string }[] = []; const missing: string[] = []; for (const t of targets) { const hs = cb.lookup(t); if (hs.length) for (const h of hs) found.push({ t, h }); else missing.push(cb.notFound(t)); } for (const m of missing) console.error(red(m)); // what nothing here names (the other side of a merge's code uses it, say) shows by a name it has elsewhere const elsewhere = namesElsewhere(cb); for (const { t, h } of found) { const rec = cb.store.getDef(h)!; if (rec.kind === "file") { const format = formatOf(rec); const name = cb.store.pathsOf(h)[0] ?? t; console.log( `${dim("//")} ${bold(cb.store.pathsOf(h).join(", ") || "(unnamed)")} ${dim(shortHash(h))} ${dim(format)}`, ); const content = fileContent(cb, rec, name.slice(0, Math.max(0, name.lastIndexOf(".")))); if (isBytes(rec)) console.log(dim(`(${format || "bytes"}, ${content.length} bytes)`)); else show(`${content.toString("utf8")}\n`); continue; } console.log(header(cb, h)); const src = cb.source(); const unnamed = new Map<string, string>(); const nameOf = (d: string) => { const n = src.nameOf(d); if (n || d === h) return n; const k = elsewhere(d); if (k) unnamed.set(d, k.note); return k?.name; }; show(render([rec], src, cb.lay, { style: "scratch", exportDeclarations: true, nameOf }).text); if (unnamed.size) console.log( dim( `// not named on this branch: ${[...unnamed].map(([d, note]) => `${shortHash(d)} is ${note}`).join("; ")}`, ), ); console.log(); } return missing.length ? 1 : 0; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.files.fileContent
- tcmts.cli.files.formatOf
- tcmts.cli.files.isBytes
- tcmts.cli.main.header
- tcmts.cli.main.lineSpans
- tcmts.cli.merge.namesElsewhere
- tcmts.cli.pattern.noLines
- tcmts.cli.pattern.textPattern
- tcmts.cli.style.bold
- tcmts.cli.style.dim
- tcmts.cli.style.red
- tcmts.cli.ui.UsageError
- tcmts.core.hash.shortHash
- tcmts.loader.render