bnash
Version:
A command-line editor for bash or compatible shells, aiming to provide great UX and productivity
117 lines (103 loc) • 3.17 kB
JavaScript
const { runHiddenCommand } = require('../../runner')
function commandOut2Array(out) {
let arr = out.trim().split('\n')
let rc = arr.pop()
return rc === '0' ? arr : []
}
async function runCommand(cmd) {
let out = await runHiddenCommand(`${cmd} | cat; echo $?`)
return commandOut2Array(out)
}
function getManOut(cmd) {
return runCommand('man ' + cmd.replace(/ /g, '-'))
}
function removeBackChars(str) {
let result = ''
for (let i = 0; i < str.length; i++) {
let c = str.charAt(i)
if (i == 0 || c != '\b')
result += c
else
result = result.slice(0, -1)
}
return result
}
function isOptionsHeader(line) {
return line == 'OPTIONS' ||
line.trim() == 'The following options are available:' ||
line.trim() == 'The options are as follows:'
}
function cleanOpt(opt) {
return { name: opt.name, desc: opt.desc.trim() }
}
function parseManLine(line, lastLine, opt, opts) {
if (line.startsWith('-') && lastLine.length == 0) {
if (opt) opts.push(cleanOpt(opt))
let p = line.indexOf(' ')
opt = { name: line, desc: '' }
if (p > 0 && !line.substr(0, p).includes(' ')) {
opt.name = line.substr(0, p)
opt.desc = line.substr(p).trim()
}
}
else if (opt) {
if (line.length == 0) {
opt.done = true
}
else if (!opt.done) opt.desc += '\n' + line
}
return opt
}
function parseMan(lines) {
if (!lines || lines.length == 0) return []
let opts = []
let opt = null
let inOptions = false
let lastLine = ''
for (let line of lines) {
line = removeBackChars(line)
if (!inOptions) {
if (isOptionsHeader(line)) {
inOptions = true
continue
}
}
else {
if (line.length > 0 && line[0] != ' ') break
line = line.trim()
opt = parseManLine(line, lastLine, opt, opts)
}
lastLine = line
}
if (opt) opts.push(cleanOpt(opt))
return opts
}
async function parseOptions(cmd) {
let out = await getManOut(cmd)
out = out.map(l => l.trimRight())
return parseMan(out).map(opt => opt.name + '##' + opt.desc)
}
function removeUglyChars(str) {
return str.split('').filter(x => x == '\x1b' || x >= ' ').join('')
}
function wrap(str, w, maxLines) {
if (!str) return str
if (str.charAt(0) == '\x1b') return str // Colorized, shouldn't wrap
// Remove hypthens and put everything in a single line
str = str.replace(/([a-z])-\n/g, '$1').split('\n').join(' ')
// Split in lines of maximum `w` characters
let rexp = new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, 'g')
let lines = str.replace(rexp, '$1\n').split('\n')
// Limit to maximum maxLines, add ellipsis if more
if (lines.length > maxLines) {
lines = lines.slice(0, maxLines)
lines[maxLines - 1] = lines[maxLines - 1].substr(0, w - 3) + '...'
}
return lines
.map(l => removeUglyChars(l).substr(0, w))
.join('\n')
}
module.exports = {
parseOptions,
wrap
}