@adecore/editor-react 0.31.12 → 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
@@ -5,61 +5,75 @@ const FENCE = /^```([\w+#.-]*)[^\n]*\n([\s\S]*?)\n```[ \t]*(?:\n|$)/;
5
5
  * in the documentation, stays where it is.
6
6
  */
7
7
  export function splitSignatures(markdown) {
8
- const signatures = [];
9
- let rest = markdown.replace(/^\s+/, '');
10
- for (let match = FENCE.exec(rest); match !== null; match = FENCE.exec(rest)) {
11
- signatures.push({ language: match[1] || 'text', code: match[2] });
12
- rest = rest.slice(match[0].length).replace(/^\s+/, '');
13
- }
14
- return { signatures, markdown: rest.replace(/^(?:-{3,}|\*{3,})\s*(?:\n|$)/, '').trim() };
8
+ const signatures = [];
9
+ let rest = markdown.replace(/^\s+/, '');
10
+ for (let match = FENCE.exec(rest); match !== null; match = FENCE.exec(rest)) {
11
+ signatures.push({
12
+ language: match[1] || 'text',
13
+ code: match[2]
14
+ });
15
+ rest = rest.slice(match[0].length).replace(/^\s+/, '');
16
+ }
17
+ return {
18
+ signatures,
19
+ markdown: rest.replace(/^(?:-{3,}|\*{3,})\s*(?:\n|$)/, '').trim()
20
+ };
15
21
  }
16
22
  function markedStringText(content) {
17
- if (typeof content !== 'string') {
18
- return { signatures: [{ language: content.language || 'text', code: content.value }], markdown: '' };
19
- }
20
- return splitSignatures(content);
23
+ if (typeof content !== 'string') {
24
+ return {
25
+ signatures: [{
26
+ language: content.language || 'text',
27
+ code: content.value
28
+ }],
29
+ markdown: ''
30
+ };
31
+ }
32
+ return splitSignatures(content);
21
33
  }
22
34
  /* The text of a hover, whichever of the three shapes the protocol allows it in. */
23
35
  export function hoverTextOf(hover) {
24
- const { contents } = hover;
25
- if (Array.isArray(contents)) {
26
- const parts = contents.map(markedStringText);
27
- return {
28
- signatures: parts.flatMap((part) => part.signatures),
29
- markdown: parts
30
- .map((part) => part.markdown)
31
- .filter(Boolean)
32
- .join('\n\n')
33
- };
34
- }
35
- if (typeof contents === 'string' || 'language' in contents) {
36
- return markedStringText(contents);
37
- }
38
- return contents.kind === 'plaintext' ? { signatures: [], markdown: escapeMarkdown(contents.value).trim() } : splitSignatures(contents.value);
36
+ const {
37
+ contents
38
+ } = hover;
39
+ if (Array.isArray(contents)) {
40
+ const parts = contents.map(markedStringText);
41
+ return {
42
+ signatures: parts.flatMap(part => part.signatures),
43
+ markdown: parts.map(part => part.markdown).filter(Boolean).join('\n\n')
44
+ };
45
+ }
46
+ if (typeof contents === 'string' || 'language' in contents) {
47
+ return markedStringText(contents);
48
+ }
49
+ return contents.kind === 'plaintext' ? {
50
+ signatures: [],
51
+ markdown: escapeMarkdown(contents.value).trim()
52
+ } : splitSignatures(contents.value);
39
53
  }
40
54
  /* Plain text as markdown that reads the same, its markdown characters escaped. */
