@adecore/editor-react 0.31.12 → 0.33.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
@@ -6,23 +6,23 @@ import { compileSnippet, parseSnippet, tabOrder } from './snippet.js';
6
6
  import { indentSnippet } from './snippet-indent.js';
7
7
  const IDENTIFIER_CHARACTER = /[\p{L}\p{N}\p{M}_$]/u;
8
8
  export function isIdentifierCharacter(character) {
9
- return IDENTIFIER_CHARACTER.test(character);
9
+ return IDENTIFIER_CHARACTER.test(character);
10
10
  }
11
11
  let lastBefore = '';
12
12
  let lastPrefix = '';
13
13
  /* The word being typed: the identifier characters at the end of the text before the caret. A list asks it of every item with the same line, so the last answer is kept. */
14
14
  export function identifierPrefix(before) {
15
- if (before === lastBefore) {
16
- return lastPrefix;
17
- }
18
- const characters = Array.from(before);
19
- let start = characters.length;
20
- while (start > 0 && IDENTIFIER_CHARACTER.test(characters[start - 1])) {
21
- start--;
22
- }
23
- lastBefore = before;
24
- lastPrefix = characters.slice(start).join('');
15
+ if (before === lastBefore) {
25
16
  return lastPrefix;
17
+ }
18
+ const characters = Array.from(before);
19
+ let start = characters.length;
20
+ while (start > 0 && IDENTIFIER_CHARACTER.test(characters[start - 1])) {
21
+ start--;
22
+ }
23
+ lastBefore = before;
24
+ lastPrefix = characters.slice(start).join('');
25
+ return lastPrefix;
26
26
  }
27
27
  const OPENS_VALUE = /(?:[-=!<>&|?+*/%^:,]|(?:^|[^\p{L}\p{N}_$])(?:return|new|case|in|of|instanceof|typeof|keyof|await|yield|throw|extends|implements|as|is|not|and|or|echo|print|use|import|from|clone))$/u;
28
28
  const NOT_A_VALUE = /(?:\+\+|--|\/\/|\/\*\*?|^\s*[-+*])$/;
@@ -32,36 +32,35 @@ const NOT_A_VALUE = /(?:\+\+|--|\/\/|\/\*\*?|^\s*[-+*])$/;
32
32
  * opener, a list marker and `++` or `--` open nothing.
33
33
  */
34
34
  export function opensCompletion(before, text) {
35
- if (text === '(' || text === '()' || text === '[' || text === '[]') {
36
- return true;
37
- }
38
- return text === ' ' && OPENS_VALUE.test(before) && !NOT_A_VALUE.test(before);
35
+ if (text === '(' || text === '()' || text === '[' || text === '[]') {
36
+ return true;
37
+ }
38
+ return text === ' ' && OPENS_VALUE.test(before) && !NOT_A_VALUE.test(before);
39
39
  }
40
40
  function run(start, length) {
41
- return Array.from({ length }, (_, offset) => start + offset);
41
+ return Array.from({
42
+ length
43
+ }, (_, offset) => start + offset);
42
44
  }
43
45
  const SHAPE_LIMIT = 4000;
44
46
  const shapes = new Map();
45
47
  /* What matching reads of a label, which is the same at every keystroke of a word and costs a regular expression pass to find. */
