99 lines
2.8 KiB
JavaScript
99 lines
2.8 KiB
JavaScript
/** ANSI helpers for /bin/edit (preamble; no import in src). */
|
|
|
|
const EDIT_ANSI_RESET = '\x1b[0m'
|
|
|
|
/**
|
|
* @param {Record<string, unknown>} [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<string, unknown>} [ctx]
|
|
*/
|
|
function bareEditUseColor(ctx) {
|
|
const env =
|
|
ctx && typeof ctx === 'object' && ctx.env && typeof ctx.env === 'object'
|
|
? /** @type {Record<string, string>} */ (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'
|
|
}
|