41
55
  export function escapeMarkdown(text) {
42
- return text.replace(/([\\`*_{}[\]()#+\-.!|<>~])/g, '\\$1');
56
+ return text.replace(/([\\`*_{}[\]()#+\-.!|<>~])/g, '\\$1');
43
57
  }
44
58
  export function isEmptyHover(text) {
45
- return text.signatures.length === 0 && text.markdown === '';
59
+ return text.signatures.length === 0 && text.markdown === '';
46
60
  }
47
61
  function isLink(entry) {
48
- return 'targetUri' in entry;
62
+ return 'targetUri' in entry;
49
63
  }
50
64
  /* Where a navigation result leads, as plain locations: the part of a link that names the symbol is the one to land on. */
51
65
  export function locationsOf(result) {
52
- if (result === null) {
53
- return [];
54
- }
55
- const list = Array.isArray(result) ? result : [result];
56
- return list.map((entry) => (isLink(entry) ? { uri: entry.targetUri, range: entry.targetSelectionRange } : entry));
66
+ if (result === null) {
67
+ return [];
68
+ }
69
+ const list = Array.isArray(result) ? result : [result];
70
+ return list.map(entry => isLink(entry) ? {
71
+ uri: entry.targetUri,
72
+ range: entry.targetSelectionRange
73
+ } : entry);
57
74
  }
58
75
  export function sameRange(left, right) {
59
- return (left.start.line === right.start.line &&
60
- left.start.character === right.start.character &&
61
- left.end.line === right.end.line &&
62
- left.end.character === right.end.character);
76
+ return left.start.line === right.start.line && left.start.character === right.start.character && left.end.line === right.end.line && left.end.character === right.end.character;
63
77
  }
64
78
  const TAG_LINE = /^(?:_@([\w-]+)_|\*@([\w-]+)\*|@([\w-]+))(?:\s+|$)([\s\S]*)$/;
65
79
  /* Tags whose text is a block of its own, which a row of the tag list has no room for. */
@@ -70,59 +84,72 @@ const BLOCK_TAGS = new Set(['example']);
70
84
  * `@` stays prose.
71
85
  */
72
86
  export function splitDocTags(markdown) {
73
- const tags = [];
74
- const kept = [];
75
- for (const paragraph of markdown.split(/\n{2,}/)) {
76
- const lines = paragraph.split('\n');
77
- const matches = lines.map((line) => TAG_LINE.exec(line.trim()));
78
- const names = matches.map((match) => (match === null ? null : (match[1] ?? match[2] ?? match[3])));
79
- if (paragraph.trim() === '' || names.some((name) => name === null || BLOCK_TAGS.has(name))) {
80
- kept.push(paragraph);
81
- continue;
82
- }
83
- matches.forEach((match, index) => {
84
- // The TypeScript server puts a dash between a tag, its parameter and its text.
85
- const text = match[4].replace(/^[\u2014\u2013-]\s*/, '').replace(/^(`[^`]+`)\s+[\u2014\u2013-]\s+/, '$1 ');
86
- tags.push({ name: names[index], markdown: text.trim() });
87
- });
87
+ const tags = [];
88
+ const kept = [];
89
+ for (const paragraph of markdown.split(/\n{2,}/)) {
90
+ const lines = paragraph.split('\n');
91
+ const matches = lines.map(line => TAG_LINE.exec(line.trim()));
92
+ const names = matches.map(match => match === null ? null : match[1] ?? match[2] ?? match[3]);
93
+ if (paragraph.trim() === '' || names.some(name => name === null || BLOCK_TAGS.has(name))) {
94
+ kept.push(paragraph);
95
+ continue;
88
96
  }
89
- // By name, and in the order written within one name, so parameters keep the order of the signature.
90
- return { markdown: kept.join('\n\n').trim(), tags: tags.toSorted((left, right) => left.name.localeCompare(right.name)) };
97
+ matches.forEach((match, index) => {
98
+ // The TypeScript server puts a dash between a tag, its parameter and its text.
99
+ const text = match[4].replace(/^[\u2014\u2013-]\s*/, '').replace(/^(`[^`]+`)\s+[\u2014\u2013-]\s+/, '$1 ');
100
+ tags.push({
101
+ name: names[index],
102
+ markdown: text.trim()
103
+ });
104
+ });
105
+ }
106
+ // By name, and in the order written within one name, so parameters keep the order of the signature.
107
+ return {
108
+ markdown: kept.join('\n\n').trim(),
109
+ tags: tags.toSorted((left, right) => left.name.localeCompare(right.name))
110
+ };
91
111
  }
92
112
  /* A CSS server draws the status as a data URL image in front of an italic sentence, and a data URL is not an address the markdown renderer lets through. */
93
113
  const BASELINE = /!\[Baseline icon\]\([^)\s]*\)[ \t]*_([^\n]+?)_[ \t]*(?:\n|$)/;
94
114
  /* The Baseline status a text carries, which the card draws with a mark of its own, and the text without it. */
95
115
  function takeBaseline(markdown) {
96
- const match = BASELINE.exec(markdown);
97
- if (match === null) {
98
- return { baseline: null, rest: markdown };
99
- }
100
- const text = match[1].trim();
101
- const level = text.startsWith('Widely') ? 'widely' : text.startsWith('Newly') ? 'newly' : 'limited';
102
- return { baseline: { level, text }, rest: (markdown.slice(0, match.index) + markdown.slice(match.index + match[0].length)).replace(/\n{3,}/g, '\n\n') };
116
+ const match = BASELINE.exec(markdown);
117
+ if (match === null) {
118
+ return {
119
+ baseline: null,
120
+ rest: markdown
121
+ };
122
+ }
123
+ const text = match[1].trim();
124
+ const level = text.startsWith('Widely') ? 'widely' : text.startsWith('Newly') ? 'newly' : 'limited';
125
+ return {
126
+ baseline: {
127
+ level,
128
+ text
129
+ },
130
+ rest: (markdown.slice(0, match.index) + markdown.slice(match.index + match[0].length)).replace(/\n{3,}/g, '\n\n')
131
+ };
103
132
  }
104
133
  const RULE = /^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/m;
105
134
  const TITLE = /^(?:__(.+)__|\*\*(.+)\*\*)\s*(?:\n|$)/;
106
135
  const ANY_FENCE = /```([\w+#.-]*)[^\n]*\n([\s\S]*?)\n```[ \t]*(?:\n|$)/;
107
136
  /* The PHP server opens every snippet with a tag the reader does not need to see. */
108
137
  function withoutOpenTag(block) {
109
- return { language: block.language, code: block.code.replace(/^<\?php\s*\n/, '') };
138
+ return {
139
+ language: block.language,
140
+ code: block.code.replace(/^<\?php\s*\n/, '')
141
+ };
110
142
  }
111
143
  const HTML_CODE = /<(code|pre)>([\s\S]*?)<\/\1>/g;
112
144
  const INLINE_TAG = /\{@(see|link|linkplain)\s+([^}\s]+)(?:\s+([^}]+?))?\s*\}/g;