46
48
  function shapeOf(value) {
47
- const known = shapes.get(value);
48
- if (known !== undefined) {
49
- return known;
50
- }
51
- const boundaries = [...value.matchAll(/(?:^|[_$\s-])([\p{L}\p{N}])|(\p{Lu})/gu)];
52
- const shape = {
53
- lower: value.toLocaleLowerCase(),
54
- offsets: boundaries.map((boundary) => boundary.index + (boundary[1] ? boundary[0].length - boundary[1].length : 0)),
55
- initials: boundaries
56
- .map((match) => match[1] ?? match[2])
57
- .join('')
58
- .toLocaleLowerCase()
59
- };
60
- if (shapes.size >= SHAPE_LIMIT) {
61
- shapes.clear();
62
- }
63
- shapes.set(value, shape);
64
- return shape;
49
+ const known = shapes.get(value);
50
+ if (known !== undefined) {
51
+ return known;
52
+ }
53
+ const boundaries = [...value.matchAll(/(?:^|[_$\s-])([\p{L}\p{N}])|(\p{Lu})/gu)];
54
+ const shape = {
55
+ lower: value.toLocaleLowerCase(),
56
+ offsets: boundaries.map(boundary => boundary.index + (boundary[1] ? boundary[0].length - boundary[1].length : 0)),
57
+ initials: boundaries.map(match => match[1] ?? match[2]).join('').toLocaleLowerCase()
58
+ };
59
+ if (shapes.size >= SHAPE_LIMIT) {
60
+ shapes.clear();
61
+ }
62
+ shapes.set(value, shape);
63
+ return shape;
65
64
  }
66
65
  /*
67
66
  * How well a prefix fits a label, lower being better, and which characters it matched, or undefined when
@@ -70,75 +69,98 @@ function shapeOf(value) {
70
69
  * in order for a prefix of two or more.
71
70
  */
