@adecore/editor-react 0.31.11 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnchoredPopup.js +73 -19
- package/dist/AttributionCard.js +162 -4
- package/dist/BaselineStatus.js +62 -8
- package/dist/CallHierarchyPanel.js +977 -60
- package/dist/ChangeReview.js +76 -25
- package/dist/CodeAuthorsCard.js +314 -14
- package/dist/CompletionPopup.js +490 -83
- package/dist/DiffView.js +548 -181
- package/dist/EditorContextMenu.js +352 -34
- package/dist/EditorView.js +276 -99
- package/dist/FindReplace.js +525 -67
- package/dist/HoverCard.js +378 -38
- package/dist/HoverSections.js +367 -56
- package/dist/LanguagePopups.js +95 -34
- package/dist/Markdown.js +41 -6
- package/dist/PanelResizeHandle.js +199 -66
- package/dist/PathText.js +49 -7
- package/dist/PeekPanel.js +675 -72
- package/dist/PickPopup.js +304 -34
- package/dist/RenameCard.js +679 -85
- package/dist/RenderedDocs.js +329 -58
- package/dist/SignatureCard.js +91 -7
- package/dist/SplitDiffView.js +344 -254
- package/dist/SymbolPicker.js +440 -85
- package/dist/call-hierarchy-model.js +102 -68
- package/dist/call-hierarchy.js +458 -359
- package/dist/code-actions-model.js +88 -76
- package/dist/code-actions.js +372 -283
- package/dist/code-authors.js +64 -55
- package/dist/code-vision-model.js +134 -127
- package/dist/code-vision.js +224 -206
- package/dist/completion-call.js +94 -67
- package/dist/completion-model.js +334 -244
- package/dist/completion.js +840 -649
- package/dist/conflict-model.js +35 -29
- package/dist/context-menu-rows.js +42 -17
- package/dist/context-menu.js +64 -46
- package/dist/definition-link.js +88 -85
- package/dist/diagnostics-model.js +74 -55
- package/dist/diagnostics.js +121 -100
- package/dist/diff-expansion.js +38 -35
- package/dist/diff-overlays.js +144 -22
- package/dist/diff-syntax.js +65 -42
- package/dist/diff-wordmarks.js +26 -23
- package/dist/doc-comment-model.js +265 -216
- package/dist/editor-language.js +177 -152
- package/dist/error-message.js +2 -2
- package/dist/find-query.js +49 -36
- package/dist/folding.js +81 -62
- package/dist/format-on-type.js +86 -67
- package/dist/highlight-layers.js +19 -20
- package/dist/highlights.js +105 -83
- package/dist/history.js +44 -38
- package/dist/holder.js +18 -18
- package/dist/host-types.js +1 -1
- package/dist/hover-content.js +167 -129
- package/dist/hover.js +285 -245
- package/dist/index.js +10 -10
- package/dist/inlay-hints.js +369 -324
- package/dist/key-priority.js +34 -31
- package/dist/language-ids.js +45 -42
- package/dist/line-edits.js +74 -28
- package/dist/line-text.js +18 -3
- package/dist/locales/en.json +38 -38
- package/dist/locales/nl.json +41 -41
- package/dist/models.js +1 -1
- package/dist/navigation-history.js +40 -41
- package/dist/navigation.js +141 -123
- package/dist/panel-size.js +8 -8
- package/dist/patch-decorations.js +236 -160
- package/dist/patch-layout.js +331 -262
- package/dist/paths.js +3 -3
- package/dist/peek-model.js +86 -69
- package/dist/peek.js +254 -192
- package/dist/pick.js +164 -157
- package/dist/popup-focus.js +5 -5
- package/dist/popup-placement.js +36 -21
- package/dist/popups.js +13 -13
- package/dist/project-language.js +228 -211
- package/dist/project-problems.js +81 -69
- package/dist/proposal-model.js +163 -126
- package/dist/recent-choices.js +25 -25
- package/dist/refresher.js +44 -49
- package/dist/rename-model.js +113 -113
- package/dist/rename.js +321 -233
- package/dist/rendered-docs.d.ts +4 -2
- package/dist/rendered-docs.js +384 -318
- package/dist/rendering-context.js +5 -3
- package/dist/rendering.js +22 -4
- package/dist/review-group.js +42 -39
- package/dist/row-host.d.ts +4 -2
- package/dist/row-host.js +90 -78
- package/dist/selection-ranges.js +21 -17
- package/dist/semantic-delta.js +22 -19
- package/dist/semantic-model.js +86 -73
- package/dist/semantic-tokens.js +201 -162
- package/dist/shortcut-keys.js +16 -10
- package/dist/signature-model.js +34 -28
- package/dist/signature.js +177 -129
- package/dist/snippet-field-transforms.js +118 -106
- package/dist/snippet-indent.js +48 -43
- package/dist/snippet-range-order.js +11 -11
- package/dist/snippet-session.js +703 -667
- package/dist/snippet-transform-client.js +59 -59
- package/dist/snippet-transform-format.js +285 -263
- package/dist/snippet-transform-protocol.js +1 -1
- package/dist/snippet-transform-queue.js +163 -153
- package/dist/snippet-transform-worker.js +42 -35
- package/dist/snippet-variable-names.js +1 -36
- package/dist/snippet-variables.js +53 -63
- package/dist/snippet.js +477 -405
- package/dist/split-alignment.js +47 -35
- package/dist/split-scroll.js +42 -40
- package/dist/symbol-links.js +33 -25
- package/dist/symbol-picker-model.js +126 -116
- package/dist/symbol-picker.js +98 -59
- package/dist/symbols.js +117 -88
- package/dist/testing.js +248 -165
- package/dist/timers.js +35 -30
- package/dist/use-panel-body.js +49 -8
- package/dist/use-problem-counts.js +29 -15
- package/dist/view-state.js +54 -33
- package/dist/word-diff.js +18 -18
- package/dist/workspace-edit.js +300 -249
- package/package.json +6 -6
- package/src/CallHierarchyPanel.tsx +2 -2
- package/src/DiffView.tsx +3 -3
- package/src/FindReplace.tsx +2 -2
- package/src/LanguagePopups.tsx +2 -0
- package/src/PeekPanel.tsx +3 -3
- package/src/RenderedDocs.tsx +9 -10
- package/src/SymbolPicker.tsx +2 -2
- package/src/diff-overlays.tsx +6 -4
- package/src/locales/en.json +38 -38
- package/src/locales/nl.json +41 -41
- package/src/rendered-docs.ts +14 -3
- package/src/row-host.ts +14 -3
|
@@ -1,302 +1,324 @@
|
|
|
1
1
|
import { MAX_TRANSFORM_FORMAT, MAX_TRANSFORM_NODES, MAX_TRANSFORM_PATTERN, MAX_TRANSFORM_JOBS, MAX_TRANSFORM_VALUE, MAX_TRANSFORM_CHARACTERS, MAX_TRANSFORM_MATCHES, MAX_TRANSFORM_WORK } from './snippet-transform-protocol.js';
|
|
2
2
|
const CASES = new Set(['upcase', 'downcase', 'capitalize', 'camelcase', 'pascalcase']);
|
|
3
3
|
function readFormat(source, start, stopAtSlash) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
bound();
|
|
4
|
+
const nodes = [];
|
|
5
|
+
let at = start,
|
|
6
|
+
text = '';
|
|
7
|
+
const bound = () => {
|
|
8
|
+
if (at - start > MAX_TRANSFORM_FORMAT) {
|
|
9
|
+
throw new RangeError('The transform format is too long.');
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const push = node => {
|
|
13
|
+
if (nodes.length >= MAX_TRANSFORM_NODES) {
|
|
14
|
+
throw new RangeError('The transform format has too many nodes.');
|
|
15
|
+
}
|
|
16
|
+
nodes.push(node);
|
|
17
|
+
};
|
|
18
|
+
const flush = () => {
|
|
19
|
+
if (text !== '') {
|
|
20
|
+
push({
|
|
21
|
+
kind: 'text',
|
|
22
|
+
text
|
|
23
|
+
});
|
|
24
|
+
text = '';
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const string = (colon = false) => {
|
|
28
|
+
let value = '';
|
|
29
|
+
while (at < source.length && source[at] !== '}' && !(colon && source[at] === ':')) {
|
|
30
|
+
if (source[at] === '\\') {
|
|
31
|
+
if (!['$', '}', '\\'].includes(source[at + 1] ?? '')) {
|
|
32
|
+
throw new SyntaxError('Invalid conditional transform escape.');
|
|
34
33
|
}
|
|
35
|
-
|
|
34
|
+
at++;
|
|
35
|
+
}
|
|
36
|
+
value += source[at++];
|
|
37
|
+
bound();
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
while (at < source.length && !(stopAtSlash && source[at] === '/')) {
|
|
42
|
+
bound();
|
|
43
|
+
if (source[at] === '\\' && (source[at + 1] === '\\' || source[at + 1] === '/')) {
|
|
44
|
+
at++;
|
|
45
|
+
text += source[at++];
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (source[at] !== '$') {
|
|
49
|
+
text += source[at++];
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
at++;
|
|
53
|
+
const brace = source[at] === '{';
|
|
54
|
+
if (brace) {
|
|
55
|
+
at++;
|
|
56
|
+
}
|
|
57
|
+
const start = at;
|
|
58
|
+
while (at < source.length && source.charCodeAt(at) >= 48 && source.charCodeAt(at) <= 57) {
|
|
59
|
+
at++;
|
|
60
|
+
}
|
|
61
|
+
if (at === start) {
|
|
62
|
+
if (brace) {
|
|
63
|
+
throw new SyntaxError('A transform format needs a capture index.');
|
|
64
|
+
}
|
|
65
|
+
text += '$';
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const index = Number(source.slice(start, at));
|
|
69
|
+
if (index > 99) {
|
|
70
|
+
throw new RangeError('Transform captures are limited to 99.');
|
|
71
|
+
}
|
|
72
|
+
flush();
|
|
73
|
+
let node = {
|
|
74
|
+
kind: 'capture',
|
|
75
|
+
index
|
|
36
76
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (source[at] === '\\' && (source[at + 1] === '\\' || source[at + 1] === '/')) {
|
|
40
|
-
at++;
|
|
41
|
-
text += source[at++];
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (source[at] !== '$') {
|
|
45
|
-
text += source[at++];
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
77
|
+
if (brace) {
|
|
78
|
+
if (source[at] === ':') {
|
|
48
79
|
at++;
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
80
|
+
const operator = source[at];
|
|
81
|
+
if (operator === '/') {
|
|
82
|
+
at++;
|
|
83
|
+
const operation = string();
|
|
84
|
+
if (!CASES.has(operation)) {
|
|
85
|
+
throw new SyntaxError('Unknown transform case operation.');
|
|
86
|
+
}
|
|
87
|
+
node = {
|
|
88
|
+
kind: 'capture',
|
|
89
|
+
index,
|
|
90
|
+
case: operation
|
|
91
|
+
};
|
|
92
|
+
} else if (operator === '+') {
|
|
93
|
+
at++;
|
|
94
|
+
node = {
|
|
95
|
+
kind: 'conditional',
|
|
96
|
+
index,
|
|
97
|
+
if: string()
|
|
98
|
+
};
|
|
99
|
+
} else if (operator === '?') {
|
|
100
|
+
at++;
|
|
101
|
+
const when = string(true);
|
|
102
|
+
if (source[at++] !== ':') {
|
|
103
|
+
throw new SyntaxError('A conditional transform needs both branches.');
|
|
104
|
+
}
|
|
105
|
+
node = {
|
|
106
|
+
kind: 'conditional',
|
|
107
|
+
index,
|
|
108
|
+
if: when,
|
|
109
|
+
else: string()
|
|
110
|
+
};
|
|
111
|
+
} else {
|
|
112
|
+
if (operator === '-') {
|
|
51
113
|
at++;
|
|
114
|
+
}
|
|
115
|
+
node = {
|
|
116
|
+
kind: 'conditional',
|
|
117
|
+
index,
|
|
118
|
+
else: string()
|
|
119
|
+
};
|
|
52
120
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (at === start) {
|
|
58
|
-
if (brace) {
|
|
59
|
-
throw new SyntaxError('A transform format needs a capture index.');
|
|
60
|
-
}
|
|
61
|
-
text += '$';
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const index = Number(source.slice(start, at));
|
|
65
|
-
if (index > 99) {
|
|
66
|
-
throw new RangeError('Transform captures are limited to 99.');
|
|
67
|
-
}
|
|
68
|
-
flush();
|
|
69
|
-
let node = { kind: 'capture', index };
|
|
70
|
-
if (brace) {
|
|
71
|
-
if (source[at] === ':') {
|
|
72
|
-
at++;
|
|
73
|
-
const operator = source[at];
|
|
74
|
-
if (operator === '/') {
|
|
75
|
-
at++;
|
|
76
|
-
const operation = string();
|
|
77
|
-
if (!CASES.has(operation)) {
|
|
78
|
-
throw new SyntaxError('Unknown transform case operation.');
|
|
79
|
-
}
|
|
80
|
-
node = { kind: 'capture', index, case: operation };
|
|
81
|
-
}
|
|
82
|
-
else if (operator === '+') {
|
|
83
|
-
at++;
|
|
84
|
-
node = { kind: 'conditional', index, if: string() };
|
|
85
|
-
}
|
|
86
|
-
else if (operator === '?') {
|
|
87
|
-
at++;
|
|
88
|
-
const when = string(true);
|
|
89
|
-
if (source[at++] !== ':') {
|
|
90
|
-
throw new SyntaxError('A conditional transform needs both branches.');
|
|
91
|
-
}
|
|
92
|
-
node = { kind: 'conditional', index, if: when, else: string() };
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
if (operator === '-') {
|
|
96
|
-
at++;
|
|
97
|
-
}
|
|
98
|
-
node = { kind: 'conditional', index, else: string() };
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (source[at++] !== '}') {
|
|
102
|
-
throw new SyntaxError('An unclosed transform capture cannot be applied.');
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
push(node);
|
|
106
|
-
}
|
|
107
|
-
flush();
|
|
108
|
-
bound();
|
|
109
|
-
if (stopAtSlash && source[at] !== '/') {
|
|
110
|
-
throw new SyntaxError('Unclosed snippet transform format.');
|
|
121
|
+
}
|
|
122
|
+
if (source[at++] !== '}') {
|
|
123
|
+
throw new SyntaxError('An unclosed transform capture cannot be applied.');
|
|
124
|
+
}
|
|
111
125
|
}
|
|
112
|
-
|
|
126
|
+
push(node);
|
|
127
|
+
}
|
|
128
|
+
flush();
|
|
129
|
+
bound();
|
|
130
|
+
if (stopAtSlash && source[at] !== '/') {
|
|
131
|
+
throw new SyntaxError('Unclosed snippet transform format.');
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
nodes,
|
|
135
|
+
end: at
|
|
136
|
+
};
|
|
113
137
|
}
|
|
114
138
|
export function compileSnippetTransformFormat(source) {
|
|
115
|
-
|
|
139
|
+
return readFormat(source, 0, false).nodes;
|
|
116
140
|
}
|
|
117
141
|
export function scanSnippetTransform(source, at) {
|
|
118
|
-
|
|
119
|
-
|
|
142
|
+
if (source[at++] !== '/') {
|
|
143
|
+
throw new SyntaxError('A snippet transform needs an opening slash.');
|
|
144
|
+
}
|
|
145
|
+
let pattern = '';
|
|
146
|
+
while (at < source.length && source[at] !== '/') {
|
|
147
|
+
const character = source[at++];
|
|
148
|
+
if (character === '\\' && source[at] === '/') {
|
|
149
|
+
pattern += source[at++];
|
|
150
|
+
} else {
|
|
151
|
+
pattern += character;
|
|
120
152
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const character = source[at++];
|
|
124
|
-
if (character === '\\' && source[at] === '/') {
|
|
125
|
-
pattern += source[at++];
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
pattern += character;
|
|
129
|
-
}
|
|
130
|
-
if (pattern.length > MAX_TRANSFORM_PATTERN) {
|
|
131
|
-
throw new RangeError('The snippet transform pattern is too long.');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (source[at++] !== '/') {
|
|
135
|
-
throw new SyntaxError('Unclosed snippet transform pattern.');
|
|
153
|
+
if (pattern.length > MAX_TRANSFORM_PATTERN) {
|
|
154
|
+
throw new RangeError('The snippet transform pattern is too long.');
|
|
136
155
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
throw new SyntaxError('Unclosed snippet transform flags.');
|
|
156
|
+
}
|
|
157
|
+
if (source[at++] !== '/') {
|
|
158
|
+
throw new SyntaxError('Unclosed snippet transform pattern.');
|
|
159
|
+
}
|
|
160
|
+
const parsed = readFormat(source, at, true);
|
|
161
|
+
at = parsed.end + 1;
|
|
162
|
+
let flags = '';
|
|
163
|
+
while (at < source.length && source[at] !== '}') {
|
|
164
|
+
const flag = source[at++];
|
|
165
|
+
if (!'gimsu'.includes(flag) || flags.includes(flag)) {
|
|
166
|
+
throw new SyntaxError('Invalid snippet transform flags.');
|
|
149
167
|
}
|
|
150
|
-
|
|
168
|
+
flags += flag;
|
|
169
|
+
}
|
|
170
|
+
if (source[at++] !== '}') {
|
|
171
|
+
throw new SyntaxError('Unclosed snippet transform flags.');
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
pattern,
|
|
175
|
+
flags,
|
|
176
|
+
format: parsed.nodes,
|
|
177
|
+
end: at
|
|
178
|
+
};
|
|
151
179
|
}
|
|
152
180
|
function capitalize(value) {
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
const first = value.slice(0, (value.codePointAt(0) ?? 0) > 0xffff ? 2 : 1);
|
|
182
|
+
return first.toUpperCase() + value.slice(first.length);
|
|
155
183
|
}
|
|
156
184
|
function changeCase(value, operation) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
185
|
+
if (operation === 'upcase') {
|
|
186
|
+
return value.toUpperCase();
|
|
187
|
+
}
|
|
188
|
+
if (operation === 'downcase') {
|
|
189
|
+
return value.toLowerCase();
|
|
190
|
+
}
|
|
191
|
+
if (operation === 'capitalize') {
|
|
192
|
+
return capitalize(value);
|
|
193
|
+
}
|
|
194
|
+
if (operation === 'camelcase' || operation === 'pascalcase') {
|
|
195
|
+
const words = value.match(/[\p{L}0-9]+/gu);
|
|
196
|
+
if (words === null) {
|
|
197
|
+
return value;
|
|
165
198
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return value;
|
|
170
|
-
}
|
|
171
|
-
return words
|
|
172
|
-
.map((word, index) => (operation === 'camelcase' && index === 0 ? word.slice(0, 1).toLowerCase() + word.slice(1) : capitalize(word)))
|
|
173
|
-
.join('');
|
|
174
|
-
}
|
|
175
|
-
return value;
|
|
199
|
+
return words.map((word, index) => operation === 'camelcase' && index === 0 ? word.slice(0, 1).toLowerCase() + word.slice(1) : capitalize(word)).join('');
|
|
200
|
+
}
|
|
201
|
+
return value;
|
|
176
202
|
}
|
|
177
203
|
export function validateSnippetTransformJobs(jobs) {
|
|
178
|
-
|
|
179
|
-
|
|
204
|
+
if (!Array.isArray(jobs) || jobs.length > MAX_TRANSFORM_JOBS) {
|
|
205
|
+
throw new RangeError('Too many snippet transform jobs.');
|
|
206
|
+
}
|
|
207
|
+
let characters = 0;
|
|
208
|
+
const patterns = new Set();
|
|
209
|
+
for (const job of jobs) {
|
|
210
|
+
if (typeof job.value !== 'string' || job.value.length > MAX_TRANSFORM_VALUE || typeof job.pattern !== 'string' || job.pattern.length > MAX_TRANSFORM_PATTERN) {
|
|
211
|
+
throw new RangeError('A snippet transform value or pattern is too large.');
|
|
180
212
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
if (typeof job.flags !== 'string' ||
|
|
195
|
-
Array.from(job.flags).some((flag) => !'gimsu'.includes(flag)) ||
|
|
196
|
-
new Set(job.flags).size !== job.flags.length) {
|
|
197
|
-
throw new SyntaxError('Snippet transforms support the flags g, i, m, s and u once each.');
|
|
198
|
-
}
|
|
199
|
-
let formatLength = 0;
|
|
200
|
-
for (const node of job.format) {
|
|
201
|
-
if (node.kind === 'text') {
|
|
202
|
-
if (typeof node.text !== 'string') {
|
|
203
|
-
throw new SyntaxError('Invalid transform text.');
|
|
204
|
-
}
|
|
205
|
-
formatLength += node.text.length;
|
|
206
|
-
}
|
|
207
|
-
else if (node.kind === 'capture' || node.kind === 'conditional') {
|
|
208
|
-
if (!Number.isInteger(node.index) || node.index < 0 || node.index > 99) {
|
|
209
|
-
throw new RangeError('Invalid transform capture index.');
|
|
210
|
-
}
|
|
211
|
-
if (node.kind === 'capture' && node.case !== undefined && !CASES.has(node.case)) {
|
|
212
|
-
throw new SyntaxError('Invalid transform case operation.');
|
|
213
|
-
}
|
|
214
|
-
if (node.kind === 'conditional') {
|
|
215
|
-
if ((node.if !== undefined && typeof node.if !== 'string') || (node.else !== undefined && typeof node.else !== 'string')) {
|
|
216
|
-
throw new SyntaxError('Invalid transform branches.');
|
|
217
|
-
}
|
|
218
|
-
formatLength += (node.if?.length ?? 0) + (node.else?.length ?? 0);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
throw new SyntaxError('Invalid transform format node.');
|
|
223
|
-
}
|
|
213
|
+
characters += job.value.length + job.pattern.length;
|
|
214
|
+
if (characters > MAX_TRANSFORM_CHARACTERS || !Array.isArray(job.format) || job.format.length > MAX_TRANSFORM_NODES) {
|
|
215
|
+
throw new RangeError('The snippet transform batch is too large.');
|
|
216
|
+
}
|
|
217
|
+
if (typeof job.flags !== 'string' || Array.from(job.flags).some(flag => !'gimsu'.includes(flag)) || new Set(job.flags).size !== job.flags.length) {
|
|
218
|
+
throw new SyntaxError('Snippet transforms support the flags g, i, m, s and u once each.');
|
|
219
|
+
}
|
|
220
|
+
let formatLength = 0;
|
|
221
|
+
for (const node of job.format) {
|
|
222
|
+
if (node.kind === 'text') {
|
|
223
|
+
if (typeof node.text !== 'string') {
|
|
224
|
+
throw new SyntaxError('Invalid transform text.');
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
formatLength += node.text.length;
|
|
227
|
+
} else if (node.kind === 'capture' || node.kind === 'conditional') {
|
|
228
|
+
if (!Number.isInteger(node.index) || node.index < 0 || node.index > 99) {
|
|
229
|
+
throw new RangeError('Invalid transform capture index.');
|
|
227
230
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
throw new RangeError('The snippet transform batch is too large.');
|
|
231
|
+
if (node.kind === 'capture' && node.case !== undefined && !CASES.has(node.case)) {
|
|
232
|
+
throw new SyntaxError('Invalid transform case operation.');
|
|
231
233
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
if (node.kind === 'conditional') {
|
|
235
|
+
if (node.if !== undefined && typeof node.if !== 'string' || node.else !== undefined && typeof node.else !== 'string') {
|
|
236
|
+
throw new SyntaxError('Invalid transform branches.');
|
|
237
|
+
}
|
|
238
|
+
formatLength += (node.if?.length ?? 0) + (node.else?.length ?? 0);
|
|
237
239
|
}
|
|
240
|
+
} else {
|
|
241
|
+
throw new SyntaxError('Invalid transform format node.');
|
|
242
|
+
}
|
|
238
243
|
}
|
|
244
|
+
if (formatLength > MAX_TRANSFORM_FORMAT) {
|
|
245
|
+
throw new RangeError('The transform format is too long.');
|
|
246
|
+
}
|
|
247
|
+
characters += formatLength;
|
|
248
|
+
if (characters > MAX_TRANSFORM_CHARACTERS) {
|
|
249
|
+
throw new RangeError('The snippet transform batch is too large.');
|
|
250
|
+
}
|
|
251
|
+
const key = job.flags + '\0' + job.pattern;
|
|
252
|
+
if (!patterns.has(key)) {
|
|
253
|
+
// Compilation validates syntax; only the worker executes patterns supplied by a server.
|
|
254
|
+
new RegExp(job.pattern, job.flags);
|
|
255
|
+
patterns.add(key);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
239
258
|
}
|
|
240
259
|
export function createSnippetTransformBudget() {
|
|
241
|
-
|
|
260
|
+
return {
|
|
261
|
+
remaining: MAX_TRANSFORM_WORK,
|
|
262
|
+
matches: 0
|
|
263
|
+
};
|
|
242
264
|
}
|
|
243
265
|
function spend(budget, amount) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
266
|
+
budget.remaining -= amount;
|
|
267
|
+
if (budget.remaining < 0) {
|
|
268
|
+
throw new RangeError('The snippet transform work budget was exceeded.');
|
|
269
|
+
}
|
|
248
270
|
}
|
|
249
271
|
export function applySnippetTransform(job, maximum = MAX_TRANSFORM_CHARACTERS, expression = new RegExp(job.pattern, job.flags), budget = createSnippetTransformBudget()) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
append(node.kind === 'capture' ? changeCase(capture, node.case) : capture !== '' ? (node.if ?? capture) : (node.else ?? ''));
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
for (let match = expression.exec(job.value); match !== null; match = expression.exec(job.value)) {
|
|
280
|
-
if (++budget.matches > MAX_TRANSFORM_MATCHES) {
|
|
281
|
-
throw new RangeError('The snippet transform batch has too many matches.');
|
|
282
|
-
}
|
|
283
|
-
matches++;
|
|
284
|
-
spend(budget, 1);
|
|
285
|
-
append(job.value.slice(at, match.index));
|
|
286
|
-
format(match);
|
|
287
|
-
at = match.index + match[0].length;
|
|
288
|
-
if (!expression.global) {
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
if (match[0].length === 0) {
|
|
292
|
-
expression.lastIndex += expression.unicode && (job.value.codePointAt(expression.lastIndex) ?? 0) > 0xffff ? 2 : 1;
|
|
272
|
+
expression.lastIndex = 0;
|
|
273
|
+
spend(budget, job.value.length);
|
|
274
|
+
const pieces = [];
|
|
275
|
+
let at = 0,
|
|
276
|
+
length = 0,
|
|
277
|
+
matches = 0;
|
|
278
|
+
const append = text => {
|
|
279
|
+
if (text === '') {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
length += text.length;
|
|
283
|
+
if (length > maximum) {
|
|
284
|
+
throw new RangeError('The snippet transform output is too large.');
|
|
285
|
+
}
|
|
286
|
+
pieces.push(text);
|
|
287
|
+
};
|
|
288
|
+
const format = captures => {
|
|
289
|
+
for (const node of job.format) {
|
|
290
|
+
spend(budget, 1);
|
|
291
|
+
if (node.kind === 'text') {
|
|
292
|
+
append(node.text);
|
|
293
|
+
} else {
|
|
294
|
+
const capture = captures[node.index] ?? '';
|
|
295
|
+
if (node.kind === 'capture' && node.case !== undefined) {
|
|
296
|
+
spend(budget, capture.length);
|
|
293
297
|
}
|
|
298
|
+
append(node.kind === 'capture' ? changeCase(capture, node.case) : capture !== '' ? node.if ?? capture : node.else ?? '');
|
|
299
|
+
}
|
|
294
300
|
}
|
|
295
|
-
|
|
296
|
-
|
|
301
|
+
};
|
|
302
|
+
for (let match = expression.exec(job.value); match !== null; match = expression.exec(job.value)) {
|
|
303
|
+
if (++budget.matches > MAX_TRANSFORM_MATCHES) {
|
|
304
|
+
throw new RangeError('The snippet transform batch has too many matches.');
|
|
297
305
|
}
|
|
298
|
-
|
|
299
|
-
|
|
306
|
+
matches++;
|
|
307
|
+
spend(budget, 1);
|
|
308
|
+
append(job.value.slice(at, match.index));
|
|
309
|
+
format(match);
|
|
310
|
+
at = match.index + match[0].length;
|
|
311
|
+
if (!expression.global) {
|
|
312
|
+
break;
|
|
300
313
|
}
|
|
301
|
-
|
|
302
|
-
|
|
314
|
+
if (match[0].length === 0) {
|
|
315
|
+
expression.lastIndex += expression.unicode && (job.value.codePointAt(expression.lastIndex) ?? 0) > 0xffff ? 2 : 1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (matches === 0 && job.format.some(node => node.kind === 'conditional' && Boolean(node.else))) {
|
|
319
|
+
format([]);
|
|
320
|
+
} else {
|
|
321
|
+
append(job.value.slice(at));
|
|
322
|
+
}
|
|
323
|
+
return pieces.join('');
|
|
324
|
+
}
|