113
145
  function decodeEntities(text) {
114
- return text
115
- .replace(/&lt;/g, '<')
116
- .replace(/&gt;/g, '>')
117
- .replace(/&quot;/g, '"')
118
- .replace(/&#0?39;/g, "'")
119
- .replace(/&amp;/g, '&');
146
+ return text.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/&amp;/g, '&');
120
147
  }
121
148
  function dedent(text) {
122
- const lines = text.replace(/^\n+|\s+$/g, '').split('\n');
123
- const indents = lines.filter((line) => line.trim() !== '').map((line) => line.length - line.trimStart().length);
124
- const common = indents.length === 0 ? 0 : Math.min(...indents);
125
- return lines.map((line) => line.slice(common)).join('\n');
149
+ const lines = text.replace(/^\n+|\s+$/g, '').split('\n');
150
+ const indents = lines.filter(line => line.trim() !== '').map(line => line.length - line.trimStart().length);
151
+ const common = indents.length === 0 ? 0 : Math.min(...indents);
152
+ return lines.map(line => line.slice(common)).join('\n');
126
153
  }
127
154
  /*
128
155
  * What a docblock writes for a reader of its source, written for one of a hover: a `<code>` or `<pre>`
@@ -130,70 +157,81 @@ function dedent(text) {
130
157
  * the name in code. Servers pass both on as they are, and markdown would show the tags as text.
131
158
  */
132
159
  export function docblockMarkdown(markdown, language) {
133
- return markdown
134
- .replace(HTML_CODE, (_match, _tag, body) => {
135
- const code = decodeEntities(body);
136
- return code.includes('\n') ? `\n\n\`\`\`${language}\n${dedent(code)}\n\`\`\`\n\n` : `\`${code.trim()}\``;
137
- })
138
- .replace(INLINE_TAG, (_match, _tag, target, label) => (label === undefined ? `\`${target}\`` : label))
139
- .replace(/\n{3,}/g, '\n\n')
140
- .trim();
160
+ return markdown.replace(HTML_CODE, (_match, _tag, body) => {
161
+ const code = decodeEntities(body);
162
+ return code.includes('\n') ? `\n\n\`\`\`${language}\n${dedent(code)}\n\`\`\`\n\n` : `\`${code.trim()}\``;
163
+ }).replace(INLINE_TAG, (_match, _tag, target, label) => label === undefined ? `\`${target}\`` : label).replace(/\n{3,}/g, '\n\n').trim();
141
164
  }
