@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.
Files changed (137) hide show
  1. package/dist/AnchoredPopup.js +73 -19
  2. package/dist/AttributionCard.js +162 -4
  3. package/dist/BaselineStatus.js +62 -8
  4. package/dist/CallHierarchyPanel.js +977 -60
  5. package/dist/ChangeReview.js +76 -25
  6. package/dist/CodeAuthorsCard.js +314 -14
  7. package/dist/CompletionPopup.js +490 -83
  8. package/dist/DiffView.js +548 -181
  9. package/dist/EditorContextMenu.js +352 -34
  10. package/dist/EditorView.js +276 -99
  11. package/dist/FindReplace.js +525 -67
  12. package/dist/HoverCard.js +378 -38
  13. package/dist/HoverSections.js +367 -56
  14. package/dist/LanguagePopups.js +95 -34
  15. package/dist/Markdown.js +41 -6
  16. package/dist/PanelResizeHandle.js +199 -66
  17. package/dist/PathText.js +49 -7
  18. package/dist/PeekPanel.js +675 -72
  19. package/dist/PickPopup.js +304 -34
  20. package/dist/RenameCard.js +679 -85
  21. package/dist/RenderedDocs.js +329 -58
  22. package/dist/SignatureCard.js +91 -7
  23. package/dist/SplitDiffView.js +344 -254
  24. package/dist/SymbolPicker.js +440 -85
  25. package/dist/call-hierarchy-model.js +102 -68
  26. package/dist/call-hierarchy.js +458 -359
  27. package/dist/code-actions-model.js +88 -76
  28. package/dist/code-actions.js +372 -283
  29. package/dist/code-authors.js +64 -55
  30. package/dist/code-vision-model.js +134 -127
  31. package/dist/code-vision.js +224 -206
  32. package/dist/completion-call.js +94 -67
  33. package/dist/completion-model.js +334 -244
  34. package/dist/completion.js +840 -649
  35. package/dist/conflict-model.js +35 -29
  36. package/dist/context-menu-rows.js +42 -17
  37. package/dist/context-menu.js +64 -46
  38. package/dist/definition-link.js +88 -85
  39. package/dist/diagnostics-model.js +74 -55
  40. package/dist/diagnostics.js +121 -100
  41. package/dist/diff-expansion.js +38 -35
  42. package/dist/diff-overlays.js +144 -22
  43. package/dist/diff-syntax.js +65 -42
  44. package/dist/diff-wordmarks.js +26 -23
  45. package/dist/doc-comment-model.js +265 -216
  46. package/dist/editor-language.js +177 -152
  47. package/dist/error-message.js +2 -2
  48. package/dist/find-query.js +49 -36
  49. package/dist/folding.js +81 -62
  50. package/dist/format-on-type.js +86 -67
  51. package/dist/highlight-layers.js +19 -20
  52. package/dist/highlights.js +105 -83
  53. package/dist/history.js +44 -38
  54. package/dist/holder.js +18 -18
  55. package/dist/host-types.js +1 -1
  56. package/dist/hover-content.js +167 -129
  57. package/dist/hover.js +285 -245
  58. package/dist/index.js +10 -10
  59. package/dist/inlay-hints.js +369 -324
  60. package/dist/key-priority.js +34 -31
  61. package/dist/language-ids.js +45 -42
  62. package/dist/line-edits.js +74 -28
  63. package/dist/line-text.js +18 -3
  64. package/dist/locales/en.json +38 -38
  65. package/dist/locales/nl.json +41 -41
  66. package/dist/models.js +1 -1
  67. package/dist/navigation-history.js +40 -41
  68. package/dist/navigation.js +141 -123
  69. package/dist/panel-size.js +8 -8
  70. package/dist/patch-decorations.js +236 -160
  71. package/dist/patch-layout.js +331 -262
  72. package/dist/paths.js +3 -3
  73. package/dist/peek-model.js +86 -69
  74. package/dist/peek.js +254 -192
  75. package/dist/pick.js +164 -157
  76. package/dist/popup-focus.js +5 -5
  77. package/dist/popup-placement.js +36 -21
  78. package/dist/popups.js +13 -13
  79. package/dist/project-language.js +228 -211
  80. package/dist/project-problems.js +81 -69
  81. package/dist/proposal-model.js +163 -126
  82. package/dist/recent-choices.js +25 -25
  83. package/dist/refresher.js +44 -49
  84. package/dist/rename-model.js +113 -113
  85. package/dist/rename.js +321 -233
  86. package/dist/rendered-docs.d.ts +4 -2
  87. package/dist/rendered-docs.js +384 -318
  88. package/dist/rendering-context.js +5 -3
  89. package/dist/rendering.js +22 -4
  90. package/dist/review-group.js +42 -39
  91. package/dist/row-host.d.ts +4 -2
  92. package/dist/row-host.js +90 -78
  93. package/dist/selection-ranges.js +21 -17
  94. package/dist/semantic-delta.js +22 -19
  95. package/dist/semantic-model.js +86 -73
  96. package/dist/semantic-tokens.js +201 -162
  97. package/dist/shortcut-keys.js +16 -10
  98. package/dist/signature-model.js +34 -28
  99. package/dist/signature.js +177 -129
  100. package/dist/snippet-field-transforms.js +118 -106
  101. package/dist/snippet-indent.js +48 -43
  102. package/dist/snippet-range-order.js +11 -11
  103. package/dist/snippet-session.js +703 -667
  104. package/dist/snippet-transform-client.js +59 -59
  105. package/dist/snippet-transform-format.js +285 -263
  106. package/dist/snippet-transform-protocol.js +1 -1
  107. package/dist/snippet-transform-queue.js +163 -153
  108. package/dist/snippet-transform-worker.js +42 -35
  109. package/dist/snippet-variable-names.js +1 -36
  110. package/dist/snippet-variables.js +53 -63
  111. package/dist/snippet.js +477 -405
  112. package/dist/split-alignment.js +47 -35
  113. package/dist/split-scroll.js +42 -40
  114. package/dist/symbol-links.js +33 -25
  115. package/dist/symbol-picker-model.js +126 -116
  116. package/dist/symbol-picker.js +98 -59
  117. package/dist/symbols.js +117 -88
  118. package/dist/testing.js +248 -165
  119. package/dist/timers.js +35 -30
  120. package/dist/use-panel-body.js +49 -8
  121. package/dist/use-problem-counts.js +29 -15
  122. package/dist/view-state.js +54 -33
  123. package/dist/word-diff.js +18 -18
  124. package/dist/workspace-edit.js +300 -249
  125. package/package.json +6 -6
  126. package/src/CallHierarchyPanel.tsx +2 -2
  127. package/src/DiffView.tsx +3 -3
  128. package/src/FindReplace.tsx +2 -2
  129. package/src/LanguagePopups.tsx +2 -0
  130. package/src/PeekPanel.tsx +3 -3
  131. package/src/RenderedDocs.tsx +9 -10
  132. package/src/SymbolPicker.tsx +2 -2
  133. package/src/diff-overlays.tsx +6 -4
  134. package/src/locales/en.json +38 -38
  135. package/src/locales/nl.json +41 -41
  136. package/src/rendered-docs.ts +14 -3
  137. package/src/row-host.ts +14 -3