72
71
  export function matchDetail(value, prefix) {
73
- if (prefix === '') {
74
- return { score: 0, positions: [] };
75
- }
76
- if (value.startsWith(prefix)) {
77
- return { score: 0, positions: run(0, prefix.length) };
78
- }
79
- const { lower, offsets, initials } = shapeOf(value);
80
- const query = prefix.toLocaleLowerCase();
81
- if (lower.startsWith(query)) {
82
- return { score: 1, positions: run(0, query.length) };
83
- }
84
- for (const offset of offsets) {
85
- if (lower.slice(offset).startsWith(query)) {
86
- return { score: 2 + offset / 1000, positions: run(offset, query.length) };
87
- }
88
- }
89
- if (initials.startsWith(query)) {
90
- return { score: 3, positions: offsets.slice(0, query.length) };
91
- }
92
- if (query.length < 2 || lower[0] !== query[0]) {
93
- return undefined;
72
+ if (prefix === '') {
73
+ return {
74
+ score: 0,
75
+ positions: []
76
+ };
77
+ }
78
+ if (value.startsWith(prefix)) {
79
+ return {
80
+ score: 0,
81
+ positions: run(0, prefix.length)
82
+ };
83
+ }
84
+ const {
85
+ lower,
86
+ offsets,
87
+ initials
88
+ } = shapeOf(value);
89
+ const query = prefix.toLocaleLowerCase();
90
+ if (lower.startsWith(query)) {
91
+ return {
92
+ score: 1,
93
+ positions: run(0, query.length)
94
+ };
95
+ }
96
+ for (const offset of offsets) {
97
+ if (lower.slice(offset).startsWith(query)) {
98
+ return {
99
+ score: 2 + offset / 1000,
100
+ positions: run(offset, query.length)
101
+ };
94
102
  }
95
- let cursor = 0;
96
- const positions = [];
97
- for (const character of query) {
98
- const next = lower.indexOf(character, cursor);
99
- if (next < 0) {
100
- return undefined;
101
- }
102
- positions.push(next);
103
- cursor = next + character.length;
103
+ }
104
+ if (initials.startsWith(query)) {
105
+ return {
106
+ score: 3,
107
+ positions: offsets.slice(0, query.length)
108
+ };
109
+ }
110
+ if (query.length < 2 || lower[0] !== query[0]) {
111
+ return undefined;
112
+ }
113
+ let cursor = 0;
114
+ const positions = [];
115
+ for (const character of query) {
116
+ const next = lower.indexOf(character, cursor);
117
+ if (next < 0) {
118
+ return undefined;
104
119
  }
105
- return { score: 4 + cursor / 1000, positions };
120
+ positions.push(next);
121
+ cursor = next + character.length;
122
+ }
123
+ return {
124
+ score: 4 + cursor / 1000,
125
+ positions
126
+ };
106
127
  }
107
128
  export function matchScore(value, prefix) {
108
- return matchDetail(value, prefix)?.score;
129
+ return matchDetail(value, prefix)?.score;
109
130
  }
110
131
  /* The range an item replaces when it is accepted; for an insert-or-replace edit, the one `replace` asks for. */
111
132
  export function editRangeOf(item, replace) {
112
- const edit = item.textEdit;
113
- if (edit === undefined) {
114
- return null;
115
- }
116
- if ('range' in edit) {
117
- return edit.range;
118
- }
119
- return replace ? edit.replace : edit.insert;
133
+ const edit = item.textEdit;
134
+ if (edit === undefined) {
135
+ return null;
136
+ }
137
+ if ('range' in edit) {
138
+ return edit.range;
139
+ }
140
+ return replace ? edit.replace : edit.insert;
120
141
  }
121
142
  /* Items from a result, with the list's defaults filled in so each carries its own edit range and format. */
122
143
  export function itemsOf(result) {
123
- const list = result === null || Array.isArray(result) ? undefined : result;
124
- const items = result === null ? [] : Array.isArray(result) ? result : result.items;
125
- return {
126
- incomplete: list?.isIncomplete ?? false,
127
- items: items.flatMap((item) => {
128
- if (typeof item?.label !== 'string') {
129
- return [];
130
- }
131
- try {
132
- return [applyCompletionDefaults(item, list?.itemDefaults, { onInvalidRange: () => undefined })];
133
- }
134
- catch (error) {
135
- if (error instanceof TypeError || error instanceof RangeError) {
136
- return [];
137
- }
138
- throw error;
139
- }
140
- })
141
- };
144
+ const list = result === null || Array.isArray(result) ? undefined : result;
145
+ const items = result === null ? [] : Array.isArray(result) ? result : result.items;
146
+ return {
147
+ incomplete: list?.isIncomplete ?? false,
148
+ items: items.flatMap(item => {
149
+ if (typeof item?.label !== 'string') {
150
+ return [];
151
+ }
152
+ try {
153
+ return [applyCompletionDefaults(item, list?.itemDefaults, {
154
+ onInvalidRange: () => undefined
155
+ })];
156
+ } catch (error) {
157
+ if (error instanceof TypeError || error instanceof RangeError) {
158
+ return [];
159
+ }
160
+ throw error;
161
+ }
162
+ })
163
+ };
142
164
  }
143
165
  /*
144
166
  * What a person has typed of an item: from the start of the range it will replace up to the caret when
@@ -146,70 +168,80 @@ export function itemsOf(result) {
146
168
  * `thisArg.` from filtering on the dot.
147
169
  */
148
170
  export function prefixFor(item, caret, lineBefore, textBetween) {
149
- const range = editRangeOf(item, false);
150
- if (range !== null && range.start.line === caret.line && comparePositions(range.start, caret) <= 0 && comparePositions(caret, range.end) <= 0) {
151
- return textBetween({ start: range.start, end: caret });
152
- }
153
- return identifierPrefix(lineBefore);
171
+ const range = editRangeOf(item, false);
172
+ if (range !== null && range.start.line === caret.line && comparePositions(range.start, caret) <= 0 && comparePositions(caret, range.end) <= 0) {
173
+ return textBetween({
174
+ start: range.start,
175
+ end: caret
176
+ });
177
+ }
178
+ return identifierPrefix(lineBefore);
154
179
  }
155
180
  /*
156
181
  * The items that fit what is typed, best first: by how they match, then one the person chose lately, a
157
182
  * preselected one, and the server's own order. `recency` is higher for a choice made more recently, and 0 for none.
158
183
  */
159
184
  export function rankCompletions(items, prefixOf, recency = () => 0, limit = 150) {
160
- const { compare } = labelCollator();
161
- return items
162
- .map((item, index) => {
163
- const prefix = prefixOf(item);
164
- return { item, index, prefix, score: matchScore(item.filterText ?? item.label, prefix), recent: recency(item) };
165
- })
166
- .filter((entry) => entry.score !== undefined)
167
- .sort((left, right) => left.score - right.score ||
168
- right.recent - left.recent ||
169
- Number(right.item.preselect === true) - Number(left.item.preselect === true) ||
170
- compare(left.item.sortText ?? left.item.label, right.item.sortText ?? right.item.label) ||
171
- left.index - right.index)
172
- .slice(0, limit)
173
- .map(({ item, score, prefix }) => ({ item, score, prefix }));
185
+ const {
186
+ compare
187
+ } = labelCollator();
188
+ return items.map((item, index) => {
189
+ const prefix = prefixOf(item);
190
+ return {
191
+ item,
192
+ index,
193
+ prefix,
194
+ score: matchScore(item.filterText ?? item.label, prefix),
195
+ recent: recency(item)
196
+ };
197
+ }).filter(entry => entry.score !== undefined).sort((left, right) => left.score - right.score || right.recent - left.recent || Number(right.item.preselect === true) - Number(left.item.preselect === true) || compare(left.item.sortText ?? left.item.label, right.item.sortText ?? right.item.label) || left.index - right.index).slice(0, limit).map(({
198
+ item,
199
+ score,
200
+ prefix
201
+ }) => ({
202
+ item,
203
+ score,
204
+ prefix
205
+ }));
174
206
  }
175
207
  /* The characters of a label to mark for what was typed; none when the label is not what the server filtered on. */
176
208
  export function matchedCharacters(label, prefix) {
177
- return matchDetail(label, prefix)?.positions ?? [];
209
+ return matchDetail(label, prefix)?.positions ?? [];
178
210
  }
179
211
  /* The first line of what a server says about where an item comes from, with a PHP `use` made into the namespace it names. */
180
212
  function sourceOf(item) {
181
- const detail = item.detail?.split('\n')[0]?.trim() ?? '';
182
- const imported = /^use\s+(.+)$/.exec(detail);
183
- return imported === null ? detail : imported[1].slice(0, Math.max(0, imported[1].lastIndexOf('\\')));
213
+ const detail = item.detail?.split('\n')[0]?.trim() ?? '';
214
+ const imported = /^use\s+(.+)$/.exec(detail);
215
+ return imported === null ? detail : imported[1].slice(0, Math.max(0, imported[1].lastIndexOf('\\')));
184
216
  }
185
217
  /* What stands right-aligned after a row: the module or namespace the item comes from, else what the server calls its type. */
186
218
  export function qualifierOf(item) {
187
- const description = item.labelDetails?.description?.trim() ?? '';
188
- return description !== '' ? description : sourceOf(item);
219
+ const description = item.labelDetails?.description?.trim() ?? '';
220
+ return description !== '' ? description : sourceOf(item);
189
221
  }
190
222
  /*
191
223
  * The qualifier of each row of a list. Items that share a label are told apart however it takes: by the
192
224
  * qualifier, or by what the server says besides, and by a number when a server says nothing that differs.
193
225
  */
194
226
  export function qualifiersOf(items) {
195
- const result = items.map(qualifierOf);
196
- const groups = new Map();
197
- items.forEach((item, index) => groups.set(item.label, [...(groups.get(item.label) ?? []), index]));
198
- for (const indexes of groups.values()) {
199
- if (indexes.length < 2 || new Set(indexes.map((index) => result[index])).size === indexes.length) {
200
- continue;
201
- }
202
- const alternate = indexes.map((index) => sourceOf(items[index]) || result[index]);
203
- const distinct = new Set(alternate).size === indexes.length && !alternate.includes('');
204
- indexes.forEach((index, position) => {
205
- result[index] = distinct ? alternate[position] : `${result[index] ?? ''} #${position + 1}`.trim();
206
- });
227
+ const result = items.map(qualifierOf);
228
+ const groups = new Map();
229
+ items.forEach((item, index) => groups.set(item.label, [...(groups.get(item.label) ?? []), index]));
230
+ for (const indexes of groups.values()) {
231
+ if (indexes.length < 2 || new Set(indexes.map(index => result[index])).size === indexes.length) {
232
+ continue;
207
233
  }
208
- return result;
234
+ const alternate = indexes.map(index => sourceOf(items[index]) || result[index]);
235
+ const distinct = new Set(alternate).size === indexes.length && !alternate.includes('');
236
+ indexes.forEach((index, position) => {
237
+ result[index] = distinct ? alternate[position] : `${result[index] ?? ''} #${position + 1}`.trim();
238
+ });
239
+ }
240
+ return result;
209
241
  }
210
242
  /* A snippet as the plain text it would insert: tab stops and variables vanish, placeholders and choices become their text, and the escapes of the format are undone. */
211
243
  export function snippetToText(snippet) {
212
- return parseSnippet(snippet).text;
244
+ return parseSnippet(snippet).text;
213
245
  }
214
246
  export const METHOD_KIND = 2;
215
247
  export const FUNCTION_KIND = 3;
@@ -217,80 +249,132 @@ export const CONSTRUCTOR_KIND = 4;
217
249
  export const CLASS_KIND = 7;
218
250
  export const SNIPPET_FORMAT = 2;
219
251
  export function isCallableKind(kind) {
220
- return kind === METHOD_KIND || kind === FUNCTION_KIND || kind === CONSTRUCTOR_KIND;
252
+ return kind === METHOD_KIND || kind === FUNCTION_KIND || kind === CONSTRUCTOR_KIND;
221
253
  }
222
254
  /* A call snippet with the call cut off, for a name that already has its parentheses: `log(${1:value})$0` becomes `log`. */
223
255
  function withoutCall(snippet) {
224
- const open = snippet.search(/(?<!\\)\(/);
225
- return open > 0 ? snippet.slice(0, open) : snippet;
256
+ const open = snippet.search(/(?<!\\)\(/);
257
+ return open > 0 ? snippet.slice(0, open) : snippet;
226
258
  }
227
259
  /*
228
260
  * What goes into the text for an item and the range it takes the place of, given the caret. `lineAfter` is
229
261
  * the rest of the line after the caret: an item that would add a call does not when a parenthesis follows.
230
262
  */
231
263
  export function insertionOf(item, caret, lineBefore, replace, lineAfter = '', options = {}) {
232
- const raw = item.textEdit?.newText ?? item.insertText ?? item.label;
233
- const edit = editRangeOf(item, replace);
234
- const range = edit === null
235
- ? { start: { line: caret.line, character: caret.character - identifierPrefix(lineBefore).length }, end: caret }
236
- : // The word grew since the answer came, so what was typed since belongs to what is replaced.
237
- { start: edit.start, end: comparePositions(edit.end, caret) < 0 && edit.end.line === caret.line ? caret : edit.end };
238
- if (item.insertTextFormat !== SNIPPET_FORMAT) {
239
- const adjusted = item.insertTextMode === 2
240
- ? indentSnippet({ text: raw, stops: [] }, options.indentationBefore ?? lineBefore.slice(0, range.start.character))
241
- : { text: raw, stops: [] };
242
- return { range, text: adjusted.text, stops: [], ...(adjusted.limited ? { limited: true } : {}) };
243
- }
244
- const followed = range.end.line === caret.line && lineAfter.slice(Math.max(0, range.end.character - caret.character)).startsWith('(');
245
- const source = followed && isCallableKind(item.kind) ? withoutCall(raw) : raw;
246
- const template = options.template?.source === source ? options.template : compileSnippet(source);
247
- const rendered = template.render(options.resolve, options.variableTransforms);
248
- const parsed = item.insertTextMode === 2 ? indentSnippet(rendered, options.indentationBefore ?? lineBefore.slice(0, range.start.character)) : rendered;
264
+ const raw = item.textEdit?.newText ?? item.insertText ?? item.label;
265
+ const edit = editRangeOf(item, replace);
266
+ const range = edit === null ? {
267
+ start: {
268
+ line: caret.line,
269
+ character: caret.character - identifierPrefix(lineBefore).length
270
+ },
271
+ end: caret
272
+ } :
273
+ // The word grew since the answer came, so what was typed since belongs to what is replaced.
274
+ {
275
+ start: edit.start,
276
+ end: comparePositions(edit.end, caret) < 0 && edit.end.line === caret.line ? caret : edit.end
277
+ };
278
+ if (item.insertTextFormat !== SNIPPET_FORMAT) {
279
+ const adjusted = item.insertTextMode === 2 ? indentSnippet({
280
+ text: raw,
281
+ stops: []
282
+ }, options.indentationBefore ?? lineBefore.slice(0, range.start.character)) : {
283
+ text: raw,
284
+ stops: []
285
+ };
249
286
  return {
250
- range,
251
- text: parsed.text,
252
- stops: tabOrder(parsed),
253
- fields: parsed.fields,
254
- ...(options.variableTransforms !== undefined || rendered.transforms === undefined
255
- ? {}
256
- : {
257
- variableTransforms: rendered.transforms.map((range) => ({ ...range.transform, value: rendered.text.slice(range.start, range.end) }))
258
- }),
259
- ...(parsed.limited ? { limited: true } : {})
287
+ range,
288
+ text: adjusted.text,
289
+ stops: [],
290
+ ...(adjusted.limited ? {
291
+ limited: true
292
+ } : {})
260
293
  };
294
+ }
295
+ const followed = range.end.line === caret.line && lineAfter.slice(Math.max(0, range.end.character - caret.character)).startsWith('(');
296
+ const source = followed && isCallableKind(item.kind) ? withoutCall(raw) : raw;
297
+ const template = options.template?.source === source ? options.template : compileSnippet(source);
298
+ const rendered = template.render(options.resolve, options.variableTransforms);
299
+ const parsed = item.insertTextMode === 2 ? indentSnippet(rendered, options.indentationBefore ?? lineBefore.slice(0, range.start.character)) : rendered;
300
+ return {
301
+ range,
302
+ text: parsed.text,
303
+ stops: tabOrder(parsed),
304
+ fields: parsed.fields,
305
+ ...(options.variableTransforms !== undefined || rendered.transforms === undefined ? {} : {
306
+ variableTransforms: rendered.transforms.map(range => ({
307
+ ...range.transform,
308
+ value: rendered.text.slice(range.start, range.end)
309
+ }))
310
+ }),
311
+ ...(parsed.limited ? {
312
+ limited: true
313
+ } : {})
314
+ };
261
315
  }
262
316
  /*
263
317
  * The same insertion at every other caret that has the word the primary caret has before it, so a name typed at
264
318
  * several carets is completed at all of them. A caret that is inside a selection, or after other text, is left alone.
265
319
  */
266
320
  export function mirroredInsertions(carets, primary, caret, text, insertion, includeSelections = false) {
267
- const before = insertion.range.start.line === caret.line ? caret.character - insertion.range.start.character : -1;
268
- const after = insertion.range.end.line === caret.line ? insertion.range.end.character - caret.character : -1;
269
- if (before < 0 || after < 0) {
270
- return [];
321
+ const before = insertion.range.start.line === caret.line ? caret.character - insertion.range.start.character : -1;
322
+ const after = insertion.range.end.line === caret.line ? insertion.range.end.character - caret.character : -1;
323
+ if (before < 0 || after < 0) {
324
+ return [];
325
+ }
326
+ const typed = text({
327
+ start: insertion.range.start,
328
+ end: caret
329
+ });
330
+ const replaced = text({
331
+ start: caret,
332
+ end: insertion.range.end
333
+ });
334
+ return carets.flatMap(other => {
335
+ const at = includeSelections ? other.end : other.start;
336
+ if (comparePositions(other.start, primary.start) === 0 && comparePositions(other.end, primary.end) === 0) {
337
+ return [];
271
338
  }
272
- const typed = text({ start: insertion.range.start, end: caret });
273
- const replaced = text({ start: caret, end: insertion.range.end });
274
- return carets.flatMap((other) => {
275
- const at = includeSelections ? other.end : other.start;
276
- if (comparePositions(other.start, primary.start) === 0 && comparePositions(other.end, primary.end) === 0) {
277
- return [];
278
- }
279
- if ((!includeSelections && comparePositions(other.start, other.end) !== 0) || at.character < before) {
280
- return [];
281
- }
282
- const range = { start: { line: at.line, character: at.character - before }, end: { line: at.line, character: at.character + after } };
283
- const preceding = range.start.character > 0 ? text({ start: { line: at.line, character: range.start.character - 1 }, end: range.start }) : '';
284
- const startsWord = preceding === '' || !isIdentifierCharacter(preceding);
285
- const matches = startsWord && text({ start: range.start, end: at }) === typed && text({ start: at, end: range.end }) === replaced;
286
- return matches ? [{ range, text: insertion.text }] : [];
287
- });
339
+ if (!includeSelections && comparePositions(other.start, other.end) !== 0 || at.character < before) {
340
+ return [];
341
+ }
342
+ const range = {
343
+ start: {
344
+ line: at.line,
345
+ character: at.character - before
346
+ },
347
+ end: {
348
+ line: at.line,
349
+ character: at.character + after
350
+ }
351
+ };
352
+ const preceding = range.start.character > 0 ? text({
353
+ start: {
354
+ line: at.line,
355
+ character: range.start.character - 1
356
+ },
357
+ end: range.start
358
+ }) : '';
359
+ const startsWord = preceding === '' || !isIdentifierCharacter(preceding);
360
+ const matches = startsWord && text({
361
+ start: range.start,
362
+ end: at
363
+ }) === typed && text({
364
+ start: at,
365
+ end: range.end
366
+ }) === replaced;
367
+ return matches ? [{
368
+ range,
369
+ text: insertion.text
370
+ }] : [];
371
+ });
288
372
  }
289
373
  export function documentationText(documentation) {
290
- if (documentation === undefined) {
291
- return '';
292
- }
293
- return typeof documentation === 'string' ? documentation : documentation.kind === 'plaintext' ? escapeMarkdown(documentation.value) : documentation.value;
374
+ if (documentation === undefined) {
375
+ return '';
376
+ }
377
+ return typeof documentation === 'string' ? documentation : documentation.kind === 'plaintext' ? escapeMarkdown(documentation.value) : documentation.value;
294
378
  }
295
379
  /* A detail line that only says where an item comes from: a PHP `use` or an auto import. */
296
380
  export const SOURCE_LINE = /^(?:use\s|Auto import from\b)/;
@@ -300,75 +384,81 @@ export const SOURCE_LINE = /^(?:use\s|Auto import from\b)/;
300
384
  * kept apart, since it is no code.
301
385
  */
302
386
  export function completionDocsOf(item, highlightLanguage) {
303
- const lines = (item.detail ?? '').trim().split('\n');
304
- const hasSource = SOURCE_LINE.test(lines[0] ?? '');
305
- const source = hasSource ? lines[0].trim() : '';
306
- const code = (hasSource ? lines.slice(1) : lines).join('\n').trim();
307
- const parsed = splitSignatures(documentationText(item.documentation));
308
- const text = {
309
- signatures: code === '' ? parsed.signatures : [{ language: highlightLanguage, code }, ...parsed.signatures],
310
- markdown: parsed.markdown
311
- };
312
- return source === '' && isEmptyHover(text) ? null : { source, text };
387
+ const lines = (item.detail ?? '').trim().split('\n');
388
+ const hasSource = SOURCE_LINE.test(lines[0] ?? '');
389
+ const source = hasSource ? lines[0].trim() : '';
390
+ const code = (hasSource ? lines.slice(1) : lines).join('\n').trim();
391
+ const parsed = splitSignatures(documentationText(item.documentation));
392
+ const text = {
393
+ signatures: code === '' ? parsed.signatures : [{
394
+ language: highlightLanguage,
395
+ code
396
+ }, ...parsed.signatures],
397
+ markdown: parsed.markdown
398
+ };
399
+ return source === '' && isEmptyHover(text) ? null : {
400
+ source,
401
+ text
402
+ };
313
403
  }
314
404
  const KIND_LETTERS = {
315
- 1: 't',
316
- 2: 'm',
317
- 3: 'f',
318
- 4: 'c',
319
- 5: 'p',
320
- 6: 'v',
321
- 7: 'c',
322
- 8: 'i',
323
- 9: 'm',
324
- 10: 'p',
325
- 11: 'u',
326
- 12: 'v',
327
- 13: 'e',
328
- 14: 'k',
329
- 15: 's',
330
- 16: 'c',
331
- 17: 'f',
332
- 18: 'r',
333
- 19: 'f',
334
- 20: 'e',
335
- 21: 'k',
336
- 22: 's',
337
- 23: 'e',
338
- 24: 'o',
339
- 25: 't'
405
+ 1: 't',
406
+ 2: 'm',
407
+ 3: 'f',
408
+ 4: 'c',
409
+ 5: 'p',
410
+ 6: 'v',
411
+ 7: 'c',
412
+ 8: 'i',
413
+ 9: 'm',
414
+ 10: 'p',
415
+ 11: 'u',
416
+ 12: 'v',
417
+ 13: 'e',
418
+ 14: 'k',
419
+ 15: 's',
420
+ 16: 'c',
421
+ 17: 'f',
422
+ 18: 'r',
423
+ 19: 'f',
424
+ 20: 'e',
425
+ 21: 'k',
426
+ 22: 's',
427
+ 23: 'e',
428
+ 24: 'o',
429
+ 25: 't'
340
430
  };
341
431
  const KIND_TONES = {
342
- 2: 'callable',
343
- 3: 'callable',
344
- 4: 'callable',
345
- 5: 'value',
346
- 6: 'value',
347
- 10: 'value',
348
- 12: 'value',
349
- 20: 'value',
350
- 21: 'value',
351
- 7: 'type',
352
- 8: 'type',
353
- 9: 'type',
354
- 13: 'type',
355
- 22: 'type',
356
- 25: 'type'
432
+ 2: 'callable',
433
+ 3: 'callable',
434
+ 4: 'callable',
435
+ 5: 'value',
436
+ 6: 'value',
437
+ 10: 'value',
438
+ 12: 'value',
439
+ 20: 'value',
440
+ 21: 'value',
441
+ 7: 'type',
442
+ 8: 'type',
443
+ 9: 'type',
444
+ 13: 'type',
445
+ 22: 'type',
446
+ 25: 'type'
357
447
  };
358
448
  export function kindLetterOf(kind) {
359
- return kind === undefined ? '·' : (KIND_LETTERS[kind] ?? '·');
449
+ return kind === undefined ? '·' : KIND_LETTERS[kind] ?? '·';
360
450
  }
361
451
  export function kindToneOf(kind) {
362
- return kind === undefined ? 'other' : (KIND_TONES[kind] ?? 'other');
452
+ return kind === undefined ? 'other' : KIND_TONES[kind] ?? 'other';
363
453
  }
364
454
  /*
365
455
  * The rows of a list worth drawing: the ones in view, a few on each side, and the active one with the
366
456
  * same margin, so the rows between never leave a gap. The rest is only space.
367
457
  */
368
458
  export function rowWindow(scrollTop, active, total, rowHeight, visible, margin, padding) {
369
- const shown = Math.floor(Math.max(0, scrollTop - padding) / rowHeight);
370
- return {
371
- first: Math.max(0, Math.min(shown, active) - margin),
372
- last: Math.min(total, Math.max(shown + visible + 1, active + 1) + margin)
373
- };
374
- }
459
+ const shown = Math.floor(Math.max(0, scrollTop - padding) / rowHeight);
460
+ return {
461
+ first: Math.max(0, Math.min(shown, active) - margin),
462
+ last: Math.min(total, Math.max(shown + visible + 1, active + 1) + margin)
463
+ };
464
+ }