142
165
  export function markdownParts(markdown) {
143
- const parts = [];
144
- let rest = markdown;
145
- for (let match = ANY_FENCE.exec(rest); match !== null; match = ANY_FENCE.exec(rest)) {
146
- const prose = rest.slice(0, match.index).trim();
147
- if (prose !== '') {
148
- parts.push({ kind: 'prose', text: prose });
149
- }
150
- parts.push({ kind: 'code', block: { language: match[1] || 'text', code: match[2] } });
151
- rest = rest.slice(match.index + match[0].length);
152
- }
153
- if (rest.trim() !== '') {
154
- parts.push({ kind: 'prose', text: rest.trim() });
166
+ const parts = [];
167
+ let rest = markdown;
168
+ for (let match = ANY_FENCE.exec(rest); match !== null; match = ANY_FENCE.exec(rest)) {
169
+ const prose = rest.slice(0, match.index).trim();
170
+ if (prose !== '') {
171
+ parts.push({
172
+ kind: 'prose',
173
+ text: prose
174
+ });
155
175
  }
156
- return parts;
176
+ parts.push({
177
+ kind: 'code',
178
+ block: {
179
+ language: match[1] || 'text',
180
+ code: match[2]
181
+ }
182
+ });
183
+ rest = rest.slice(match.index + match[0].length);
184
+ }
185
+ if (rest.trim() !== '') {
186
+ parts.push({
187
+ kind: 'prose',
188
+ text: rest.trim()
189
+ });
190
+ }
191
+ return parts;
157
192
  }
158
193
  /* The title is drawn as text, so the escapes that kept it from reading as markdown come off: `A\\B::\_\_construct` is `A\B::__construct`. */
159
194
  function unescapeMarkdown(text) {
160
- return text.replace(/\\([\\`*_{}[\]()#+\-.!|<>~])/g, '$1');
195
+ return text.replace(/\\([\\`*_{}[\]()#+\-.!|<>~])/g, '$1');
161
196
  }
162
197
  function sectionOf(chunk, leading) {
163
- let rest = chunk.trim();
164
- const title = TITLE.exec(rest);
165
- if (title !== null) {
166
- rest = rest.slice(title[0].length).trim();
167
- }
168
- const signatures = [...leading];
169
- // A section's own snippet is its first block; one further down after the prose, such as an example, stays prose.
170
- const fence = ANY_FENCE.exec(rest);
171
- if (fence !== null && !/@example/.test(rest.slice(0, fence.index))) {
172
- signatures.push({ language: fence[1] || 'text', code: fence[2] });
173
- rest = (rest.slice(0, fence.index) + rest.slice(fence.index + fence[0].length)).trim();
174
- }
175
- const taken = takeBaseline(rest);
176
- const doc = splitDocTags(docblockMarkdown(taken.rest, signatures[0]?.language ?? 'text'));
177
- return {
178
- title: title === null ? null : unescapeMarkdown((title[1] ?? title[2]).trim()),
179
- signatures: signatures.map(withoutOpenTag),
180
- markdown: doc.markdown,
181
- tags: doc.tags,
182
- baseline: taken.baseline
183
- };
198
+ let rest = chunk.trim();
199
+ const title = TITLE.exec(rest);
200
+ if (title !== null) {
201
+ rest = rest.slice(title[0].length).trim();
202
+ }
203
+ const signatures = [...leading];
204
+ // A section's own snippet is its first block; one further down after the prose, such as an example, stays prose.
205
+ const fence = ANY_FENCE.exec(rest);
206
+ if (fence !== null && !/@example/.test(rest.slice(0, fence.index))) {
207
+ signatures.push({
208
+ language: fence[1] || 'text',
209
+ code: fence[2]
210
+ });
211
+ rest = (rest.slice(0, fence.index) + rest.slice(fence.index + fence[0].length)).trim();
212
+ }
213
+ const taken = takeBaseline(rest);
214
+ const doc = splitDocTags(docblockMarkdown(taken.rest, signatures[0]?.language ?? 'text'));
215
+ return {
216
+ title: title === null ? null : unescapeMarkdown((title[1] ?? title[2]).trim()),
217
+ signatures: signatures.map(withoutOpenTag),
218
+ markdown: doc.markdown,
219
+ tags: doc.tags,
220
+ baseline: taken.baseline
221
+ };
184
222
  }
185
223
  /* The symbols of a hover apart, split where a server draws a rule between them, each described once. */
186
224
  export function hoverSectionsOf(text) {
187
- const chunks = text.markdown === '' ? [''] : text.markdown.split(RULE);
188
- const sections = chunks.map((chunk, index) => sectionOf(chunk, index === 0 ? text.signatures : []));
189
- const seen = new Set();
190
- return sections.filter((section) => {
191
- const empty = section.title === null && section.signatures.length === 0 && section.markdown === '' && section.tags.length === 0 && section.baseline === null;
192
- const key = JSON.stringify([section.title, section.signatures, section.markdown, section.baseline]);
193
- if (empty || seen.has(key)) {
194
- return false;
195
- }
196
- seen.add(key);
197
- return true;
198
- });
199
- }
225
+ const chunks = text.markdown === '' ? [''] : text.markdown.split(RULE);
226
+ const sections = chunks.map((chunk, index) => sectionOf(chunk, index === 0 ? text.signatures : []));
227
+ const seen = new Set();
228
+ return sections.filter(section => {
229
+ const empty = section.title === null && section.signatures.length === 0 && section.markdown === '' && section.tags.length === 0 && section.baseline === null;
230
+ const key = JSON.stringify([section.title, section.signatures, section.markdown, section.baseline]);
231
+ if (empty || seen.has(key)) {
232
+ return false;
233
+ }
234
+ seen.add(key);
235
+ return true;
236
+ });
237
+ }