UNPKG

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