@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
@@ -1,5 +1,5 @@
1
1
  import { disposeSnippetTransforms } from './snippet-transform-queue.js';
2
- import i18next, {} from 'i18next';
2
+ import i18next from 'i18next';
3
3
  import { fileUriToPath, pathToFileUri } from '@adecore/lsp';
4
4
  import { NavigationHistory } from './navigation-history.js';
5
5
  import { ProjectProblems, serviceProblems } from './project-problems.js';
@@ -8,236 +8,253 @@ import { relativeTo } from './paths.js';
8
8
  import { PANEL_BODY_HEIGHT } from './panel-size.js';
9
9
  /* Sends one editor's edits to the document, holding them until the document is open. */
10
10
  class DocumentSync {
11
- ready;
12
- editor;
13
- service;
14
- uri;
15
- stop;
16
- pending = [];
17
- opened = false;
18
- disposed = false;
19
- constructor(service, uri, languageId, editor) {
20
- this.service = service;
21
- this.uri = uri;
22
- this.editor = editor;
23
- this.stop = editor.onTextChange((change) => this.changed(change.changes));
24
- this.ready = service.openDocument({ uri, languageId, text: editor.getText() }).then(() => {
25
- this.opened = true;
26
- if (!this.disposed) {
27
- this.flush();
28
- }
29
- });
30
- // A failed open is also a document nothing can be asked of; the features' own requests say so.
31
- this.ready.catch(() => undefined);
32
- }
33
- get text() {
34
- return this.editor.getText();
35
- }
36
- dispose() {
37
- this.disposed = true;
38
- this.pending = [];
39
- this.stop();
40
- }
41
- changed(changes) {
42
- this.pending.push(...changes);
43
- if (this.opened) {
44
- this.flush();
45
- }
11
+ ready;
12
+ editor;
13
+ service;
14
+ uri;
15
+ stop;
16
+ pending = [];
17
+ opened = false;
18
+ disposed = false;
19
+ constructor(service, uri, languageId, editor) {
20
+ this.service = service;
21
+ this.uri = uri;
22
+ this.editor = editor;
23
+ this.stop = editor.onTextChange(change => this.changed(change.changes));
24
+ this.ready = service.openDocument({
25
+ uri,
26
+ languageId,
27
+ text: editor.getText()
28
+ }).then(() => {
29
+ this.opened = true;
30
+ if (!this.disposed) {
31
+ this.flush();
32
+ }
33
+ });
34
+ // A failed open is also a document nothing can be asked of; the features' own requests say so.
35
+ this.ready.catch(() => undefined);
36
+ }
37
+ get text() {
38
+ return this.editor.getText();
39
+ }
40
+ dispose() {
41
+ this.disposed = true;
42
+ this.pending = [];
43
+ this.stop();
44
+ }
45
+ changed(changes) {
46
+ this.pending.push(...changes);
47
+ if (this.opened) {
48
+ this.flush();
46
49
  }
47
- flush() {
48
- if (this.pending.length === 0) {
49
- return;
50
- }
51
- const changes = this.pending;
52
- this.pending = [];
53
- void this.service.changeDocument(this.uri, changes).catch(() => undefined);
50
+ }
51
+ flush() {
52
+ if (this.pending.length === 0) {
53
+ return;
54
54
  }
55
+ const changes = this.pending;
56
+ this.pending = [];
57
+ void this.service.changeDocument(this.uri, changes).catch(() => undefined);
58
+ }
55
59
  }
56
60
  function filesOf(targets) {
57
- return targets.map((target) => {
58
- const { path, directory } = typeof target === 'string' ? { path: target, directory: false } : { path: target.path, directory: target.directory === true };
59
- return { uri: pathToFileUri(path), directory };
60
- });
61
+ return targets.map(target => {
62
+ const {
63
+ path,
64
+ directory
65
+ } = typeof target === 'string' ? {
66
+ path: target,
67
+ directory: false
68
+ } : {
69
+ path: target.path,
70
+ directory: target.directory === true
71
+ };
72
+ return {
73
+ uri: pathToFileUri(path),
74
+ directory
75
+ };
76
+ });
61
77
  }
62
78
  /*
63
79
  * A shared language service and document ownership for the editors of a project. Two editors on the same
64
80
  * file hold one document, and only the first one sends changes, since both would report the same edit.
65
81
  */
66
82
  export class ProjectLanguage {
67
- service;
68
- host;
69
- i18n;
70
- folder;
71
- problems;
72
- history = new NavigationHistory();
73
- holders = new Map();
74
- files;
75
- /* The body height of the peek and call hierarchy panels, shared by the editors of the project. */
76
- panelBodyHeight;
77
- panelListeners = new Set();
78
- pendingCaret = null;
79
- lastPlace = null;
80
- disposed = false;
81
- constructor(service, host = {}) {
82
- this.service = service;
83
- this.host = host;
84
- this.i18n = host.i18n ?? i18next;
85
- this.folder = host.folder ?? '';
86
- this.files = host.files ?? null;
87
- this.panelBodyHeight = host.panelBodyHeight ?? PANEL_BODY_HEIGHT;
88
- this.problems = new ProjectProblems(host.problemSource ?? serviceProblems(service, (uri) => this.pathOfLocation(uri) ?? uri));
89
- }
90
- /* Open panels of other editors of the project follow a resize. */
91
- onPanelBodyChange(listener) {
92
- this.panelListeners.add(listener);
93
- return () => this.panelListeners.delete(listener);
94
- }
95
- resizePanelBody(height) {
96
- if (height === this.panelBodyHeight) {
97
- return;
98
- }
99
- this.panelBodyHeight = height;
100
- this.panelListeners.forEach((listener) => listener(height));
101
- this.host.onPanelResize?.(height);
102
- }
103
- pathOfLocation(uri) {
104
- if (this.host.pathOfUri !== undefined) {
105
- return this.host.pathOfUri(uri);
106
- }
107
- const path = fileUriToPath(uri);
108
- return path !== null && this.folder !== '' ? relativeTo(path, this.folder) : path;
109
- }
110
- /* Makes an edit of a language server: an open document takes it as one undo step and any other file gets an unsaved draft. */
111
- applyWorkspaceEdit(edit) {
112
- const { service } = this;
113
- return applyWorkspaceEdit(edit, {
114
- editorOf: (uri) => this.holders.get(uri)?.editors[0],
115
- files: this.files,
116
- ...(service.versionOf === undefined ? {} : { versionOf: (uri) => service.versionOf(uri) })
117
- });
83
+ service;
84
+ host;
85
+ i18n;
86
+ folder;
87
+ problems;
88
+ history = new NavigationHistory();
89
+ holders = new Map();
90
+ files;
91
+ /* The body height of the peek and call hierarchy panels, shared by the editors of the project. */
92
+ panelBodyHeight;
93
+ panelListeners = new Set();
94
+ pendingCaret = null;
95
+ lastPlace = null;
96
+ disposed = false;
97
+ constructor(service, host = {}) {
98
+ this.service = service;
99
+ this.host = host;
100
+ this.i18n = host.i18n ?? i18next;
101
+ this.folder = host.folder ?? '';
102
+ this.files = host.files ?? null;
103
+ this.panelBodyHeight = host.panelBodyHeight ?? PANEL_BODY_HEIGHT;
104
+ this.problems = new ProjectProblems(host.problemSource ?? serviceProblems(service, uri => this.pathOfLocation(uri) ?? uri));
105
+ }
106
+ /* Open panels of other editors of the project follow a resize. */
107
+ onPanelBodyChange(listener) {
108
+ this.panelListeners.add(listener);
109
+ return () => this.panelListeners.delete(listener);
110
+ }
111
+ resizePanelBody(height) {
112
+ if (height === this.panelBodyHeight) {
113
+ return;
118
114
  }
119
- /*
120
- * What the servers want changed before files are made, for a host that makes them outside a language server's
121
- * edit: apply the edit with `applyWorkspaceEdit`, then make the files. Null when no server has anything to say.
122
- */
123
- willCreateFiles(targets) {
124
- return this.service.willCreateFiles?.(filesOf(targets)) ?? Promise.resolve(null);
115
+ this.panelBodyHeight = height;
116
+ this.panelListeners.forEach(listener => listener(height));
117
+ this.host.onPanelResize?.(height);
118
+ }
119
+ pathOfLocation(uri) {
120
+ if (this.host.pathOfUri !== undefined) {
121
+ return this.host.pathOfUri(uri);
125
122
  }
126
- /* Tells the servers that files were made, once they are. */
127
- didCreateFiles(targets) {
128
- return this.service.didCreateFiles?.(filesOf(targets)) ?? Promise.resolve();
129
- }
130
- /* What the servers want changed before files are deleted, such as the references to them; null when no server has anything to say. */
131
- willDeleteFiles(targets) {
132
- return this.service.willDeleteFiles?.(filesOf(targets)) ?? Promise.resolve(null);
133
- }
134
- /* Tells the servers that files were deleted, once they are. */
135
- didDeleteFiles(targets) {
136
- return this.service.didDeleteFiles?.(filesOf(targets)) ?? Promise.resolve();
123
+ const path = fileUriToPath(uri);
124
+ return path !== null && this.folder !== '' ? relativeTo(path, this.folder) : path;
125
+ }
126
+ /* Makes an edit of a language server: an open document takes it as one undo step and any other file gets an unsaved draft. */
127
+ applyWorkspaceEdit(edit) {
128
+ const {
129
+ service
130
+ } = this;
131
+ return applyWorkspaceEdit(edit, {
132
+ editorOf: uri => this.holders.get(uri)?.editors[0],
133
+ files: this.files,
134
+ ...(service.versionOf === undefined ? {} : {
135
+ versionOf: uri => service.versionOf(uri)
136
+ })
137
+ });
138
+ }
139
+ /*
140
+ * What the servers want changed before files are made, for a host that makes them outside a language server's
141
+ * edit: apply the edit with `applyWorkspaceEdit`, then make the files. Null when no server has anything to say.
142
+ */
143
+ willCreateFiles(targets) {
144
+ return this.service.willCreateFiles?.(filesOf(targets)) ?? Promise.resolve(null);
145
+ }
146
+ /* Tells the servers that files were made, once they are. */
147
+ didCreateFiles(targets) {
148
+ return this.service.didCreateFiles?.(filesOf(targets)) ?? Promise.resolve();
149
+ }
150
+ /* What the servers want changed before files are deleted, such as the references to them; null when no server has anything to say. */
151
+ willDeleteFiles(targets) {
152
+ return this.service.willDeleteFiles?.(filesOf(targets)) ?? Promise.resolve(null);
153
+ }
154
+ /* Tells the servers that files were deleted, once they are. */
155
+ didDeleteFiles(targets) {
156
+ return this.service.didDeleteFiles?.(filesOf(targets)) ?? Promise.resolve();
157
+ }
158
+ /* The text of a file as it stands now: an open document's, else a draft's or the machine's; null when it is no text file. */
159
+ async readText(uri) {
160
+ const open = this.holders.get(uri)?.sync.text;
161
+ if (open !== undefined) {
162
+ return open;
137
163
  }
138
- /* The text of a file as it stands now: an open document's, else a draft's or the machine's; null when it is no text file. */
139
- async readText(uri) {
140
- const open = this.holders.get(uri)?.sync.text;
141
- if (open !== undefined) {
142
- return open;
143
- }
144
- const path = fileUriToPath(uri);
145
- return path === null || this.files === null ? null : ((await this.files.read(path))?.text ?? null);
164
+ const path = fileUriToPath(uri);
165
+ return path === null || this.files === null ? null : (await this.files.read(path))?.text ?? null;
166
+ }
167
+ /*
168
+ * Opens the file of a place on its line. A file opens on a line only, so the column is kept here until the editor
169
+ * that shows the line asks for it with `takeCaret`.
170
+ */
171
+ openPlace(place) {
172
+ this.pendingCaret = place;
173
+ this.host.openPlace?.(place);
174
+ }
175
+ /* Where an editor's caret stands now, so a jump from a surface without an editor, such as the Problems panel, still knows what it left. */
176
+ noteCaret(place) {
177
+ this.lastPlace = place;
178
+ }
179
+ /* Opens a place from outside any editor and remembers the caret that was last seen. */
180
+ jumpTo(place) {
181
+ if (this.lastPlace !== null) {
182
+ this.history.record(this.lastPlace);
146
183
  }
147
- /*
148
- * Opens the file of a place on its line. A file opens on a line only, so the column is kept here until the editor
149
- * that shows the line asks for it with `takeCaret`.
150
- */
151
- openPlace(place) {
152
- this.pendingCaret = place;
153
- this.host.openPlace?.(place);
184
+ this.openPlace(place);
185
+ }
186
+ /* The column an opened place waits for, once the editor of its file has put its caret on the line; each place is asked for once. */
187
+ takeCaret(uri, line) {
188
+ const pending = this.pendingCaret;
189
+ if (pending === null || pending.uri !== uri || pending.position.line !== line) {
190
+ return null;
154
191
  }
155
- /* Where an editor's caret stands now, so a jump from a surface without an editor, such as the Problems panel, still knows what it left. */
156
- noteCaret(place) {
157
- this.lastPlace = place;
192
+ this.pendingCaret = null;
193
+ return pending.position;
194
+ }
195
+ /* Opens `uri` for the editor, or joins the editors that already hold it. */
196
+ acquire(uri, languageId, editor) {
197
+ if (this.disposed) {
198
+ throw new Error('The project language is disposed');
158
199
  }
159
- /* Opens a place from outside any editor and remembers the caret that was last seen. */
160
- jumpTo(place) {
161
- if (this.lastPlace !== null) {
162
- this.history.record(this.lastPlace);
163
- }
164
- this.openPlace(place);
200
+ let holder = this.holders.get(uri);
201
+ if (holder === undefined) {
202
+ holder = {
203
+ editors: [],
204
+ sync: new DocumentSync(this.service, uri, languageId, editor),
205
+ languageId
206
+ };
207
+ this.holders.set(uri, holder);
165
208
  }
166
- /* The column an opened place waits for, once the editor of its file has put its caret on the line; each place is asked for once. */
167
- takeCaret(uri, line) {
168
- const pending = this.pendingCaret;
169
- if (pending === null || pending.uri !== uri || pending.position.line !== line) {
170
- return null;
209
+ holder.editors.push(editor);
210
+ let released = false;
211
+ return {
212
+ uri,
213
+ service: this.service,
214
+ get ready() {
215
+ return holder.sync.ready;
216
+ },
217
+ release: () => {
218
+ if (!released) {
219
+ released = true;
220
+ this.release(uri, editor);
171
221
  }
172
- this.pendingCaret = null;
173
- return pending.position;
222
+ }
223
+ };
224
+ }
225
+ release(uri, editor) {
226
+ const holder = this.holders.get(uri);
227
+ if (holder === undefined) {
228
+ return;
174
229
  }
175
- /* Opens `uri` for the editor, or joins the editors that already hold it. */
176
- acquire(uri, languageId, editor) {
177
- if (this.disposed) {
178
- throw new Error('The project language is disposed');
179
- }
180
- let holder = this.holders.get(uri);
181
- if (holder === undefined) {
182
- holder = { editors: [], sync: new DocumentSync(this.service, uri, languageId, editor), languageId };
183
- this.holders.set(uri, holder);
230
+ holder.editors.splice(holder.editors.indexOf(editor), 1);
231
+ const next = holder.editors[0];
232
+ if (next === undefined) {
233
+ holder.sync.dispose();
234
+ this.holders.delete(uri);
235
+ void holder.sync.ready.catch(() => undefined).then(() => {
236
+ if (this.holders.has(uri)) {
237
+ return undefined;
184
238
  }
185
- holder.editors.push(editor);
186
- let released = false;
187
- return {
188
- uri,
189
- service: this.service,
190
- get ready() {
191
- return holder.sync.ready;
192
- },
193
- release: () => {
194
- if (!released) {
195
- released = true;
196
- this.release(uri, editor);
197
- }
198
- }
199
- };
239
+ return this.service.closeDocument(uri);
240
+ }).catch(() => undefined);
241
+ } else if (holder.sync.editor === editor) {
242
+ // The editor that sent the changes is gone; the next one takes over and opens the document with its own text.
243
+ holder.sync.dispose();
244
+ holder.sync = new DocumentSync(this.service, uri, holder.languageId, next);
200
245
  }
201
- release(uri, editor) {
202
- const holder = this.holders.get(uri);
203
- if (holder === undefined) {
204
- return;
205
- }
206
- holder.editors.splice(holder.editors.indexOf(editor), 1);
207
- const next = holder.editors[0];
208
- if (next === undefined) {
209
- holder.sync.dispose();
210
- this.holders.delete(uri);
211
- void holder.sync.ready
212
- .catch(() => undefined)
213
- .then(() => {
214
- if (this.holders.has(uri)) {
215
- return undefined;
216
- }
217
- return this.service.closeDocument(uri);
218
- })
219
- .catch(() => undefined);
220
- }
221
- else if (holder.sync.editor === editor) {
222
- // The editor that sent the changes is gone; the next one takes over and opens the document with its own text.
223
- holder.sync.dispose();
224
- holder.sync = new DocumentSync(this.service, uri, holder.languageId, next);
225
- }
246
+ }
247
+ dispose() {
248
+ if (this.disposed) {
249
+ return;
226
250
  }
227
- dispose() {
228
- if (this.disposed) {
229
- return;
230
- }
231
- this.disposed = true;
232
- disposeSnippetTransforms(this);
233
- for (const [uri, holder] of this.holders) {
234
- holder.sync.dispose();
235
- void holder.sync.ready
236
- .catch(() => undefined)
237
- .then(() => this.service.closeDocument(uri))
238
- .catch(() => undefined);
239
- }
240
- this.holders.clear();
241
- this.problems.dispose();
251
+ this.disposed = true;
252
+ disposeSnippetTransforms(this);
253
+ for (const [uri, holder] of this.holders) {
254
+ holder.sync.dispose();
255
+ void holder.sync.ready.catch(() => undefined).then(() => this.service.closeDocument(uri)).catch(() => undefined);
242
256
  }
243
- }
257
+ this.holders.clear();
258
+ this.problems.dispose();
259
+ }
260
+ }
@@ -1,85 +1,97 @@
1
1
  import { comparePositions, problemCountsOf, severityOf } from './diagnostics-model.js';
2
- const SEVERITY_ORDER = { error: 0, warning: 1, info: 2, hint: 3 };
2
+ const SEVERITY_ORDER = {
3
+ error: 0,
4
+ warning: 1,
5
+ info: 2,
6
+ hint: 3
7
+ };
3
8
  /* Worst first, then in the order of the file. */
4
9
  export function compareRows(left, right) {
5
- return (SEVERITY_ORDER[severityOf(left.diagnostic)] - SEVERITY_ORDER[severityOf(right.diagnostic)] ||
6
- comparePositions(left.diagnostic.range.start, right.diagnostic.range.start));
10
+ return SEVERITY_ORDER[severityOf(left.diagnostic)] - SEVERITY_ORDER[severityOf(right.diagnostic)] || comparePositions(left.diagnostic.range.start, right.diagnostic.range.start);
7
11
  }
8
12
  /* The diagnostics a service reports, by the stored path `pathOf` gives each URI. */
9
- export function serviceProblems(service, pathOf = (uri) => uri) {
10
- return (listener) => {
11
- const subscription = service.onDiagnostics((report) => listener({ path: pathOf(report.uri), server: report.source, diagnostics: report.diagnostics }));
12
- return () => subscription.dispose();
13
- };
13
+ export function serviceProblems(service, pathOf = uri => uri) {
14
+ return listener => {
15
+ const subscription = service.onDiagnostics(report => listener({
16
+ path: pathOf(report.uri),
17
+ server: report.source,
18
+ diagnostics: report.diagnostics
19
+ }));
20
+ return () => subscription.dispose();
21
+ };
14
22
  }
15
23
  /*
16
24
  * Each report replaces the previous diagnostics from that source for the file. Files with no
17
25
  * remaining problems drop out. Hints remain editor annotations rather than listed problems.
18
26
  */
19
27
  export class ProjectProblems {
20
- reports = new Map();
21
- files = new Map();
22
- listeners = new Set();
23
- stop;
24
- /* The paths of `files` in order; null once a file came or went. */
25
- order = [];
26
- /* Made on first read after a report, so a project nobody lists does no sorting. */
27
- snapshot = [];
28
- /* A service stands for `serviceProblems(service, pathOf)`. */
29
- constructor(source, pathOf) {
30
- const listen = typeof source === 'function' ? source : serviceProblems(source, pathOf);
31
- this.stop = listen((report) => this.report(report.path, report.server, report.diagnostics));
28
+ reports = new Map();
29
+ files = new Map();
30
+ listeners = new Set();
31
+ stop;
32
+ /* The paths of `files` in order; null once a file came or went. */
33
+ order = [];
34
+ /* Made on first read after a report, so a project nobody lists does no sorting. */
35
+ snapshot = [];
36
+ /* A service stands for `serviceProblems(service, pathOf)`. */
37
+ constructor(source, pathOf) {
38
+ const listen = typeof source === 'function' ? source : serviceProblems(source, pathOf);
39
+ this.stop = listen(report => this.report(report.path, report.server, report.diagnostics));
40
+ }
41
+ report(path, server, diagnostics) {
42
+ const kept = diagnostics.filter(diagnostic => severityOf(diagnostic) !== 'hint');
43
+ const servers = this.reports.get(path) ?? new Map();
44
+ if (kept.length === 0 && !servers.has(server)) {
45
+ // A clean file reported clean again, as servers do after every edit.
46
+ return;
32
47
  }
33
- report(path, server, diagnostics) {
34
- const kept = diagnostics.filter((diagnostic) => severityOf(diagnostic) !== 'hint');
35
- const servers = this.reports.get(path) ?? new Map();
36
- if (kept.length === 0 && !servers.has(server)) {
37
- // A clean file reported clean again, as servers do after every edit.
38
- return;
39
- }
40
- if (kept.length === 0) {
41
- servers.delete(server);
42
- }
43
- else {
44
- servers.set(server, kept);
45
- }
46
- if (servers.size === 0) {
47
- this.reports.delete(path);
48
- this.files.delete(path);
49
- this.order = null;
50
- }
51
- else {
52
- if (!this.files.has(path)) {
53
- this.order = null;
54
- }
55
- this.reports.set(path, servers);
56
- const rows = [...servers].flatMap(([name, list]) => list.map((diagnostic) => ({ diagnostic, server: name }))).sort(compareRows);
57
- this.files.set(path, { path, rows });
58
- }
59
- this.snapshot = null;
60
- for (const listener of [...this.listeners]) {
61
- listener();
62
- }
48
+ if (kept.length === 0) {
49
+ servers.delete(server);
50
+ } else {
51
+ servers.set(server, kept);
63
52
  }
64
- /* The same array until the next report, which is what a store a component reads must hand out. */
65
- getSnapshot = () => {
66
- if (this.snapshot === null) {
67
- this.order ??= [...this.files.keys()].sort((left, right) => left.localeCompare(right));
68
- this.snapshot = this.order.map((path) => this.files.get(path));
69
- }
70
- return this.snapshot;
71
- };
72
- subscribe = (listener) => {
73
- this.listeners.add(listener);
74
- return () => {
75
- this.listeners.delete(listener);
76
- };
77
- };
78
- dispose() {
79
- this.stop();
80
- this.listeners.clear();
53
+ if (servers.size === 0) {
54
+ this.reports.delete(path);
55
+ this.files.delete(path);
56
+ this.order = null;
57
+ } else {
58
+ if (!this.files.has(path)) {
59
+ this.order = null;
60
+ }
61
+ this.reports.set(path, servers);
62
+ const rows = [...servers].flatMap(([name, list]) => list.map(diagnostic => ({
63
+ diagnostic,
64
+ server: name
65
+ }))).sort(compareRows);
66
+ this.files.set(path, {
67
+ path,
68
+ rows
69
+ });
70
+ }
71
+ this.snapshot = null;
72
+ for (const listener of [...this.listeners]) {
73
+ listener();
81
74
  }
75
+ }
76
+ /* The same array until the next report, which is what a store a component reads must hand out. */
77
+ getSnapshot = () => {
78
+ if (this.snapshot === null) {
79
+ this.order ??= [...this.files.keys()].sort((left, right) => left.localeCompare(right));
80
+ this.snapshot = this.order.map(path => this.files.get(path));
81
+ }
82
+ return this.snapshot;
83
+ };
84
+ subscribe = listener => {
85
+ this.listeners.add(listener);
86
+ return () => {
87
+ this.listeners.delete(listener);
88
+ };
89
+ };
90
+ dispose() {
91
+ this.stop();
92
+ this.listeners.clear();
93
+ }
82
94
  }
83
95
  export function countsOf(files) {
84
- return problemCountsOf(files.flatMap((file) => file.rows.map((row) => row.diagnostic)));
85
- }
96
+ return problemCountsOf(files.flatMap(file => file.rows.map(row => row.diagnostic)));
97
+ }