/** ANSI helpers for /bin/edit (preamble; no import in src). */ const EDIT_ANSI_RESET = '\x1b[0m' /** * @param {Record} [ctx] * @returns {import('stream').Writable | undefined} */ function bareEditResolveStdout(ctx) { if (!ctx || typeof ctx !== 'object') return globalThis.process?.stdout const c = /** @type {{ replStdout?: unknown, stdout?: unknown }} */ (ctx) const out = c.replStdout || c.stdout || globalThis.process?.stdout return /** @type {import('stream').Writable | undefined} */ (out) } /** * @param {Record} [ctx] */ function bareEditUseColor(ctx) { const env = ctx && typeof ctx === 'object' && ctx.env && typeof ctx.env === 'object' ? /** @type {Record} */ (ctx.env) : {} if (env.NO_COLOR != null && String(env.NO_COLOR) !== '') return false const out = bareEditResolveStdout(ctx) return Boolean(out && /** @type {{ isTTY?: boolean }} */ (out).isTTY) } /** * @param {'keyword'|'string'|'comment'|'number'|'status'|'inverse'|'dim'} cls * @param {boolean} on */ function bareEditSgr(cls, on) { if (!on) return '' switch (cls) { case 'keyword': return '\x1b[36m' case 'string': return '\x1b[32m' case 'comment': return '\x1b[90m' case 'number': return '\x1b[33m' case 'status': return '\x1b[44m\x1b[97m' case 'inverse': return '\x1b[7m' case 'dim': return '\x1b[2m' default: return '' } } /** * @param {string} fullLine * @param {Array<{ start: number, end: number, cls: string }>} spans offsets into fullLine * @param {number} visStart first column (0-based) * @param {number} maxLen max code units to show * @param {boolean} useColor */ function bareEditPaintLineWindow(fullLine, spans, visStart, maxLen, useColor) { const slice = fullLine.slice(visStart, visStart + maxLen) if (!useColor) return slice const n = slice.length const relSpans = spans .map((s) => ({ start: Math.max(0, s.start - visStart), end: Math.min(n, s.end - visStart), cls: s.cls })) .filter((s) => s.end > 0 && s.start < n) .sort((a, b) => a.start - b.start) let out = '' let pos = 0 for (const sp of relSpans) { if (sp.start > pos) out += slice.slice(pos, sp.start) out += bareEditSgr(/** @type {'keyword'} */ (sp.cls), true) + slice.slice(sp.start, sp.end) + EDIT_ANSI_RESET pos = sp.end } if (pos < n) out += slice.slice(pos) return out } /** * Move cursor (1-based row/col, DEC origin). Clamp to sane bounds for escape parsing. * @param {number} row1 * @param {number} col1 */ function bareEditCup(row1, col1) { const r = Math.max(1, Math.min(Math.floor(row1), 9999)) const c = Math.max(1, Math.min(Math.floor(col1), 9999)) return '\x1b[' + r + ';' + c + 'H' }