[d24f17c] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | const FOLD_FLOW = 'flow';
|
---|
| 4 | const FOLD_BLOCK = 'block';
|
---|
| 5 | const FOLD_QUOTED = 'quoted';
|
---|
| 6 | /**
|
---|
| 7 | * Tries to keep input at up to `lineWidth` characters, splitting only on spaces
|
---|
| 8 | * not followed by newlines or spaces unless `mode` is `'quoted'`. Lines are
|
---|
| 9 | * terminated with `\n` and started with `indent`.
|
---|
| 10 | */
|
---|
| 11 | function foldFlowLines(text, indent, mode = 'flow', { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
|
---|
| 12 | if (!lineWidth || lineWidth < 0)
|
---|
| 13 | return text;
|
---|
| 14 | const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
|
---|
| 15 | if (text.length <= endStep)
|
---|
| 16 | return text;
|
---|
| 17 | const folds = [];
|
---|
| 18 | const escapedFolds = {};
|
---|
| 19 | let end = lineWidth - indent.length;
|
---|
| 20 | if (typeof indentAtStart === 'number') {
|
---|
| 21 | if (indentAtStart > lineWidth - Math.max(2, minContentWidth))
|
---|
| 22 | folds.push(0);
|
---|
| 23 | else
|
---|
| 24 | end = lineWidth - indentAtStart;
|
---|
| 25 | }
|
---|
| 26 | let split = undefined;
|
---|
| 27 | let prev = undefined;
|
---|
| 28 | let overflow = false;
|
---|
| 29 | let i = -1;
|
---|
| 30 | let escStart = -1;
|
---|
| 31 | let escEnd = -1;
|
---|
| 32 | if (mode === FOLD_BLOCK) {
|
---|
| 33 | i = consumeMoreIndentedLines(text, i);
|
---|
| 34 | if (i !== -1)
|
---|
| 35 | end = i + endStep;
|
---|
| 36 | }
|
---|
| 37 | for (let ch; (ch = text[(i += 1)]);) {
|
---|
| 38 | if (mode === FOLD_QUOTED && ch === '\\') {
|
---|
| 39 | escStart = i;
|
---|
| 40 | switch (text[i + 1]) {
|
---|
| 41 | case 'x':
|
---|
| 42 | i += 3;
|
---|
| 43 | break;
|
---|
| 44 | case 'u':
|
---|
| 45 | i += 5;
|
---|
| 46 | break;
|
---|
| 47 | case 'U':
|
---|
| 48 | i += 9;
|
---|
| 49 | break;
|
---|
| 50 | default:
|
---|
| 51 | i += 1;
|
---|
| 52 | }
|
---|
| 53 | escEnd = i;
|
---|
| 54 | }
|
---|
| 55 | if (ch === '\n') {
|
---|
| 56 | if (mode === FOLD_BLOCK)
|
---|
| 57 | i = consumeMoreIndentedLines(text, i);
|
---|
| 58 | end = i + endStep;
|
---|
| 59 | split = undefined;
|
---|
| 60 | }
|
---|
| 61 | else {
|
---|
| 62 | if (ch === ' ' &&
|
---|
| 63 | prev &&
|
---|
| 64 | prev !== ' ' &&
|
---|
| 65 | prev !== '\n' &&
|
---|
| 66 | prev !== '\t') {
|
---|
| 67 | // space surrounded by non-space can be replaced with newline + indent
|
---|
| 68 | const next = text[i + 1];
|
---|
| 69 | if (next && next !== ' ' && next !== '\n' && next !== '\t')
|
---|
| 70 | split = i;
|
---|
| 71 | }
|
---|
| 72 | if (i >= end) {
|
---|
| 73 | if (split) {
|
---|
| 74 | folds.push(split);
|
---|
| 75 | end = split + endStep;
|
---|
| 76 | split = undefined;
|
---|
| 77 | }
|
---|
| 78 | else if (mode === FOLD_QUOTED) {
|
---|
| 79 | // white-space collected at end may stretch past lineWidth
|
---|
| 80 | while (prev === ' ' || prev === '\t') {
|
---|
| 81 | prev = ch;
|
---|
| 82 | ch = text[(i += 1)];
|
---|
| 83 | overflow = true;
|
---|
| 84 | }
|
---|
| 85 | // Account for newline escape, but don't break preceding escape
|
---|
| 86 | const j = i > escEnd + 1 ? i - 2 : escStart - 1;
|
---|
| 87 | // Bail out if lineWidth & minContentWidth are shorter than an escape string
|
---|
| 88 | if (escapedFolds[j])
|
---|
| 89 | return text;
|
---|
| 90 | folds.push(j);
|
---|
| 91 | escapedFolds[j] = true;
|
---|
| 92 | end = j + endStep;
|
---|
| 93 | split = undefined;
|
---|
| 94 | }
|
---|
| 95 | else {
|
---|
| 96 | overflow = true;
|
---|
| 97 | }
|
---|
| 98 | }
|
---|
| 99 | }
|
---|
| 100 | prev = ch;
|
---|
| 101 | }
|
---|
| 102 | if (overflow && onOverflow)
|
---|
| 103 | onOverflow();
|
---|
| 104 | if (folds.length === 0)
|
---|
| 105 | return text;
|
---|
| 106 | if (onFold)
|
---|
| 107 | onFold();
|
---|
| 108 | let res = text.slice(0, folds[0]);
|
---|
| 109 | for (let i = 0; i < folds.length; ++i) {
|
---|
| 110 | const fold = folds[i];
|
---|
| 111 | const end = folds[i + 1] || text.length;
|
---|
| 112 | if (fold === 0)
|
---|
| 113 | res = `\n${indent}${text.slice(0, end)}`;
|
---|
| 114 | else {
|
---|
| 115 | if (mode === FOLD_QUOTED && escapedFolds[fold])
|
---|
| 116 | res += `${text[fold]}\\`;
|
---|
| 117 | res += `\n${indent}${text.slice(fold + 1, end)}`;
|
---|
| 118 | }
|
---|
| 119 | }
|
---|
| 120 | return res;
|
---|
| 121 | }
|
---|
| 122 | /**
|
---|
| 123 | * Presumes `i + 1` is at the start of a line
|
---|
| 124 | * @returns index of last newline in more-indented block
|
---|
| 125 | */
|
---|
| 126 | function consumeMoreIndentedLines(text, i) {
|
---|
| 127 | let ch = text[i + 1];
|
---|
| 128 | while (ch === ' ' || ch === '\t') {
|
---|
| 129 | do {
|
---|
| 130 | ch = text[(i += 1)];
|
---|
| 131 | } while (ch && ch !== '\n');
|
---|
| 132 | ch = text[i + 1];
|
---|
| 133 | }
|
---|
| 134 | return i;
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | exports.FOLD_BLOCK = FOLD_BLOCK;
|
---|
| 138 | exports.FOLD_FLOW = FOLD_FLOW;
|
---|
| 139 | exports.FOLD_QUOTED = FOLD_QUOTED;
|
---|
| 140 | exports.foldFlowLines = foldFlowLines;
|
---|