rittenhop-ghost/versions/5.94.2/node_modules/sywac/buffer.js

466 lines
16 KiB
JavaScript

'use strict'
class Buffer {
static get (opts) {
return new Buffer(opts)
}
constructor (opts) {
opts = opts || {}
// output settings
this._lineSep = opts.lineSep || '\n'
this._sectionSep = opts.sectionSep || this._lineSep + this._lineSep
this._pad = opts.pad || ' '
this._indent = opts.indent || this._pad + this._pad
this._split = opts.split || /\s/g // note that global is needed for the chunk method and doesn't affect split() usage
this._maxWidth = opts.maxWidth || Math.min(process.stdout.columns || 100, 100)
this._examplePrefix = 'examplePrefix' in opts ? opts.examplePrefix : '$ '
// preface
this._icon = opts.icon || ''
this._slogan = opts.slogan || ''
// usage
this._usage = opts.usage || ''
this._usageName = opts.usageName || ''
this._usagePrefix = 'usagePrefix' in opts ? opts.usagePrefix : 'Usage: $0'
this._usageHasCommand = 'usageHasCommand' in opts ? opts.usageHasCommand : false
this._usageCommandPlaceholder = 'usageCommandPlaceholder' in opts ? opts.usageCommandPlaceholder : '<command>'
this._usageHasArgs = 'usageHasArgs' in opts ? opts.usageHasArgs : false
this._usageArgsPlaceholder = 'usageArgsPlaceholder' in opts ? opts.usageArgsPlaceholder : '<args>'
this._usageHasOptions = 'usageHasOptions' in opts ? opts.usageHasOptions : false
this._usageOptionsPlaceholder = 'usageOptionsPlaceholder' in opts ? opts.usageOptionsPlaceholder : '[options]'
this._usagePositionals = Array.isArray(opts.usagePositionals) ? opts.usagePositionals : []
// types
this._groups = opts.groups || {} // maps heading to array of types, see Type.toObject and Context.addDeferredHelp
this._groupOrder = opts.groupOrder || []
// examples
this._examples = opts.examples || {} // see Api.example
this._exampleOrder = opts.exampleOrder || []
// epilogue
this._epilogue = opts.epilogue || ''
// cli/validation/error messages
this._showHelpOnError = 'showHelpOnError' in opts ? opts.showHelpOnError : true
this._messages = opts.messages || []
// style hooks
this._styleUsagePrefix = opts.styleUsagePrefix
this._styleUsagePositionals = opts.styleUsagePositionals
this._styleUsageCommandPlaceholder = opts.styleUsageCommandPlaceholder
this._styleUsageArgsPlaceholder = opts.styleUsageArgsPlaceholder
this._styleUsageOptionsPlaceholder = opts.styleUsageOptionsPlaceholder
this._styleGroup = opts.styleGroup
this._styleGroupError = opts.styleGroupError
this._styleFlags = opts.styleFlags
this._styleFlagsError = opts.styleFlagsError
this._styleDesc = opts.styleDesc
this._styleDescError = opts.styleDescError
this._styleHints = opts.styleHints
this._styleHintsError = opts.styleHintsError
this._styleMessages = opts.styleMessages
this._styleExample = opts.styleExample
this._styleAll = opts.styleAll
// dependencies
this._utils = opts.utils
}
get utils () {
if (!this._utils) this._utils = require('./lib/utils').get()
return this._utils
}
get lineSep () {
return this._lineSep
}
get sectionSep () {
return this._sectionSep
// return this.lineSep + new Array(this.maxWidth + 1).join('-') + this.lineSep // just checking
}
get pad () {
return this._pad
}
get indent () {
return this._indent
}
get split () {
return this._split
}
get maxWidth () {
return this._maxWidth
}
get icon () {
return this._icon
}
get slogan () {
return this._slogan
}
get usage () {
let usage
if (typeof this._usage === 'function') usage = this._usage(this._usageName)
else if (this._usage) usage = this._usage
else usage = this.buildUsage()
return this._usageName ? usage.replace('$0', this._usageName) : usage
}
buildUsage () {
// _usagePrefix + (_usagePositionals.join(' ') || _usageHasCommand + _usageHasArgs) + _usageHasOptions
let usage = this.styleUsagePrefix(typeof this._usagePrefix === 'function' ? this._usagePrefix(this._usageName) : this._usagePrefix)
if (this._usagePositionals.length) {
usage += ' ' + this.styleUsagePositionals(this._usagePositionals.join(' '))
} else {
if (this._usageHasCommand && this._usageCommandPlaceholder) {
usage += ' ' + this.styleUsageCommandPlaceholder(typeof this._usageCommandPlaceholder === 'function' ? this._usageCommandPlaceholder(this._usageName) : this._usageCommandPlaceholder)
}
if (this._usageHasArgs && this._usageArgsPlaceholder) {
usage += ' ' + this.styleUsageArgsPlaceholder(typeof this._usageArgsPlaceholder === 'function' ? this._usageArgsPlaceholder(this._usageName) : this._usageArgsPlaceholder)
}
}
if (this._usageHasOptions && this._usageOptionsPlaceholder) {
usage += ' ' + this.styleUsageOptionsPlaceholder(typeof this._usageOptionsPlaceholder === 'function' ? this._usageOptionsPlaceholder(this._usageName) : this._usageOptionsPlaceholder)
}
return usage
}
get groups () {
return this._groups
}
set groups (g) {
this._groups = g
}
get groupOrder () {
return this._groupOrder
}
get examples () {
return this._examples
}
get exampleOrder () {
return this._exampleOrder
}
get messages () {
return this._messages || []
}
set messages (m) {
this._messages = m
}
get epilogue () {
return this._epilogue
}
get styleUsagePrefix () {
return typeof this._styleUsagePrefix === 'function' ? this._styleUsagePrefix : s => s
}
get styleUsagePositionals () {
return typeof this._styleUsagePositionals === 'function' ? this._styleUsagePositionals : s => s
}
get styleUsageCommandPlaceholder () {
return typeof this._styleUsageCommandPlaceholder === 'function' ? this._styleUsageCommandPlaceholder : s => s
}
get styleUsageArgsPlaceholder () {
return typeof this._styleUsageArgsPlaceholder === 'function' ? this._styleUsageArgsPlaceholder : s => s
}
get styleUsageOptionsPlaceholder () {
return typeof this._styleUsageOptionsPlaceholder === 'function' ? this._styleUsageOptionsPlaceholder : s => s
}
get styleGroup () {
return typeof this._styleGroup === 'function' ? this._styleGroup : s => s
}
get styleGroupError () {
return typeof this._styleGroupError === 'function' ? this._styleGroupError : this.styleGroup
}
get styleFlags () {
return typeof this._styleFlags === 'function' ? this._styleFlags : s => s
}
get styleFlagsError () {
return typeof this._styleFlagsError === 'function' ? this._styleFlagsError : this.styleFlags
}
get styleDesc () {
return typeof this._styleDesc === 'function' ? this._styleDesc : s => s
}
get styleDescError () {
return typeof this._styleDescError === 'function' ? this._styleDescError : this.styleDesc
}
get styleHints () {
return typeof this._styleHints === 'function' ? this._styleHints : s => s
}
get styleHintsError () {
return typeof this._styleHintsError === 'function' ? this._styleHintsError : this.styleHints
}
get styleMessages () {
return typeof this._styleMessages === 'function' ? this._styleMessages : s => s
}
get styleExample () {
return typeof this._styleExample === 'function' ? this._styleExample : this.styleFlags
}
get styleAll () {
return typeof this._styleAll === 'function' ? this._styleAll : s => s
}
toString (opts) {
opts = Object.assign({
includePreface: true,
includeUsage: true,
includeGroups: true,
includeExamples: true,
includeEpilogue: true
}, opts)
let str = (this._showHelpOnError || !this.messages.length) ? this.helpContent(opts) : ''
str = this.appendSection(str, this.errorContent(opts), this.sectionSep)
return this.styleAll(str, opts)
}
helpContent (opts) {
opts = opts || {}
let str = this.appendSection('', !!opts.includePreface && this.icon, this.sectionSep)
if (opts.includePreface) str = this.appendSection(str, this.slogan, this.lineSep)
if (opts.includeUsage) str = this.appendSection(str, this.usage, this.sectionSep)
if (opts.includeGroups) str = this.appendSection(str, this.groupsContent(), this.sectionSep)
if (opts.includeExamples) str = this.appendSection(str, this.examplesContent(), this.sectionSep)
if (opts.includeEpilogue) str = this.appendSection(str, this.epilogue, this.sectionSep)
return str
}
groupsContent () {
return this.buildGroupedContent(this.groups, this.groupOrder, this.appendGroup)
}
examplesContent () {
return this.buildGroupedContent(this.examples, this.exampleOrder, this.appendExampleGroup)
}
buildGroupedContent (groups, order, appendMethod) {
let str = ''
const groupsLeft = JSON.parse(JSON.stringify(groups))
if (!order || !order.length) {
// default order: Commands, Arguments, <custom>, Options
order = Array.from(new Set(['Commands:', 'Arguments:'].concat(Object.keys(groupsLeft)).concat('Options:')))
}
let types
const handleGroup = heading => {
types = (groupsLeft[heading] || []).filter(type => !type.isHidden)
if (!types.length) {
delete groupsLeft[heading]
return
}
str = appendMethod.call(this, str, heading, types)
delete groupsLeft[heading]
}
// add groups mentioned in the order first
order.forEach(handleGroup)
// then add any groups not mentioned in the order
Object.keys(groupsLeft).forEach(handleGroup)
return str
}
appendGroup (str, heading, typeObjects) {
// first determine width needed for all flags
let flagsWidth = 0
let anyInvalid = false
const types = typeObjects.map(type => {
if (type.invalid) anyInvalid = true
return Object.assign({}, type, {
helpFlags: type.invalid ? this.styleFlagsError(type.helpFlags, type) : this.styleFlags(type.helpFlags, type),
helpDesc: type.invalid ? this.styleDescError(type.helpDesc, type) : this.styleDesc(type.helpDesc, type),
helpHints: type.invalid ? this.styleHintsError(type.helpHints, type) : this.styleHints(type.helpHints, type)
})
})
types.forEach(type => {
if (type.helpFlags) flagsWidth = Math.max(flagsWidth, this.utils.stripAnsi(type.helpFlags).length)
})
const maxWidth = Math.max(this.maxWidth, this.indent.length + flagsWidth)
if (heading) {
const styledHeading = anyInvalid ? this.styleGroupError(heading) : this.styleGroup(heading)
str = this.appendSection(str, styledHeading, this.sectionSep)
}
// if any types in this group require multi line, then just do all of them multi line
const multiline = types.some(type => this.determineLines(type, flagsWidth, maxWidth) > 1)
// then add each line:
// indent + flags + padding + indent + ((desc + padding + hints) || (descMultiline + hintsMultiline))
let first = true
types.forEach(type => {
if (multiline && str && !first) str += this.lineSep
first = false
str = multiline ? this.appendTypeMultiLine(str, type, flagsWidth, maxWidth) : this.appendTypeSingleLine(str, type, flagsWidth, maxWidth)
})
return str
}
appendExampleGroup (str, heading, examples) {
if (heading) str = this.appendSection(str, this.styleGroup(heading), this.sectionSep)
let flags
let desc
let first = true
examples.forEach(example => {
desc = example.description || example.desc
if (str && !first && desc) str += this.lineSep
first = false
flags = !example.flags ? '' : (this._examplePrefix || '') + example.flags
if (flags && this._usageName) flags = flags.replace('$0', this._usageName)
str = this.appendTypeMultiLine(str, {
helpDesc: this.styleDesc(desc, example),
helpHints: this.styleExample(flags, example)
}, 0, this.maxWidth)
})
return str
}
errorContent () {
return this.messages.map(s => this.styleMessages(s)).join(this.lineSep)
}
determineLines (type, flagsWidth, maxWidth) {
if (!type.helpFlags && !type.helpDesc && !type.helpHints) return 0
let singleLineWidth = 0
if (type.helpFlags) singleLineWidth += this.indent.length + flagsWidth
if (type.helpDesc) singleLineWidth += this.indent.length + this.utils.stripAnsi(type.helpDesc).length
if (type.helpHints) singleLineWidth += this.indent.length + this.utils.stripAnsi(type.helpHints).length
return singleLineWidth <= maxWidth ? 1 : 2
}
appendTypeSingleLine (str, type, flagsWidth, maxWidth) {
const flag = type.helpFlags
const desc = type.helpDesc
const hint = type.helpHints
let line = ''
if (flag) {
line += this.indent + flag + new Array(this.pos(flagsWidth, flag)).join(this.pad)
} else if (flagsWidth > 0) {
line += this.indent + new Array(flagsWidth + 1).join(this.pad)
}
if (desc) {
line += this.indent + desc
}
if (hint) {
line += new Array(this.pos(maxWidth, line + hint)).join(this.pad)
line += hint
}
return this.appendSection(str, line, this.lineSep)
}
appendTypeMultiLine (str, type, flagsWidth, maxWidth) {
const flag = type.helpFlags
let desc = type.helpDesc
let hint = type.helpHints
let line = ''
let leftOverWidth = maxWidth
if (flag) {
line += this.indent + flag + new Array(this.pos(flagsWidth, flag)).join(this.pad)
} else if (flagsWidth > 0) {
line += this.indent + new Array(flagsWidth + 1).join(this.pad)
}
if (line) leftOverWidth -= this.utils.stripAnsi(line).length
if (desc || hint) leftOverWidth -= this.indent.length
if (desc) {
const chunks = this.chunk(desc, leftOverWidth)
desc = chunks.shift()
let first = true
while (desc) {
if (!first) line = ''
if (!first && flagsWidth > 0) line += this.indent + new Array(flagsWidth + 1).join(this.pad)
first = false
line += this.indent + desc
str = this.appendSection(str, line, this.lineSep)
desc = chunks.shift()
}
line = ''
}
if (hint) {
const chunks = this.chunk(hint, leftOverWidth)
hint = chunks.shift()
let first = !type.helpDesc
while (hint) {
if (!first) line = ''
if (!first && flagsWidth > 0) line += this.indent + new Array(flagsWidth + 1).join(this.pad)
first = false
line += this.indent + hint
str = this.appendSection(str, line, this.lineSep)
hint = chunks.shift()
}
}
return str
}
// split a string into an array of chunks which are each <= width
chunk (str, width) {
const chunks = []
let chunk
let ansiDiff
let index
let noAnsi
let attempts = 0
while (str && ++attempts <= 999) {
noAnsi = this.utils.stripAnsi(str)
index = noAnsi.length <= width ? width : this.lastIndexOfRegex(noAnsi, this.split, width)
if (index < 1) index = Math.max(width, 1)
// TODO this ain't cutting it for ansi reconstitution
chunk = str.slice(0, index).trim()
ansiDiff = chunk.length - this.utils.stripAnsi(chunk).length
if (ansiDiff > 0) {
index += ansiDiff
chunk = str.slice(0, index).trim()
}
chunks.push(chunk)
// prep for next iteration
str = str.slice(index)
}
return chunks
}
lastIndexOfRegex (str, regex, fromIndex) {
// based on http://stackoverflow.com/a/21420210/1174467
str = fromIndex ? str.substring(0, fromIndex) : str
const match = str.match(regex)
return match ? str.lastIndexOf(match[match.length - 1]) : -1
}
// width diff
pos (w, s) {
return Math.max(0, w - this.utils.stripAnsi(s).length + 1)
}
appendSection (str, section, sep) {
if (section && str.length) str += sep
if (section) str += section
return str
}
}
module.exports = Buffer