123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- 'use strict'
- const wordsep_simple_re = /([\t\n\x0b\x0c\r ]+)/
- class TextWrapper {
-
- constructor(options = {}) {
- let {
- width = 70,
- initial_indent = '',
- subsequent_indent = '',
- expand_tabs = true,
- replace_whitespace = true,
- fix_sentence_endings = false,
- break_long_words = true,
- drop_whitespace = true,
- break_on_hyphens = true,
- tabsize = 8,
- max_lines = undefined,
- placeholder=' [...]'
- } = options
- this.width = width
- this.initial_indent = initial_indent
- this.subsequent_indent = subsequent_indent
- this.expand_tabs = expand_tabs
- this.replace_whitespace = replace_whitespace
- this.fix_sentence_endings = fix_sentence_endings
- this.break_long_words = break_long_words
- this.drop_whitespace = drop_whitespace
- this.break_on_hyphens = break_on_hyphens
- this.tabsize = tabsize
- this.max_lines = max_lines
- this.placeholder = placeholder
- }
-
-
- _munge_whitespace(text) {
-
- if (this.expand_tabs) {
- text = text.replace(/\t/g, ' '.repeat(this.tabsize))
- }
- if (this.replace_whitespace) {
- text = text.replace(/[\t\n\x0b\x0c\r]/g, ' ')
- }
- return text
- }
- _split(text) {
-
- let chunks = text.split(wordsep_simple_re)
- chunks = chunks.filter(Boolean)
- return chunks
- }
- _handle_long_word(reversed_chunks, cur_line, cur_len, width) {
-
-
-
- let space_left
- if (width < 1) {
- space_left = 1
- } else {
- space_left = width - cur_len
- }
-
-
- if (this.break_long_words) {
- cur_line.push(reversed_chunks[reversed_chunks.length - 1].slice(0, space_left))
- reversed_chunks[reversed_chunks.length - 1] = reversed_chunks[reversed_chunks.length - 1].slice(space_left)
-
-
-
- } else if (!cur_line) {
- cur_line.push(...reversed_chunks.pop())
- }
-
-
-
-
-
- }
- _wrap_chunks(chunks) {
-
- let lines = []
- let indent
- if (this.width <= 0) {
- throw Error(`invalid width ${this.width} (must be > 0)`)
- }
- if (this.max_lines !== undefined) {
- if (this.max_lines > 1) {
- indent = this.subsequent_indent
- } else {
- indent = this.initial_indent
- }
- if (indent.length + this.placeholder.trimStart().length > this.width) {
- throw Error('placeholder too large for max width')
- }
- }
-
-
- chunks = chunks.reverse()
- while (chunks.length > 0) {
-
-
- let cur_line = []
- let cur_len = 0
-
- let indent
- if (lines) {
- indent = this.subsequent_indent
- } else {
- indent = this.initial_indent
- }
-
- let width = this.width - indent.length
-
-
- if (this.drop_whitespace && chunks[chunks.length - 1].trim() === '' && lines.length > 0) {
- chunks.pop()
- }
- while (chunks.length > 0) {
- let l = chunks[chunks.length - 1].length
-
- if (cur_len + l <= width) {
- cur_line.push(chunks.pop())
- cur_len += l
-
- } else {
- break
- }
- }
-
-
- if (chunks.length && chunks[chunks.length - 1].length > width) {
- this._handle_long_word(chunks, cur_line, cur_len, width)
- cur_len = cur_line.map(l => l.length).reduce((a, b) => a + b, 0)
- }
-
- if (this.drop_whitespace && cur_line.length > 0 && cur_line[cur_line.length - 1].trim() === '') {
- cur_len -= cur_line[cur_line.length - 1].length
- cur_line.pop()
- }
- if (cur_line) {
- if (this.max_lines === undefined ||
- lines.length + 1 < this.max_lines ||
- (chunks.length === 0 ||
- this.drop_whitespace &&
- chunks.length === 1 &&
- !chunks[0].trim()) && cur_len <= width) {
-
-
- lines.push(indent + cur_line.join(''))
- } else {
- let had_break = false
- while (cur_line) {
- if (cur_line[cur_line.length - 1].trim() &&
- cur_len + this.placeholder.length <= width) {
- cur_line.push(this.placeholder)
- lines.push(indent + cur_line.join(''))
- had_break = true
- break
- }
- cur_len -= cur_line[-1].length
- cur_line.pop()
- }
- if (!had_break) {
- if (lines) {
- let prev_line = lines[lines.length - 1].trimEnd()
- if (prev_line.length + this.placeholder.length <=
- this.width) {
- lines[lines.length - 1] = prev_line + this.placeholder
- break
- }
- }
- lines.push(indent + this.placeholder.lstrip())
- }
- break
- }
- }
- }
- return lines
- }
- _split_chunks(text) {
- text = this._munge_whitespace(text)
- return this._split(text)
- }
-
- wrap(text) {
-
- let chunks = this._split_chunks(text)
-
-
-
-
- return this._wrap_chunks(chunks)
- }
- fill(text) {
-
- return this.wrap(text).join('\n')
- }
- }
- function wrap(text, options = {}) {
-
- let { width = 70, ...kwargs } = options
- let w = new TextWrapper(Object.assign({ width }, kwargs))
- return w.wrap(text)
- }
- function fill(text, options = {}) {
-
- let { width = 70, ...kwargs } = options
- let w = new TextWrapper(Object.assign({ width }, kwargs))
- return w.fill(text)
- }
- let _whitespace_only_re = /^[ \t]+$/mg
- let _leading_whitespace_re = /(^[ \t]*)(?:[^ \t\n])/mg
- function dedent(text) {
-
-
-
- let margin = undefined
- text = text.replace(_whitespace_only_re, '')
- let indents = text.match(_leading_whitespace_re) || []
- for (let indent of indents) {
- indent = indent.slice(0, -1)
- if (margin === undefined) {
- margin = indent
-
-
- } else if (indent.startsWith(margin)) {
-
-
-
- } else if (margin.startsWith(indent)) {
- margin = indent
-
-
- } else {
- for (let i = 0; i < margin.length && i < indent.length; i++) {
- if (margin[i] !== indent[i]) {
- margin = margin.slice(0, i)
- break
- }
- }
- }
- }
- if (margin) {
- text = text.replace(new RegExp('^' + margin, 'mg'), '')
- }
- return text
- }
- module.exports = { wrap, fill, dedent }
|