package/dist/snippet.js CHANGED
@@ -6,444 +6,516 @@ const MAX_SNIPPET_NODES = 10_000;
6
6
  const MAX_SNIPPET_WORK = 50_000;
7
7
  const MAX_SNIPPET_RANGES = 1_000;
8
8
  const MAX_SNIPPET_DEPTH = 64;
9
- class SnippetLimitError extends Error {
10
- }
11
- class MalformedSnippetError extends Error {
12
- }
9
+ class SnippetLimitError extends Error {}
10
+ class MalformedSnippetError extends Error {}
13
11
  class SnippetParser {
14
- at = 0;
15
- nodes = 0;
16
- depth = 0;
17
- name = /(?:\d+|[A-Za-z_]\w*)/y;
18
- source;
19
- constructor(source) {
20
- this.source = source;
12
+ at = 0;
13
+ nodes = 0;
14
+ depth = 0;
15
+ name = /(?:\d+|[A-Za-z_]\w*)/y;
16
+ source;
17
+ constructor(source) {
18
+ this.source = source;
19
+ }
20
+ parse() {
21
+ return this.sequence(false);
22
+ }
23
+ sequence(nested) {
24
+ if (++this.depth > MAX_SNIPPET_DEPTH) {
25
+ throw new SnippetLimitError();
21
26
  }
22
- parse() {
23
- return this.sequence(false);
24
- }
25
- sequence(nested) {
26
- if (++this.depth > MAX_SNIPPET_DEPTH) {
27
- throw new SnippetLimitError();
28
- }
29
- const nodes = [];
30
- let text = '';
31
- const flush = () => {
32
- if (text !== '') {
33
- this.push(nodes, { kind: 'text', text });
34
- text = '';
35
- }
36
- };
37
- while (this.at < this.source.length && !(nested && this.source[this.at] === '}')) {
38
- const character = this.source[this.at];
39
- if (character === '\\' && /[\\$}]/.test(this.source[this.at + 1] ?? '')) {
40
- text += this.source[this.at + 1];
41
- this.at += 2;
42
- }
43
- else if (character === '$') {
44
- const node = this.dollar();
45
- if (node === null) {
46
- text += '$';
47
- }
48
- else {
49
- flush();
50
- this.push(nodes, node);
51
- }
52
- }
53
- else {
54
- text += character;
55
- this.at++;
56
- }
27
+ const nodes = [];
28
+ let text = '';
29
+ const flush = () => {
30
+ if (text !== '') {
31
+ this.push(nodes, {
32
+ kind: 'text',
33
+ text
34
+ });
35
+ text = '';
36
+ }
37
+ };
38
+ while (this.at < this.source.length && !(nested && this.source[this.at] === '}')) {
39
+ const character = this.source[this.at];
40
+ if (character === '\\' && /[\\$}]/.test(this.source[this.at + 1] ?? '')) {
41
+ text += this.source[this.at + 1];
42
+ this.at += 2;
43
+ } else if (character === '$') {
44
+ const node = this.dollar();
45
+ if (node === null) {
46
+ text += '$';
47
+ } else {
48
+ flush();
49
+ this.push(nodes, node);
57
50
  }
58
- flush();
59
- this.depth--;
60
- return nodes;
51
+ } else {
52
+ text += character;
53
+ this.at++;
54
+ }
61
55
  }
62
- push(nodes, node) {
63
- if (++this.nodes > MAX_SNIPPET_NODES) {
64
- throw new SnippetLimitError();
65
- }
66
- nodes.push(node);
56
+ flush();
57
+ this.depth--;
58
+ return nodes;
59
+ }
60
+ push(nodes, node) {
61
+ if (++this.nodes > MAX_SNIPPET_NODES) {
62
+ throw new SnippetLimitError();
67
63
  }
68
- dollar() {
69
- const start = this.at++;
70
- const brace = this.source[this.at] === '{';
71
- if (brace) {
72
- this.at++;
73
- }
74
- this.name.lastIndex = this.at;
75
- const name = this.name.exec(this.source)?.[0];
76
- if (name === undefined) {
77
- this.at = start + 1;
78
- return null;
79
- }
80
- const index = /^\d+$/.test(name) ? Number(name) : null;
81
- if (index !== null && !Number.isSafeInteger(index)) {
82
- this.at = start + 1;
83
- return null;
84
- }
85
- this.at += name.length;
86
- const field = () => ({ kind: 'field', index: index });
87
- if (!brace) {
88
- return index === null ? { kind: 'variable', name } : field();
89
- }
90
- const next = this.source[this.at];
91
- if (next === '}') {
92
- this.at++;
93
- return index === null ? { kind: 'variable', name } : field();
94
- }
95
- if (next === ':') {
96
- this.at++;
97
- const body = this.sequence(true);
98
- if (this.source[this.at] === '}') {
99
- this.at++;
100
- }
101
- return index === null ? { kind: 'variable', name, body } : { ...field(), body };
102
- }
103
- if (next === '|' && index !== null) {
104
- this.at++;
105
- return { ...field(), choices: this.choices() };
64
+ nodes.push(node);
65
+ }
66
+ dollar() {
67
+ const start = this.at++;
68
+ const brace = this.source[this.at] === '{';
69
+ if (brace) {
70
+ this.at++;
71
+ }
72
+ this.name.lastIndex = this.at;
73
+ const name = this.name.exec(this.source)?.[0];
74
+ if (name === undefined) {
75
+ this.at = start + 1;
76
+ return null;
77
+ }
78
+ const index = /^\d+$/.test(name) ? Number(name) : null;
79
+ if (index !== null && !Number.isSafeInteger(index)) {
80
+ this.at = start + 1;
81
+ return null;
82
+ }
83
+ this.at += name.length;
84
+ const field = () => ({
85
+ kind: 'field',
86
+ index: index
87
+ });
88
+ if (!brace) {
89
+ return index === null ? {
90
+ kind: 'variable',
91
+ name
92
+ } : field();
93
+ }
94
+ const next = this.source[this.at];
95
+ if (next === '}') {
96
+ this.at++;
97
+ return index === null ? {
98
+ kind: 'variable',
99
+ name
100
+ } : field();
101
+ }
102
+ if (next === ':') {
103
+ this.at++;
104
+ const body = this.sequence(true);
105
+ if (this.source[this.at] === '}') {
106
+ this.at++;
107
+ }
108
+ return index === null ? {
109
+ kind: 'variable',
110
+ name,
111
+ body
112
+ } : {
113
+ ...field(),
114
+ body
115
+ };
116
+ }
117
+ if (next === '|' && index !== null) {
118
+ this.at++;
119
+ return {
120
+ ...field(),
121
+ choices: this.choices()
122
+ };
123
+ }
124
+ if (next === '/') {
125
+ try {
126
+ const {
127
+ end,
128
+ ...transform
129
+ } = scanSnippetTransform(this.source, this.at);
130
+ this.at = end;
131
+ return index === null ? {
132
+ kind: 'variable',
133
+ name,
134
+ transform
135
+ } : {
136
+ ...field(),
137
+ transform
138
+ };
139
+ } catch (error) {
140
+ if (error instanceof RangeError) {
141
+ throw new SnippetLimitError();
106
142
  }
107
- if (next === '/') {
108
- try {
109
- const { end, ...transform } = scanSnippetTransform(this.source, this.at);
110
- this.at = end;
111
- return index === null ? { kind: 'variable', name, transform } : { ...field(), transform };
112
- }
113
- catch (error) {
114
- if (error instanceof RangeError) {
115
- throw new SnippetLimitError();
116
- }
117
- if (error instanceof SyntaxError) {
118
- throw new MalformedSnippetError();
119
- }
120
- throw error;
121
- }
143
+ if (error instanceof SyntaxError) {
144
+ throw new MalformedSnippetError();
122
145
  }
123
- this.at = start + 1;
124
- return null;
146
+ throw error;
147
+ }
125
148
  }
126
- choices() {
127
- const values = [''];
128
- while (this.at < this.source.length && !(this.source[this.at] === '|' && this.source[this.at + 1] === '}')) {
129
- const character = this.source[this.at++];
130
- if (character === '\\' && /[\\,|]/.test(this.source[this.at] ?? '')) {
131
- values[values.length - 1] += this.source[this.at++];
132
- }
133
- else if (character === ',') {
134
- if (values.length >= MAX_SNIPPET_NODES) {
135
- throw new SnippetLimitError();
136
- }
137
- values.push('');
138
- }
139
- else {
140
- values[values.length - 1] += character;
141
- }
149
+ this.at = start + 1;
150
+ return null;
151
+ }
152
+ choices() {
153
+ const values = [''];
154
+ while (this.at < this.source.length && !(this.source[this.at] === '|' && this.source[this.at + 1] === '}')) {
155
+ const character = this.source[this.at++];
156
+ if (character === '\\' && /[\\,|]/.test(this.source[this.at] ?? '')) {
157
+ values[values.length - 1] += this.source[this.at++];
158
+ } else if (character === ',') {
159
+ if (values.length >= MAX_SNIPPET_NODES) {
160
+ throw new SnippetLimitError();
142
161
  }
143
- this.at = Math.min(this.source.length, this.at + 2);
144
- return values;
162
+ values.push('');
163
+ } else {
164
+ values[values.length - 1] += character;
165
+ }
145
166
  }
167
+ this.at = Math.min(this.source.length, this.at + 2);
168
+ return values;
169
+ }
146
170
  }
147
171
  function renderSnippet(nodes, variableTransforms) {
148
- const definitions = new Map();
149
- const collect = (nodes) => {
150
- for (const node of nodes) {
151
- if (node.kind === 'field') {
152
- const known = definitions.get(node.index);
153
- if (known === undefined ||
154
- (known.body === undefined &&
155
- known.choices === undefined &&
156
- (node.body !== undefined || node.choices !== undefined || (known.transform !== undefined && node.transform === undefined)))) {
157
- definitions.set(node.index, node);
158
- }
159
- }
160
- if (node.kind !== 'text' && node.body !== undefined) {
161
- collect(node.body);
162
- }
172
+ const definitions = new Map();
173
+ const collect = nodes => {
174
+ for (const node of nodes) {
175
+ if (node.kind === 'field') {
176
+ const known = definitions.get(node.index);
177
+ if (known === undefined || known.body === undefined && known.choices === undefined && (node.body !== undefined || node.choices !== undefined || known.transform !== undefined && node.transform === undefined)) {
178
+ definitions.set(node.index, node);
163
179
  }
164
- };
165
- collect(nodes);
166
- const fields = new Map();
167
- const stops = [];
168
- const transforms = [];
169
- let text = '';
170
- let work = 0;
171
- let ranges = 0;
172
- let occurrence = 0;
173
- let tailEvent = 0;
174
- let tailOffset = -1;
175
- const recordTail = (range) => {
176
- if (range.start === range.end || range.tail === true) {
177
- tailEvent++;
178
- tailOffset = range.end;
180
+ }
181
+ if (node.kind !== 'text' && node.body !== undefined) {
182
+ collect(node.body);
183
+ }
184
+ }
185
+ };
186
+ collect(nodes);
187
+ const fields = new Map();
188
+ const stops = [];
189
+ const transforms = [];
190
+ let text = '';
191
+ let work = 0;
192
+ let ranges = 0;
193
+ let occurrence = 0;
194
+ let tailEvent = 0;
195
+ let tailOffset = -1;
196
+ const recordTail = range => {
197
+ if (range.start === range.end || range.tail === true) {
198
+ tailEvent++;
199
+ tailOffset = range.end;
200
+ }
201
+ };
202
+ const append = value => {
203
+ if (text.length + value.length > MAX_SNIPPET_TEXT) {
204
+ throw new SnippetLimitError();
205
+ }
206
+ text += value;
207
+ };
208
+ const render = (nodes, linked, parents, visiting) => {
209
+ if (parents.length > MAX_SNIPPET_DEPTH) {
210
+ throw new SnippetLimitError();
211
+ }
212
+ for (const node of nodes) {
213
+ if (++work > MAX_SNIPPET_WORK) {
214
+ throw new SnippetLimitError();
215
+ }
216
+ const order = occurrence++;
217
+ if (node.kind === 'text') {
218
+ const start = text.length;
219
+ const value = node.transform === undefined || variableTransforms === undefined ? node.text : variableTransforms[transforms.length];
220
+ if (typeof value !== 'string') {
221
+ throw new RangeError('The variable transform count does not match the snippet.');
179
222
  }
180
- };
181
- const append = (value) => {
182
- if (text.length + value.length > MAX_SNIPPET_TEXT) {
223
+ append(value);
224
+ if (node.transform !== undefined) {
225
+ if (++ranges > MAX_SNIPPET_RANGES) {
183
226
  throw new SnippetLimitError();
227
+ }
228
+ const range = {
229
+ start,
230
+ end: text.length,
231
+ transform: node.transform,
232
+ ...(parents.length === 0 ? {} : {
233
+ parents: [...parents]
234
+ })
235
+ };
236
+ withSnippetOrder(range, order);
237
+ transforms.push(range);
238
+ recordTail(range);
184
239
  }
185
- text += value;
186
- };
187
- const render = (nodes, linked, parents, visiting) => {
188
- if (parents.length > MAX_SNIPPET_DEPTH) {
189
- throw new SnippetLimitError();
240
+ continue;
241
+ }
242
+ if (node.kind === 'variable') {
243
+ render(node.body ?? [], linked, parents, visiting);
244
+ continue;
245
+ }
246
+ if (++ranges > MAX_SNIPPET_RANGES) {
247
+ throw new SnippetLimitError();
248
+ }
249
+ const definition = definitions.get(node.index);
250
+ const from = text.length;
251
+ const beforeTail = tailEvent;
252
+ if (!visiting.has(node.index)) {
253
+ if (definition.choices !== undefined) {
254
+ append(definition.choices[0] ?? '');
255
+ } else {
256
+ render(definition.body ?? [], linked || node !== definition, [...parents, node.index], new Set([...visiting, node.index]));
190
257
  }
191
- for (const node of nodes) {
192
- if (++work > MAX_SNIPPET_WORK) {
193
- throw new SnippetLimitError();
194
- }
195
- const order = occurrence++;
196
- if (node.kind === 'text') {
197
- const start = text.length;
198
- const value = node.transform === undefined || variableTransforms === undefined ? node.text : variableTransforms[transforms.length];
199
- if (typeof value !== 'string') {
200
- throw new RangeError('The variable transform count does not match the snippet.');
201
- }
202
- append(value);
203
- if (node.transform !== undefined) {
204
- if (++ranges > MAX_SNIPPET_RANGES) {
205
- throw new SnippetLimitError();
206
- }
207
- const range = { start, end: text.length, transform: node.transform, ...(parents.length === 0 ? {} : { parents: [...parents] }) };
208
- withSnippetOrder(range, order);
209
- transforms.push(range);
210
- recordTail(range);
211
- }
212
- continue;
213
- }
214
- if (node.kind === 'variable') {
215
- render(node.body ?? [], linked, parents, visiting);
216
- continue;
217
- }
218
- if (++ranges > MAX_SNIPPET_RANGES) {
219
- throw new SnippetLimitError();
220
- }
221
- const definition = definitions.get(node.index);
222
- const from = text.length;
223
- const beforeTail = tailEvent;
224
- if (!visiting.has(node.index)) {
225
- if (definition.choices !== undefined) {
226
- append(definition.choices[0] ?? '');
227
- }
228
- else {
229
- render(definition.body ?? [], linked || node !== definition, [...parents, node.index], new Set([...visiting, node.index]));
230
- }
231
- }
232
- const range = {
233
- start: from,
234
- end: text.length,
235
- ...(parents.length === 0 ? {} : { parents: [...parents] }),
236
- ...(node.transform === undefined ? {} : { transform: node.transform }),
237
- ...(tailEvent !== beforeTail && tailOffset === text.length ? { tail: true } : {})
238
- };
239
- withSnippetOrder(range, order);
240
- recordTail(range);
241
- const field = fields.get(node.index) ?? {
242
- index: node.index,
243
- ranges: [],
244
- linked: [],
245
- primary: 0,
246
- parents: new Set(),
247
- choices: definition.choices
248
- };
249
- parents.filter((parent) => parent !== node.index).forEach((parent) => field.parents.add(parent));
250
- if (linked) {
251
- field.linked.push(range);
252
- }
253
- else {
254
- if (node === definition) {
255
- field.primary = field.ranges.length;
256
- }
257
- field.ranges.push(range);
258
- stops.push(withSnippetOrder({
259
- index: node.index,
260
- start: range.start,
261
- end: range.end,
262
- ...(node.transform === undefined ? {} : { transform: node.transform }),
263
- ...(range.tail === undefined ? {} : { tail: range.tail })
264
- }, order));
265
- }
266
- fields.set(node.index, field);
258
+ }
259
+ const range = {
260
+ start: from,
261
+ end: text.length,
262
+ ...(parents.length === 0 ? {} : {
263
+ parents: [...parents]
264
+ }),
265
+ ...(node.transform === undefined ? {} : {
266
+ transform: node.transform
267
+ }),
268
+ ...(tailEvent !== beforeTail && tailOffset === text.length ? {
269
+ tail: true
270
+ } : {})
271
+ };
272
+ withSnippetOrder(range, order);
273
+ recordTail(range);
274
+ const field = fields.get(node.index) ?? {
275
+ index: node.index,
276
+ ranges: [],
277
+ linked: [],
278
+ primary: 0,
279
+ parents: new Set(),
280
+ choices: definition.choices
281
+ };
282
+ parents.filter(parent => parent !== node.index).forEach(parent => field.parents.add(parent));
283
+ if (linked) {
284
+ field.linked.push(range);
285
+ } else {
286
+ if (node === definition) {
287
+ field.primary = field.ranges.length;
267
288
  }
268
- };
269
- render(nodes, false, [], new Set());
270
- if (variableTransforms !== undefined && variableTransforms.length !== transforms.length) {
271
- throw new RangeError('The variable transform count does not match the snippet.');
289
+ field.ranges.push(range);
290
+ stops.push(withSnippetOrder({
291
+ index: node.index,
292
+ start: range.start,
293
+ end: range.end,
294
+ ...(node.transform === undefined ? {} : {
295
+ transform: node.transform
296
+ }),
297
+ ...(range.tail === undefined ? {} : {
298
+ tail: range.tail
299
+ })
300
+ }, order));
301
+ }
302
+ fields.set(node.index, field);
272
303
  }
273
- stops.sort((left, right) => left.start - right.start || right.end - left.end);
274
- return {
275
- text,
276
- stops,
277
- ...(transforms.length === 0 ? {} : { transforms }),
278
- fields: [...fields.values()]
279
- .filter((field) => field.ranges.length > 0)
280
- .map((field) => ({
281
- index: field.index,
282
- ranges: field.ranges,
283
- primary: field.primary,
284
- ...(field.linked.length === 0 ? {} : { linked: field.linked }),
285
- ...(field.parents.size === 0 ? {} : { parents: [...field.parents] }),
286
- ...(field.choices === undefined ? {} : { choices: field.choices })
287
- }))
288
- };
304
+ };
305
+ render(nodes, false, [], new Set());
306
+ if (variableTransforms !== undefined && variableTransforms.length !== transforms.length) {
307
+ throw new RangeError('The variable transform count does not match the snippet.');
308
+ }
309
+ stops.sort((left, right) => left.start - right.start || right.end - left.end);
310
+ return {
311
+ text,
312
+ stops,
313
+ ...(transforms.length === 0 ? {} : {
314
+ transforms
315
+ }),
316
+ fields: [...fields.values()].filter(field => field.ranges.length > 0).map(field => ({
317
+ index: field.index,
318
+ ranges: field.ranges,
319
+ primary: field.primary,
320
+ ...(field.linked.length === 0 ? {} : {
321
+ linked: field.linked
322
+ }),
323
+ ...(field.parents.size === 0 ? {} : {
324
+ parents: [...field.parents]
325
+ }),
326
+ ...(field.choices === undefined ? {} : {
327
+ choices: field.choices
328
+ })
329
+ }))
330
+ };
289
331
  }
290
332
  function variableIndices(nodes) {
291
- let highest = 0;
292
- const names = new Set();
293
- const scan = (nodes) => {
294
- for (const node of nodes) {
295
- if (node.kind === 'field') {
296
- highest = Math.max(highest, node.index);
297
- }
298
- else if (node.kind === 'variable' && !SNIPPET_VARIABLES.has(node.name)) {
299
- names.add(node.name);
300
- }
301
- if (node.kind !== 'text' && node.body !== undefined) {
302
- scan(node.body);
303
- }
304
- }
305
- };
306
- scan(nodes);
307
- return new Map([...names].map((name) => {
308
- if (!Number.isSafeInteger(++highest)) {
309
- throw new SnippetLimitError();
310
- }
311
- return [name, highest];
312
- }));
333
+ let highest = 0;
334
+ const names = new Set();
335
+ const scan = nodes => {
336
+ for (const node of nodes) {
337
+ if (node.kind === 'field') {
338
+ highest = Math.max(highest, node.index);
339
+ } else if (node.kind === 'variable' && !SNIPPET_VARIABLES.has(node.name)) {
340
+ names.add(node.name);
341
+ }
342
+ if (node.kind !== 'text' && node.body !== undefined) {
343
+ scan(node.body);
344
+ }
345
+ }
346
+ };
347
+ scan(nodes);
348
+ return new Map([...names].map(name => {
349
+ if (!Number.isSafeInteger(++highest)) {
350
+ throw new SnippetLimitError();
351
+ }
352
+ return [name, highest];
353
+ }));
313
354
  }
314
355
  function resolveVariables(nodes, indices, resolve) {
315
- const unknown = new Map();
316
- const values = new Map();
317
- const valueOf = (name) => {
318
- if (!values.has(name)) {
319
- values.set(name, resolve?.(name));
320
- }
321
- return values.get(name);
322
- };
323
- const collect = (nodes) => {
324
- for (const node of nodes) {
325
- if (node.kind === 'variable' && valueOf(node.name) === undefined && !SNIPPET_VARIABLES.has(node.name)) {
326
- const previous = unknown.get(node.name);
327
- if (previous === undefined) {
328
- unknown.set(node.name, {
329
- index: indices.get(node.name),
330
- body: node.body ?? [{ kind: 'text', text: node.name }],
331
- defaulted: node.body !== undefined,
332
- definition: node
333
- });
334
- }
335
- else if (node.body !== undefined && !previous.defaulted) {
336
- previous.body = node.body;
337
- previous.defaulted = true;
338
- previous.definition = node;
339
- }
340
- else if (!previous.defaulted && node.transform === undefined && previous.definition.transform !== undefined) {
341
- previous.definition = node;
342
- }
343
- }
344
- if (node.kind !== 'text' && node.body !== undefined) {
345
- collect(node.body);
346
- }
356
+ const unknown = new Map();
357
+ const values = new Map();
358
+ const valueOf = name => {
359
+ if (!values.has(name)) {
360
+ values.set(name, resolve?.(name));
361
+ }
362
+ return values.get(name);
363
+ };
364
+ const collect = nodes => {
365
+ for (const node of nodes) {
366
+ if (node.kind === 'variable' && valueOf(node.name) === undefined && !SNIPPET_VARIABLES.has(node.name)) {
367
+ const previous = unknown.get(node.name);
368
+ if (previous === undefined) {
369
+ unknown.set(node.name, {
370
+ index: indices.get(node.name),
371
+ body: node.body ?? [{
372
+ kind: 'text',
373
+ text: node.name
374
+ }],
375
+ defaulted: node.body !== undefined,
376
+ definition: node
377
+ });
378
+ } else if (node.body !== undefined && !previous.defaulted) {
379
+ previous.body = node.body;
380
+ previous.defaulted = true;
381
+ previous.definition = node;
382
+ } else if (!previous.defaulted && node.transform === undefined && previous.definition.transform !== undefined) {
383
+ previous.definition = node;
347
384
  }
348
- };
349
- collect(nodes);
350
- // Resolved empty variables retain their defaults without adding a navigable field.
351
- let work = 0;
352
- const normalize = (nodes, visiting, depth = 0) => {
353
- if (depth > MAX_SNIPPET_DEPTH) {
354
- throw new SnippetLimitError();
385
+ }
386
+ if (node.kind !== 'text' && node.body !== undefined) {
387
+ collect(node.body);
388
+ }
389
+ }
390
+ };
391
+ collect(nodes);
392
+ // Resolved empty variables retain their defaults without adding a navigable field.
393
+ let work = 0;
394
+ const normalize = (nodes, visiting, depth = 0) => {
395
+ if (depth > MAX_SNIPPET_DEPTH) {
396
+ throw new SnippetLimitError();
397
+ }
398
+ return nodes.flatMap(node => {
399
+ if (++work > MAX_SNIPPET_WORK) {
400
+ throw new SnippetLimitError();
401
+ }
402
+ if (node.kind === 'variable' && !unknown.has(node.name)) {
403
+ const value = valueOf(node.name);
404
+ if (node.transform !== undefined) {
405
+ return [{
406
+ kind: 'text',
407
+ text: value ?? '',
408
+ transform: node.transform
409
+ }];
355
410
  }
356
- return nodes.flatMap((node) => {
357
- if (++work > MAX_SNIPPET_WORK) {
358
- throw new SnippetLimitError();
359
- }
360
- if (node.kind === 'variable' && !unknown.has(node.name)) {
361
- const value = valueOf(node.name);
362
- if (node.transform !== undefined) {
363
- return [{ kind: 'text', text: value ?? '', transform: node.transform }];
364
- }
365
- return value !== undefined && value !== '' ? [{ kind: 'text', text: value }] : normalize(node.body ?? [], visiting, depth + 1);
366
- }
367
- if (node.kind === 'field') {
368
- return [{ ...node, ...(node.body === undefined ? {} : { body: normalize(node.body, visiting, depth + 1) }) }];
369
- }
370
- if (node.kind === 'variable') {
371
- const field = unknown.get(node.name);
372
- return [
373
- {
374
- kind: 'field',
375
- index: field.index,
376
- ...(node.transform === undefined ? {} : { transform: node.transform }),
377
- ...(node !== field.definition
378
- ? {}
379
- : { body: visiting.has(node.name) ? [] : normalize(field.body, new Set([...visiting, node.name]), depth + 1) })
380
- }
381
- ];
382
- }
383
- return [node];
384
- });
385
- };
386
- return normalize(nodes, new Set());
411
+ return value !== undefined && value !== '' ? [{
412
+ kind: 'text',
413
+ text: value
414
+ }] : normalize(node.body ?? [], visiting, depth + 1);
415
+ }
416
+ if (node.kind === 'field') {
417
+ return [{
418
+ ...node,
419
+ ...(node.body === undefined ? {} : {
420
+ body: normalize(node.body, visiting, depth + 1)
421
+ })
422
+ }];
423
+ }
424
+ if (node.kind === 'variable') {
425
+ const field = unknown.get(node.name);
426
+ return [{
427
+ kind: 'field',
428
+ index: field.index,
429
+ ...(node.transform === undefined ? {} : {
430
+ transform: node.transform
431
+ }),
432
+ ...(node !== field.definition ? {} : {
433
+ body: visiting.has(node.name) ? [] : normalize(field.body, new Set([...visiting, node.name]), depth + 1)
434
+ })
435
+ }];
436
+ }
437
+ return [node];
438
+ });
439
+ };
440
+ return normalize(nodes, new Set());
387
441
  }
388
442
  export function compileSnippet(source) {
389
- let nodes = null;
390
- let indices = new Map();
391
- if (source.length <= MAX_SNIPPET_TEXT) {
392
- try {
393
- nodes = new SnippetParser(source).parse();
394
- indices = variableIndices(nodes);
395
- }
396
- catch (error) {
397
- if (error instanceof MalformedSnippetError) {
398
- return { source, render: () => ({ text: source, stops: [] }) };
399
- }
400
- if (!(error instanceof SnippetLimitError)) {
401
- throw error;
402
- }
403
- nodes = null;
404
- }
443
+ let nodes = null;
444
+ let indices = new Map();
445
+ if (source.length <= MAX_SNIPPET_TEXT) {
446
+ try {
447
+ nodes = new SnippetParser(source).parse();
448
+ indices = variableIndices(nodes);
449
+ } catch (error) {
450
+ if (error instanceof MalformedSnippetError) {
451
+ return {
452
+ source,
453
+ render: () => ({
454
+ text: source,
455
+ stops: []
456
+ })
457
+ };
458
+ }
459
+ if (!(error instanceof SnippetLimitError)) {
460
+ throw error;
461
+ }
462
+ nodes = null;
405
463
  }
406
- return {
407
- source,
408
- render: (resolve, variableTransforms) => {
409
- if (nodes === null) {
410
- return { text: source, stops: [], limited: true };
411
- }
412
- try {
413
- return renderSnippet(resolveVariables(nodes, indices, resolve), variableTransforms);
414
- }
415
- catch (error) {
416
- if (!(error instanceof SnippetLimitError)) {
417
- throw error;
418
- }
419
- // Language-server snippets may expand exponentially; retain the original text without a session.
420
- return { text: source, stops: [], limited: true };
421
- }
464
+ }
465
+ return {
466
+ source,
467
+ render: (resolve, variableTransforms) => {
468
+ if (nodes === null) {
469
+ return {
470
+ text: source,
471
+ stops: [],
472
+ limited: true
473
+ };
474
+ }
475
+ try {
476
+ return renderSnippet(resolveVariables(nodes, indices, resolve), variableTransforms);
477
+ } catch (error) {
478
+ if (!(error instanceof SnippetLimitError)) {
479
+ throw error;
422
480
  }
423
- };
481
+ // Language-server snippets may expand exponentially; retain the original text without a session.
482
+ return {
483
+ text: source,
484
+ stops: [],
485
+ limited: true
486
+ };
487
+ }
488
+ }
489
+ };
424
490
  }
425
491
  export function parseSnippet(snippet, resolve) {
426
- return compileSnippet(snippet).render(resolve);
492
+ return compileSnippet(snippet).render(resolve);
427
493
  }
428
494
  /* One defining occurrence per index, then $0 or the end of the inserted text. */
429
495
  export function tabOrder(parsed) {
430
- const first = new Map();
431
- for (const stop of parsed.stops) {
432
- if (!first.has(stop.index)) {
433
- first.set(stop.index, stop);
434
- }
496
+ const first = new Map();
497
+ for (const stop of parsed.stops) {
498
+ if (!first.has(stop.index)) {
499
+ first.set(stop.index, stop);
435
500
  }
436
- for (const field of parsed.fields ?? []) {
437
- const range = field.ranges[field.primary];
438
- if (range !== undefined) {
439
- first.set(field.index, {
440
- index: field.index,
441
- start: range.start,
442
- end: range.end,
443
- ...(range.transform === undefined ? {} : { transform: range.transform })
444
- });
445
- }
501
+ }
502
+ for (const field of parsed.fields ?? []) {
503
+ const range = field.ranges[field.primary];
504
+ if (range !== undefined) {
505
+ first.set(field.index, {
506
+ index: field.index,
507
+ start: range.start,
508
+ end: range.end,
509
+ ...(range.transform === undefined ? {} : {
510
+ transform: range.transform
511
+ })
512
+ });
446
513
  }
447
- const visited = [...first.values()].filter((stop) => stop.index > 0).sort((left, right) => left.index - right.index);
448
- return [...visited, first.get(0) ?? { index: 0, start: parsed.text.length, end: parsed.text.length }];
449
- }
514
+ }
515
+ const visited = [...first.values()].filter(stop => stop.index > 0).sort((left, right) => left.index - right.index);
516
+ return [...visited, first.get(0) ?? {
517
+ index: 0,
518
+ start: parsed.text.length,
519
+ end: parsed.text.length
520
+ }];
521
+ }