UNPKG

@thi.ng/strings

Version:

Various string formatting & utility functions

92 lines (91 loc) 2.15 kB
import { lengthAnsi } from "./ansi.js"; import { split } from "./split.js"; class Line { n = 0; w = []; constructor(word, n) { word != null && this.add(word, n); } add(word, n = word.length) { this.w.push(word); this.n += n + ~~(this.n > 0); return this; } toString() { return this.w.join(" "); } } const SPLIT_PLAIN = { length: (x) => x.length, split: (_, max) => max }; const SPLIT_ANSI = { length: lengthAnsi, split: (x, max) => { const re = /\x1b\[[0-9;]+m/g; let i = max; let match; while (match = re.exec(x)) { if (match.index >= max) break; const n = match[0].length; i += n; max += n; } return i; } }; const __append = (acc, word, wordLen, width) => { const curr = acc[acc.length - 1]; curr && width - curr.n > wordLen ? curr.add(word, wordLen) : acc.push(new Line(word, wordLen)); }; const __wrapWord = (word, { width, min, hard, splitter }, offset = 0, acc = []) => { let len = splitter.length(word); let free = width - offset; if (free < min && free < len) { free = width; } while (hard && len > free) { const split2 = splitter.split(word, free); const chunk = word.substring(0, split2); __append(acc, chunk, free, width); word = word.substring(split2); free = width; len = splitter.length(word); } __append(acc, word, len, width); return acc; }; const wordWrapLine = (line, opts, acc = []) => { if (!line.length) { acc.push(new Line()); return acc; } const $opts = { width: 80, min: 4, hard: false, splitter: SPLIT_PLAIN, ...opts }; for (let word of split(line, opts.delimWord || /\s/g)) { const curr = acc[acc.length - 1]; __wrapWord(word, $opts, curr && curr.n > 0 ? curr.n + 1 : 0, acc); } return acc; }; const wordWrapLines = (lines, opts) => { let acc = []; for (let line of split(lines, opts.delimLine)) { acc = acc.concat(wordWrapLine(line, opts)); } return acc; }; const wordWrap = (str, opts) => wordWrapLines(str, opts).join("\n"); export { Line, SPLIT_ANSI, SPLIT_PLAIN, wordWrap, wordWrapLine, wordWrapLines };