207 lines
6.0 KiB
JavaScript
207 lines
6.0 KiB
JavaScript
/** Plain-text manual formatter (prepended before src/man.js; no import in /bin/man). */
|
|
|
|
function bareManParseWidth(env) {
|
|
const raw = env && env.MANWIDTH != null ? String(env.MANWIDTH).trim() : ''
|
|
const n = raw ? Number.parseInt(raw, 10) : 72
|
|
if (!Number.isFinite(n)) return 72
|
|
return Math.max(40, Math.min(n, 200))
|
|
}
|
|
|
|
function bareManUseAnsi(ctx) {
|
|
const env = ctx.env || {}
|
|
if (env.NO_COLOR != null && String(env.NO_COLOR) !== '') return false
|
|
const out = ctx.stdout
|
|
return Boolean(out && out.isTTY)
|
|
}
|
|
|
|
function bareManBold(s, on) {
|
|
if (!on) return s
|
|
return '\x1b[1m' + s + '\x1b[0m'
|
|
}
|
|
|
|
function bareManWrap(text, width) {
|
|
const words = String(text).replace(/\s+/g, ' ').trim().split(' ')
|
|
const lines = []
|
|
let cur = ''
|
|
for (const w of words) {
|
|
const next = cur ? cur + ' ' + w : w
|
|
if (next.length <= width) cur = next
|
|
else {
|
|
if (cur) lines.push(cur)
|
|
cur = w.length > width ? w.slice(0, width) : w
|
|
while (cur.length > width) {
|
|
lines.push(cur.slice(0, width))
|
|
cur = cur.slice(width)
|
|
}
|
|
}
|
|
}
|
|
if (cur) lines.push(cur)
|
|
return lines
|
|
}
|
|
|
|
/** Indent fixed-width command lines; hard-wrap only when longer than width. */
|
|
function bareManRenderExampleCode(code, width) {
|
|
const indent = ' '
|
|
const max = Math.max(20, width - indent.length)
|
|
const out = []
|
|
for (const line of String(code).split('\n')) {
|
|
if (line.length <= max) {
|
|
out.push(indent + line)
|
|
continue
|
|
}
|
|
let rest = line
|
|
while (rest.length > max) {
|
|
out.push(indent + rest.slice(0, max))
|
|
rest = rest.slice(max)
|
|
}
|
|
if (rest) out.push(indent + rest)
|
|
}
|
|
return out.join('\n')
|
|
}
|
|
|
|
/** Keep newlines; hard-wrap long lines only (for handbook / preformatted text). */
|
|
function bareManRenderPreserve(text, width) {
|
|
const indent = ' '
|
|
const max = Math.max(20, width - indent.length)
|
|
const out = []
|
|
for (const line of String(text).split('\n')) {
|
|
if (line === '') {
|
|
out.push('')
|
|
continue
|
|
}
|
|
let rest = line
|
|
while (rest.length > max) {
|
|
out.push(indent + rest.slice(0, max))
|
|
rest = rest.slice(max)
|
|
}
|
|
out.push(indent + rest)
|
|
}
|
|
return out.join('\n') + '\n'
|
|
}
|
|
|
|
function bareManFlushBlock(lines, width, prefixFirst, prefixRest) {
|
|
const out = []
|
|
let first = true
|
|
for (const line of lines) {
|
|
const wrapped = bareManWrap(line, width - (first ? prefixFirst.length : prefixRest.length))
|
|
for (let i = 0; i < wrapped.length; i++) {
|
|
const p = i === 0 && first ? prefixFirst : prefixRest
|
|
out.push(p + wrapped[i])
|
|
first = false
|
|
}
|
|
}
|
|
return out.join('\n') + '\n'
|
|
}
|
|
|
|
function bareManRenderPage(page, ctx, width) {
|
|
const ansi = bareManUseAnsi(ctx)
|
|
const H = (s) => bareManBold(s, ansi) + '\n'
|
|
let s = ''
|
|
s += H('NAME')
|
|
s += page.name + '(' + page.section + ') - ' + page.title + '\n\n'
|
|
s += H('SYNOPSIS')
|
|
for (const line of page.synopsis) {
|
|
s += ' ' + line + '\n'
|
|
}
|
|
s += '\n'
|
|
s += H('DESCRIPTION')
|
|
if (page.descriptionMode === 'preserve') {
|
|
s += bareManRenderPreserve(page.description, width)
|
|
} else {
|
|
s += bareManFlushBlock([page.description], width, '', ' ')
|
|
}
|
|
if (page.options && page.options.length) {
|
|
s += '\n' + H('OPTIONS')
|
|
for (const o of page.options) {
|
|
const head = o.flag + '\t'
|
|
const rest = o.meaning
|
|
s += bareManFlushBlock([rest], width, ' ' + head, ' ')
|
|
}
|
|
}
|
|
if (page.examples && page.examples.length) {
|
|
s += '\n' + H('EXAMPLES')
|
|
s +=
|
|
bareManFlushBlock(
|
|
[
|
|
'tl;dr-style snippets (like cheat.sh). Copy, adapt paths; pipelines are shell-simulated on Bare OS.'
|
|
],
|
|
width,
|
|
'',
|
|
' '
|
|
) + '\n'
|
|
for (const ex of page.examples) {
|
|
if (ex.caption) {
|
|
s +=
|
|
bareManFlushBlock(
|
|
['# ' + ex.caption],
|
|
width,
|
|
'',
|
|
' '
|
|
) + '\n'
|
|
}
|
|
s += bareManRenderExampleCode(ex.code, width) + '\n\n'
|
|
}
|
|
}
|
|
if (page.environment && page.environment.length) {
|
|
s += '\n' + H('ENVIRONMENT')
|
|
for (const e of page.environment) s += ' ' + e + '\n'
|
|
}
|
|
if (page.files && page.files.length) {
|
|
s += '\n' + H('FILES')
|
|
for (const f of page.files) s += ' ' + f + '\n'
|
|
}
|
|
if (page.exitStatus && page.exitStatus.length) {
|
|
s += '\n' + H('EXIT STATUS')
|
|
for (const e of page.exitStatus) s += ' ' + e + '\n'
|
|
}
|
|
if (page.diagnostics && page.diagnostics.length) {
|
|
s += '\n' + H('DIAGNOSTICS')
|
|
for (const d of page.diagnostics) s += ' ' + d + '\n'
|
|
}
|
|
if (page.builtins && page.builtins.length) {
|
|
s += '\n' + H('SHELL BUILTINS')
|
|
for (const b of page.builtins) {
|
|
s += '\n' + bareManBold(b.name, ansi) + '\n'
|
|
if (b.synopsis && b.synopsis.length) {
|
|
for (const line of b.synopsis) s += ' ' + line + '\n'
|
|
}
|
|
s += bareManFlushBlock([b.description], width, ' ', ' ')
|
|
if (b.options && b.options.length) {
|
|
for (const o of b.options) {
|
|
const head = o.flag + '\t'
|
|
s += bareManFlushBlock([o.meaning], width, ' ' + head, ' ')
|
|
}
|
|
}
|
|
if (b.examples && b.examples.length) {
|
|
s += '\n' + bareManBold(' Examples', ansi) + '\n'
|
|
for (const ex of b.examples) {
|
|
if (ex.caption) {
|
|
s +=
|
|
bareManFlushBlock(
|
|
['# ' + ex.caption],
|
|
width,
|
|
' ',
|
|
' '
|
|
) + '\n'
|
|
}
|
|
s += bareManRenderExampleCode(ex.code, width) + '\n\n'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (page.bareOsNotes) {
|
|
s += '\n' + H('BARE OS NOTES')
|
|
s += bareManFlushBlock([page.bareOsNotes], width, '', ' ')
|
|
}
|
|
if (page.seeAlso && page.seeAlso.length) {
|
|
s += '\n' + H('SEE ALSO')
|
|
const parts = page.seeAlso.map((r) => r.name + '(' + r.section + ')')
|
|
s += ' ' + parts.join(', ') + '\n'
|
|
}
|
|
if (page.stub) {
|
|
s += '\n' + H('STATUS')
|
|
s += ' This command is a stub or intentionally limited on Bare OS.\n'
|
|
}
|
|
return s
|
|
}
|