source: node_modules/yaml/dist/compose/resolve-block-scalar.js@ d24f17c

main
Last change on this file since d24f17c was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 7.1 KB
Line 
1'use strict';
2
3var Scalar = require('../nodes/Scalar.js');
4
5function resolveBlockScalar(scalar, strict, onError) {
6 const start = scalar.offset;
7 const header = parseBlockScalarHeader(scalar, strict, onError);
8 if (!header)
9 return { value: '', type: null, comment: '', range: [start, start, start] };
10 const type = header.mode === '>' ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL;
11 const lines = scalar.source ? splitLines(scalar.source) : [];
12 // determine the end of content & start of chomping
13 let chompStart = lines.length;
14 for (let i = lines.length - 1; i >= 0; --i) {
15 const content = lines[i][1];
16 if (content === '' || content === '\r')
17 chompStart = i;
18 else
19 break;
20 }
21 // shortcut for empty contents
22 if (chompStart === 0) {
23 const value = header.chomp === '+' && lines.length > 0
24 ? '\n'.repeat(Math.max(1, lines.length - 1))
25 : '';
26 let end = start + header.length;
27 if (scalar.source)
28 end += scalar.source.length;
29 return { value, type, comment: header.comment, range: [start, end, end] };
30 }
31 // find the indentation level to trim from start
32 let trimIndent = scalar.indent + header.indent;
33 let offset = scalar.offset + header.length;
34 let contentStart = 0;
35 for (let i = 0; i < chompStart; ++i) {
36 const [indent, content] = lines[i];
37 if (content === '' || content === '\r') {
38 if (header.indent === 0 && indent.length > trimIndent)
39 trimIndent = indent.length;
40 }
41 else {
42 if (indent.length < trimIndent) {
43 const message = 'Block scalars with more-indented leading empty lines must use an explicit indentation indicator';
44 onError(offset + indent.length, 'MISSING_CHAR', message);
45 }
46 if (header.indent === 0)
47 trimIndent = indent.length;
48 contentStart = i;
49 break;
50 }
51 offset += indent.length + content.length + 1;
52 }
53 // include trailing more-indented empty lines in content
54 for (let i = lines.length - 1; i >= chompStart; --i) {
55 if (lines[i][0].length > trimIndent)
56 chompStart = i + 1;
57 }
58 let value = '';
59 let sep = '';
60 let prevMoreIndented = false;
61 // leading whitespace is kept intact
62 for (let i = 0; i < contentStart; ++i)
63 value += lines[i][0].slice(trimIndent) + '\n';
64 for (let i = contentStart; i < chompStart; ++i) {
65 let [indent, content] = lines[i];
66 offset += indent.length + content.length + 1;
67 const crlf = content[content.length - 1] === '\r';
68 if (crlf)
69 content = content.slice(0, -1);
70 /* istanbul ignore if already caught in lexer */
71 if (content && indent.length < trimIndent) {
72 const src = header.indent
73 ? 'explicit indentation indicator'
74 : 'first line';
75 const message = `Block scalar lines must not be less indented than their ${src}`;
76 onError(offset - content.length - (crlf ? 2 : 1), 'BAD_INDENT', message);
77 indent = '';
78 }
79 if (type === Scalar.Scalar.BLOCK_LITERAL) {
80 value += sep + indent.slice(trimIndent) + content;
81 sep = '\n';
82 }
83 else if (indent.length > trimIndent || content[0] === '\t') {
84 // more-indented content within a folded block
85 if (sep === ' ')
86 sep = '\n';
87 else if (!prevMoreIndented && sep === '\n')
88 sep = '\n\n';
89 value += sep + indent.slice(trimIndent) + content;
90 sep = '\n';
91 prevMoreIndented = true;
92 }
93 else if (content === '') {
94 // empty line
95 if (sep === '\n')
96 value += '\n';
97 else
98 sep = '\n';
99 }
100 else {
101 value += sep + content;
102 sep = ' ';
103 prevMoreIndented = false;
104 }
105 }
106 switch (header.chomp) {
107 case '-':
108 break;
109 case '+':
110 for (let i = chompStart; i < lines.length; ++i)
111 value += '\n' + lines[i][0].slice(trimIndent);
112 if (value[value.length - 1] !== '\n')
113 value += '\n';
114 break;
115 default:
116 value += '\n';
117 }
118 const end = start + header.length + scalar.source.length;
119 return { value, type, comment: header.comment, range: [start, end, end] };
120}
121function parseBlockScalarHeader({ offset, props }, strict, onError) {
122 /* istanbul ignore if should not happen */
123 if (props[0].type !== 'block-scalar-header') {
124 onError(props[0], 'IMPOSSIBLE', 'Block scalar header not found');
125 return null;
126 }
127 const { source } = props[0];
128 const mode = source[0];
129 let indent = 0;
130 let chomp = '';
131 let error = -1;
132 for (let i = 1; i < source.length; ++i) {
133 const ch = source[i];
134 if (!chomp && (ch === '-' || ch === '+'))
135 chomp = ch;
136 else {
137 const n = Number(ch);
138 if (!indent && n)
139 indent = n;
140 else if (error === -1)
141 error = offset + i;
142 }
143 }
144 if (error !== -1)
145 onError(error, 'UNEXPECTED_TOKEN', `Block scalar header includes extra characters: ${source}`);
146 let hasSpace = false;
147 let comment = '';
148 let length = source.length;
149 for (let i = 1; i < props.length; ++i) {
150 const token = props[i];
151 switch (token.type) {
152 case 'space':
153 hasSpace = true;
154 // fallthrough
155 case 'newline':
156 length += token.source.length;
157 break;
158 case 'comment':
159 if (strict && !hasSpace) {
160 const message = 'Comments must be separated from other tokens by white space characters';
161 onError(token, 'MISSING_CHAR', message);
162 }
163 length += token.source.length;
164 comment = token.source.substring(1);
165 break;
166 case 'error':
167 onError(token, 'UNEXPECTED_TOKEN', token.message);
168 length += token.source.length;
169 break;
170 /* istanbul ignore next should not happen */
171 default: {
172 const message = `Unexpected token in block scalar header: ${token.type}`;
173 onError(token, 'UNEXPECTED_TOKEN', message);
174 const ts = token.source;
175 if (ts && typeof ts === 'string')
176 length += ts.length;
177 }
178 }
179 }
180 return { mode, indent, chomp, comment, length };
181}
182/** @returns Array of lines split up as `[indent, content]` */
183function splitLines(source) {
184 const split = source.split(/\n( *)/);
185 const first = split[0];
186 const m = first.match(/^( *)/);
187 const line0 = m?.[1]
188 ? [m[1], first.slice(m[1].length)]
189 : ['', first];
190 const lines = [line0];
191 for (let i = 1; i < split.length; i += 2)
192 lines.push([split[i], split[i + 1]]);
193 return lines;
194}
195
196exports.resolveBlockScalar = resolveBlockScalar;
Note: See TracBrowser for help on using the repository browser.