@thi.ng/strings
Version:
Various string formatting & utility functions
92 lines (91 loc) • 2.15 kB
JavaScript
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
};