@theia/plugin-ext 1.71.0-next.8 → 1.72.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/plugin-api-rpc.d.ts +76 -0
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js +9 -1
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +13 -6
- package/lib/hosted/browser/hosted-plugin.js.map +1 -1
- package/lib/main/browser/main-context.d.ts.map +1 -1
- package/lib/main/browser/main-context.js +2 -6
- package/lib/main/browser/main-context.js.map +1 -1
- package/lib/main/browser/main-file-system-event-service.d.ts +10 -2
- package/lib/main/browser/main-file-system-event-service.d.ts.map +1 -1
- package/lib/main/browser/main-file-system-event-service.js +19 -2
- package/lib/main/browser/main-file-system-event-service.js.map +1 -1
- package/lib/main/browser/menus/menus-contribution-handler.d.ts.map +1 -1
- package/lib/main/browser/menus/menus-contribution-handler.js +2 -0
- package/lib/main/browser/menus/menus-contribution-handler.js.map +1 -1
- package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts +1 -0
- package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts.map +1 -1
- package/lib/main/browser/menus/plugin-menu-command-adapter.js +13 -1
- package/lib/main/browser/menus/plugin-menu-command-adapter.js.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js +9 -2
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
- package/lib/main/browser/scm-main.d.ts +33 -2
- package/lib/main/browser/scm-main.d.ts.map +1 -1
- package/lib/main/browser/scm-main.js +237 -3
- package/lib/main/browser/scm-main.js.map +1 -1
- package/lib/main/browser/scm-main.spec.d.ts +2 -0
- package/lib/main/browser/scm-main.spec.d.ts.map +1 -0
- package/lib/main/browser/scm-main.spec.js +87 -0
- package/lib/main/browser/scm-main.spec.js.map +1 -0
- package/lib/main/browser/test-main.d.ts +3 -2
- package/lib/main/browser/test-main.d.ts.map +1 -1
- package/lib/main/browser/test-main.js +12 -1
- package/lib/main/browser/test-main.js.map +1 -1
- package/lib/plugin/file-system-event-service-ext-impl.d.ts +11 -5
- package/lib/plugin/file-system-event-service-ext-impl.d.ts.map +1 -1
- package/lib/plugin/file-system-event-service-ext-impl.js +28 -9
- package/lib/plugin/file-system-event-service-ext-impl.js.map +1 -1
- package/lib/plugin/plugin-context.js +4 -4
- package/lib/plugin/plugin-context.js.map +1 -1
- package/lib/plugin/scm.d.ts +8 -2
- package/lib/plugin/scm.d.ts.map +1 -1
- package/lib/plugin/scm.js +188 -5
- package/lib/plugin/scm.js.map +1 -1
- package/lib/plugin/scm.spec.d.ts +2 -0
- package/lib/plugin/scm.spec.d.ts.map +1 -0
- package/lib/plugin/scm.spec.js +461 -0
- package/lib/plugin/scm.spec.js.map +1 -0
- package/lib/plugin/terminal-ext.d.ts +13 -3
- package/lib/plugin/terminal-ext.d.ts.map +1 -1
- package/lib/plugin/terminal-ext.js +51 -10
- package/lib/plugin/terminal-ext.js.map +1 -1
- package/lib/plugin/terminal-ext.spec.d.ts +2 -0
- package/lib/plugin/terminal-ext.spec.d.ts.map +1 -0
- package/lib/plugin/terminal-ext.spec.js +285 -0
- package/lib/plugin/terminal-ext.spec.js.map +1 -0
- package/lib/plugin/test-item.d.ts.map +1 -1
- package/lib/plugin/test-item.js +8 -3
- package/lib/plugin/test-item.js.map +1 -1
- package/lib/plugin/tests.d.ts.map +1 -1
- package/lib/plugin/tests.js +15 -3
- package/lib/plugin/tests.js.map +1 -1
- package/lib/plugin/type-converters.d.ts +2 -2
- package/lib/plugin/type-converters.d.ts.map +1 -1
- package/lib/plugin/type-converters.js +3 -9
- package/lib/plugin/type-converters.js.map +1 -1
- package/lib/plugin/types-impl.d.ts +1 -1
- package/lib/plugin/types-impl.d.ts.map +1 -1
- package/lib/plugin/types-impl.js +1 -1
- package/lib/plugin/types-impl.js.map +1 -1
- package/lib/plugin/workspace.d.ts.map +1 -1
- package/lib/plugin/workspace.js +17 -3
- package/lib/plugin/workspace.js.map +1 -1
- package/package.json +39 -39
- package/src/common/plugin-api-rpc.ts +78 -0
- package/src/hosted/browser/hosted-plugin.ts +13 -6
- package/src/main/browser/main-context.ts +3 -7
- package/src/main/browser/main-file-system-event-service.ts +26 -6
- package/src/main/browser/menus/menus-contribution-handler.ts +2 -0
- package/src/main/browser/menus/plugin-menu-command-adapter.ts +15 -2
- package/src/main/browser/menus/vscode-theia-menu-mappings.ts +12 -3
- package/src/main/browser/scm-main.spec.ts +105 -0
- package/src/main/browser/scm-main.ts +272 -4
- package/src/main/browser/test-main.ts +13 -3
- package/src/plugin/file-system-event-service-ext-impl.ts +40 -14
- package/src/plugin/plugin-context.ts +7 -7
- package/src/plugin/scm.spec.ts +615 -0
- package/src/plugin/scm.ts +224 -6
- package/src/plugin/terminal-ext.spec.ts +350 -0
- package/src/plugin/terminal-ext.ts +58 -12
- package/src/plugin/test-item.ts +8 -3
- package/src/plugin/tests.ts +14 -3
- package/src/plugin/type-converters.ts +7 -13
- package/src/plugin/types-impl.ts +2 -2
- package/src/plugin/workspace.ts +17 -3
|
@@ -27,14 +27,24 @@ import {
|
|
|
27
27
|
ScmMain,
|
|
28
28
|
SourceControlProviderFeatures,
|
|
29
29
|
ScmRawResourceSplices, ScmRawResourceGroup,
|
|
30
|
-
ScmActionButton as RpcScmActionButton
|
|
30
|
+
ScmActionButton as RpcScmActionButton,
|
|
31
|
+
ScmHistoryItemRefDto,
|
|
32
|
+
ScmHistoryItemDto,
|
|
33
|
+
ScmHistoryItemChangeDto,
|
|
34
|
+
ScmHistoryOptionsDto,
|
|
35
|
+
ScmHistoryItemRefsChangeEventDto
|
|
31
36
|
} from '../../common/plugin-api-rpc';
|
|
32
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
ScmProvider, ScmResource, ScmResourceDecorations, ScmResourceGroup, ScmCommand, ScmActionButton,
|
|
39
|
+
ScmHistoryProvider, ScmHistoryItemRef, ScmHistoryItemRefsChangeEvent,
|
|
40
|
+
ScmHistoryOptions, ScmHistoryItem, ScmHistoryItemChange
|
|
41
|
+
} from '@theia/scm/lib/browser/scm-provider';
|
|
33
42
|
import { ScmRepository } from '@theia/scm/lib/browser/scm-repository';
|
|
34
43
|
import { ScmService } from '@theia/scm/lib/browser/scm-service';
|
|
35
44
|
import { RPCProtocol } from '../../common/rpc-protocol';
|
|
36
45
|
import { interfaces } from '@theia/core/shared/inversify';
|
|
37
46
|
import { Emitter, Event } from '@theia/core/lib/common/event';
|
|
47
|
+
import { CancellationToken, CancellationTokenSource } from '@theia/core/lib/common/cancellation';
|
|
38
48
|
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
39
49
|
import URI from '@theia/core/lib/common/uri';
|
|
40
50
|
import { URI as vscodeURI } from '@theia/core/shared/vscode-uri';
|
|
@@ -104,6 +114,198 @@ export class PluginScmResource implements ScmResource {
|
|
|
104
114
|
}
|
|
105
115
|
}
|
|
106
116
|
|
|
117
|
+
function historyItemRefFromDto(dto: ScmHistoryItemRefDto): ScmHistoryItemRef {
|
|
118
|
+
return {
|
|
119
|
+
id: dto.id,
|
|
120
|
+
name: dto.name,
|
|
121
|
+
description: dto.description,
|
|
122
|
+
revision: dto.revision,
|
|
123
|
+
icon: dto.icon ? (ThemeIcon.isThemeIcon(dto.icon) ? ThemeIcon.asClassName(dto.icon) :
|
|
124
|
+
'light' in dto.icon ? vscodeURI.revive(dto.icon.light).toString() : vscodeURI.revive(dto.icon as UriComponents).toString()) : undefined,
|
|
125
|
+
category: dto.category,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function historyItemFromDto(dto: ScmHistoryItemDto): ScmHistoryItem {
|
|
130
|
+
return {
|
|
131
|
+
id: dto.id,
|
|
132
|
+
parentIds: dto.parentIds,
|
|
133
|
+
subject: dto.subject ?? '',
|
|
134
|
+
message: typeof dto.message === 'string' ? dto.message : dto.message?.value,
|
|
135
|
+
author: dto.author,
|
|
136
|
+
authorEmail: dto.authorEmail,
|
|
137
|
+
authorIcon: dto.authorIcon ? (ThemeIcon.isThemeIcon(dto.authorIcon) ? ThemeIcon.asClassName(dto.authorIcon) :
|
|
138
|
+
'light' in dto.authorIcon ? vscodeURI.revive(dto.authorIcon.light).toString() : vscodeURI.revive(dto.authorIcon as UriComponents).toString()) : undefined,
|
|
139
|
+
displayId: dto.displayId,
|
|
140
|
+
timestamp: dto.timestamp,
|
|
141
|
+
tooltip: typeof dto.tooltip === 'string' ? dto.tooltip : dto.tooltip?.value,
|
|
142
|
+
statistics: dto.statistics ? {
|
|
143
|
+
files: dto.statistics.files,
|
|
144
|
+
insertions: dto.statistics.insertions,
|
|
145
|
+
deletions: dto.statistics.deletions,
|
|
146
|
+
} : undefined,
|
|
147
|
+
references: dto.references ? dto.references.map(historyItemRefFromDto) : undefined,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function historyItemChangeFromDto(dto: ScmHistoryItemChangeDto): ScmHistoryItemChange {
|
|
152
|
+
return {
|
|
153
|
+
uri: vscodeURI.revive(dto.uri).toString(),
|
|
154
|
+
originalUri: dto.originalUri ? vscodeURI.revive(dto.originalUri).toString() : undefined,
|
|
155
|
+
modifiedUri: dto.modifiedUri ? vscodeURI.revive(dto.modifiedUri).toString() : undefined,
|
|
156
|
+
renameUri: dto.renameUri ? vscodeURI.revive(dto.renameUri).toString() : undefined,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export class PluginScmHistoryProvider implements ScmHistoryProvider {
|
|
161
|
+
|
|
162
|
+
private readonly onDidChangeCurrentHistoryItemRefsEmitter = new Emitter<void>();
|
|
163
|
+
readonly onDidChangeCurrentHistoryItemRefs: Event<void> = this.onDidChangeCurrentHistoryItemRefsEmitter.event;
|
|
164
|
+
|
|
165
|
+
private readonly onDidChangeHistoryItemRefsEmitter = new Emitter<ScmHistoryItemRefsChangeEvent>();
|
|
166
|
+
readonly onDidChangeHistoryItemRefs: Event<ScmHistoryItemRefsChangeEvent> = this.onDidChangeHistoryItemRefsEmitter.event;
|
|
167
|
+
|
|
168
|
+
private _currentHistoryItemRef: ScmHistoryItemRef | undefined;
|
|
169
|
+
get currentHistoryItemRef(): ScmHistoryItemRef | undefined { return this._currentHistoryItemRef; }
|
|
170
|
+
|
|
171
|
+
private _currentHistoryItemRemoteRef: ScmHistoryItemRef | undefined;
|
|
172
|
+
get currentHistoryItemRemoteRef(): ScmHistoryItemRef | undefined { return this._currentHistoryItemRemoteRef; }
|
|
173
|
+
|
|
174
|
+
private _currentHistoryItemBaseRef: ScmHistoryItemRef | undefined;
|
|
175
|
+
get currentHistoryItemBaseRef(): ScmHistoryItemRef | undefined { return this._currentHistoryItemBaseRef; }
|
|
176
|
+
|
|
177
|
+
private readonly disposables = new DisposableCollection();
|
|
178
|
+
private pendingRequests = new Set<CancellationTokenSource>();
|
|
179
|
+
|
|
180
|
+
constructor(
|
|
181
|
+
private readonly proxy: ScmExt,
|
|
182
|
+
private readonly handle: number
|
|
183
|
+
) {
|
|
184
|
+
this.disposables.push(this.onDidChangeCurrentHistoryItemRefsEmitter);
|
|
185
|
+
this.disposables.push(this.onDidChangeHistoryItemRefsEmitter);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
updateFromFeatures(features: SourceControlProviderFeatures): void {
|
|
189
|
+
if (features.currentHistoryItemRef !== undefined) {
|
|
190
|
+
this._currentHistoryItemRef = features.currentHistoryItemRef ? historyItemRefFromDto(features.currentHistoryItemRef) : undefined;
|
|
191
|
+
}
|
|
192
|
+
if (features.currentHistoryItemRemoteRef !== undefined) {
|
|
193
|
+
this._currentHistoryItemRemoteRef = features.currentHistoryItemRemoteRef ? historyItemRefFromDto(features.currentHistoryItemRemoteRef) : undefined;
|
|
194
|
+
}
|
|
195
|
+
if (features.currentHistoryItemBaseRef !== undefined) {
|
|
196
|
+
this._currentHistoryItemBaseRef = features.currentHistoryItemBaseRef ? historyItemRefFromDto(features.currentHistoryItemBaseRef) : undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
fireDidChangeCurrentHistoryItemRefs(): void {
|
|
201
|
+
this.onDidChangeCurrentHistoryItemRefsEmitter.fire();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fireDidChangeHistoryItemRefs(event: ScmHistoryItemRefsChangeEventDto): void {
|
|
205
|
+
this.onDidChangeHistoryItemRefsEmitter.fire({
|
|
206
|
+
added: event.added.map(historyItemRefFromDto),
|
|
207
|
+
removed: event.removed.map(historyItemRefFromDto),
|
|
208
|
+
modified: event.modified.map(historyItemRefFromDto),
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async provideHistoryItemRefs(historyItemRefs: string[] | undefined, token: CancellationToken): Promise<ScmHistoryItemRef[] | undefined> {
|
|
213
|
+
const cts = new CancellationTokenSource();
|
|
214
|
+
const listener = token.onCancellationRequested(() => cts.cancel());
|
|
215
|
+
this.pendingRequests.add(cts);
|
|
216
|
+
try {
|
|
217
|
+
const result = await this.proxy.$provideHistoryItemRefs(this.handle, historyItemRefs, cts.token);
|
|
218
|
+
if (!result) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
return result.map(historyItemRefFromDto);
|
|
222
|
+
} finally {
|
|
223
|
+
listener.dispose();
|
|
224
|
+
this.pendingRequests.delete(cts);
|
|
225
|
+
cts.dispose();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async provideHistoryItems(options: ScmHistoryOptions, token: CancellationToken): Promise<ScmHistoryItem[] | undefined> {
|
|
230
|
+
const dto: ScmHistoryOptionsDto = {
|
|
231
|
+
skip: options.skip,
|
|
232
|
+
limit: options.limit,
|
|
233
|
+
historyItemRefs: options.historyItemRefs ? [...options.historyItemRefs] : undefined,
|
|
234
|
+
filterText: options.filterText,
|
|
235
|
+
};
|
|
236
|
+
const cts = new CancellationTokenSource();
|
|
237
|
+
const listener = token.onCancellationRequested(() => cts.cancel());
|
|
238
|
+
this.pendingRequests.add(cts);
|
|
239
|
+
try {
|
|
240
|
+
const result = await this.proxy.$provideHistoryItems(this.handle, dto, cts.token);
|
|
241
|
+
if (!result) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
return result.map(historyItemFromDto);
|
|
245
|
+
} finally {
|
|
246
|
+
listener.dispose();
|
|
247
|
+
this.pendingRequests.delete(cts);
|
|
248
|
+
cts.dispose();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise<ScmHistoryItemChange[] | undefined> {
|
|
253
|
+
const cts = new CancellationTokenSource();
|
|
254
|
+
const listener = token.onCancellationRequested(() => cts.cancel());
|
|
255
|
+
this.pendingRequests.add(cts);
|
|
256
|
+
try {
|
|
257
|
+
const result = await this.proxy.$provideHistoryItemChanges(this.handle, historyItemId, historyItemParentId, cts.token);
|
|
258
|
+
if (!result) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
return result.map(historyItemChangeFromDto);
|
|
262
|
+
} finally {
|
|
263
|
+
listener.dispose();
|
|
264
|
+
this.pendingRequests.delete(cts);
|
|
265
|
+
cts.dispose();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async resolveHistoryItem(historyItemId: string, token: CancellationToken): Promise<ScmHistoryItem | undefined> {
|
|
270
|
+
const cts = new CancellationTokenSource();
|
|
271
|
+
const listener = token.onCancellationRequested(() => cts.cancel());
|
|
272
|
+
this.pendingRequests.add(cts);
|
|
273
|
+
try {
|
|
274
|
+
const result = await this.proxy.$resolveHistoryItem(this.handle, historyItemId, cts.token);
|
|
275
|
+
if (!result) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
return historyItemFromDto(result);
|
|
279
|
+
} finally {
|
|
280
|
+
listener.dispose();
|
|
281
|
+
this.pendingRequests.delete(cts);
|
|
282
|
+
cts.dispose();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async resolveHistoryItemRefsCommonAncestor(historyItemRefs: string[], token: CancellationToken): Promise<string | undefined> {
|
|
287
|
+
const cts = new CancellationTokenSource();
|
|
288
|
+
const listener = token.onCancellationRequested(() => cts.cancel());
|
|
289
|
+
this.pendingRequests.add(cts);
|
|
290
|
+
try {
|
|
291
|
+
return await this.proxy.$resolveHistoryItemRefsCommonAncestor(this.handle, historyItemRefs, cts.token) ?? undefined;
|
|
292
|
+
} finally {
|
|
293
|
+
listener.dispose();
|
|
294
|
+
this.pendingRequests.delete(cts);
|
|
295
|
+
cts.dispose();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
dispose(): void {
|
|
300
|
+
for (const cts of this.pendingRequests) {
|
|
301
|
+
cts.cancel();
|
|
302
|
+
cts.dispose();
|
|
303
|
+
}
|
|
304
|
+
this.pendingRequests.clear();
|
|
305
|
+
this.disposables.dispose();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
107
309
|
export class PluginScmProvider implements ScmProvider {
|
|
108
310
|
|
|
109
311
|
private _id = this.contextValue;
|
|
@@ -118,6 +320,11 @@ export class PluginScmProvider implements ScmProvider {
|
|
|
118
320
|
private _actionButton: ScmActionButton | undefined;
|
|
119
321
|
get actionButton(): ScmActionButton | undefined { return this._actionButton; }
|
|
120
322
|
|
|
323
|
+
private _historyProvider: PluginScmHistoryProvider | undefined;
|
|
324
|
+
get historyProvider(): ScmHistoryProvider | undefined {
|
|
325
|
+
return this._historyProvider;
|
|
326
|
+
}
|
|
327
|
+
|
|
121
328
|
private features: SourceControlProviderFeatures = {};
|
|
122
329
|
|
|
123
330
|
get providerContextValue(): string | undefined { return this.features.contextValue; }
|
|
@@ -172,8 +379,22 @@ export class PluginScmProvider implements ScmProvider {
|
|
|
172
379
|
|
|
173
380
|
updateSourceControl(features: SourceControlProviderFeatures): void {
|
|
174
381
|
this.features = { ...this.features, ...features };
|
|
382
|
+
|
|
383
|
+
if (typeof features.hasHistoryProvider !== 'undefined') {
|
|
384
|
+
if (features.hasHistoryProvider && !this._historyProvider) {
|
|
385
|
+
this._historyProvider = new PluginScmHistoryProvider(this.proxy, this.handle);
|
|
386
|
+
} else if (!features.hasHistoryProvider && this._historyProvider) {
|
|
387
|
+
this._historyProvider.dispose();
|
|
388
|
+
this._historyProvider = undefined;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
175
392
|
this.onDidChangeEmitter.fire();
|
|
176
393
|
|
|
394
|
+
if (this._historyProvider) {
|
|
395
|
+
this._historyProvider.updateFromFeatures(features);
|
|
396
|
+
}
|
|
397
|
+
|
|
177
398
|
if (typeof features.commitTemplate !== 'undefined') {
|
|
178
399
|
this.onDidChangeCommitTemplateEmitter.fire(this.commitTemplate!);
|
|
179
400
|
}
|
|
@@ -303,7 +524,10 @@ export class PluginScmProvider implements ScmProvider {
|
|
|
303
524
|
this.onDidChangeActionButtonEmitter.fire(actionButton);
|
|
304
525
|
}
|
|
305
526
|
|
|
306
|
-
dispose(): void {
|
|
527
|
+
dispose(): void {
|
|
528
|
+
this._historyProvider?.dispose();
|
|
529
|
+
this._historyProvider = undefined;
|
|
530
|
+
}
|
|
307
531
|
}
|
|
308
532
|
|
|
309
533
|
export class ScmMainImpl implements ScmMain {
|
|
@@ -388,7 +612,31 @@ export class ScmMainImpl implements ScmMain {
|
|
|
388
612
|
return;
|
|
389
613
|
}
|
|
390
614
|
|
|
391
|
-
|
|
615
|
+
// Defensively cascade-dispose any children (e.g. worktrees) whose parent
|
|
616
|
+
// is being unregistered. The plugin API exposes onDidDisposeParent, but if
|
|
617
|
+
// a plugin fails to propagate it (or races with an external change like a
|
|
618
|
+
// worktree being removed on disk), children would otherwise be orphaned
|
|
619
|
+
// in the Repositories view. Children are torn down inline (not recursively)
|
|
620
|
+
// to keep the iteration over `this.repositories` safe and to tolerate a
|
|
621
|
+
// plugin-side RPC for the child arriving first.
|
|
622
|
+
const childHandles: number[] = [];
|
|
623
|
+
for (const [childHandle, childRepo] of this.repositories) {
|
|
624
|
+
if (childHandle !== handle && (childRepo.provider as PluginScmProvider).parentHandle === handle) {
|
|
625
|
+
childHandles.push(childHandle);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
for (const childHandle of childHandles) {
|
|
629
|
+
const childRepository = this.repositories.get(childHandle);
|
|
630
|
+
if (!childRepository) {
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
this.repositoryDisposables.get(childHandle)?.dispose();
|
|
634
|
+
this.repositoryDisposables.delete(childHandle);
|
|
635
|
+
childRepository.dispose();
|
|
636
|
+
this.repositories.delete(childHandle);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
this.repositoryDisposables.get(handle)?.dispose();
|
|
392
640
|
this.repositoryDisposables.delete(handle);
|
|
393
641
|
|
|
394
642
|
repository.dispose();
|
|
@@ -522,4 +770,24 @@ export class ScmMainImpl implements ScmMain {
|
|
|
522
770
|
|
|
523
771
|
provider.updateActionButton(converted);
|
|
524
772
|
}
|
|
773
|
+
|
|
774
|
+
$onDidChangeCurrentHistoryItemRefs(sourceControlHandle: number): void {
|
|
775
|
+
const repository = this.repositories.get(sourceControlHandle);
|
|
776
|
+
if (!repository) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const provider = repository.provider as PluginScmProvider;
|
|
780
|
+
const historyProvider = provider.historyProvider as PluginScmHistoryProvider | undefined;
|
|
781
|
+
historyProvider?.fireDidChangeCurrentHistoryItemRefs();
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
$onDidChangeHistoryItemRefs(sourceControlHandle: number, event: ScmHistoryItemRefsChangeEventDto): void {
|
|
785
|
+
const repository = this.repositories.get(sourceControlHandle);
|
|
786
|
+
if (!repository) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const provider = repository.provider as PluginScmProvider;
|
|
790
|
+
const historyProvider = provider.historyProvider as PluginScmHistoryProvider | undefined;
|
|
791
|
+
historyProvider?.fireDidChangeHistoryItemRefs(event);
|
|
792
|
+
}
|
|
525
793
|
}
|
|
@@ -410,7 +410,8 @@ class TestControllerImpl implements TestController {
|
|
|
410
410
|
private _runs = new SimpleObservableCollection<TestRunImpl>();
|
|
411
411
|
readonly deltaBuilder = new AccumulatingTreeDeltaEmitter<string, TestItemImpl>(300);
|
|
412
412
|
canRefresh: boolean;
|
|
413
|
-
|
|
413
|
+
canResolveChildren: boolean = false;
|
|
414
|
+
private hasTriggeredInitialResolve: boolean = false;
|
|
414
415
|
readonly items = new TestItemCollection(this, item => item.path, () => this.deltaBuilder);
|
|
415
416
|
|
|
416
417
|
constructor(private readonly proxy: TestingExt, readonly id: string, public label: string) {
|
|
@@ -510,6 +511,10 @@ class TestControllerImpl implements TestController {
|
|
|
510
511
|
}
|
|
511
512
|
if ('canResolve' in change) {
|
|
512
513
|
this.canResolveChildren = change.canResolve!;
|
|
514
|
+
if (change.canResolve && !this.hasTriggeredInitialResolve) {
|
|
515
|
+
this.hasTriggeredInitialResolve = true;
|
|
516
|
+
this.resolveChildren();
|
|
517
|
+
}
|
|
513
518
|
}
|
|
514
519
|
if ('label' in change) {
|
|
515
520
|
this.label = change.label!;
|
|
@@ -543,9 +548,14 @@ class TestControllerImpl implements TestController {
|
|
|
543
548
|
}
|
|
544
549
|
onItemsChanged: Event<TreeDelta<string, TestItemImpl>[]> = this.deltaBuilder.onDidFlush;
|
|
545
550
|
|
|
546
|
-
resolveChildren(item
|
|
551
|
+
resolveChildren(item?: TestItem): void {
|
|
547
552
|
if (this.canResolveChildren) {
|
|
548
|
-
|
|
553
|
+
if (item) {
|
|
554
|
+
this.proxy.$onResolveChildren(this.id, itemToPath(item));
|
|
555
|
+
} else {
|
|
556
|
+
// Root-level resolve: trigger discovery of top-level test items
|
|
557
|
+
this.proxy.$onResolveChildren(this.id, []);
|
|
558
|
+
}
|
|
549
559
|
}
|
|
550
560
|
}
|
|
551
561
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
19
19
|
*--------------------------------------------------------------------------------------------*/
|
|
20
20
|
/**
|
|
21
|
-
* **IMPORTANT** this code is running in the plugin host process and should be
|
|
21
|
+
* **IMPORTANT** this code is running in the plugin host process and should be close as possible to VS Code counterpart:
|
|
22
22
|
* https://github.com/microsoft/vscode/blob/04c36be045a94fee58e5f8992d3e3fd980294a84/src/vs/workbench/api/common/extHostFileSystemEventService.ts
|
|
23
23
|
* One should be able to diff them to see differences.
|
|
24
24
|
*/
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
29
29
|
/* eslint-disable @typescript-eslint/tslint/config */
|
|
30
30
|
|
|
31
|
-
import { Emitter, WaitUntilEvent, AsyncEmitter, WaitUntilData } from '@theia/core/lib/common/event';
|
|
32
|
-
import {
|
|
31
|
+
import { Emitter, Event as EventNamespace, WaitUntilEvent, AsyncEmitter, WaitUntilData } from '@theia/core/lib/common/event';
|
|
32
|
+
import { GLOB_SPLIT, GLOBSTAR, parse } from '@theia/core/lib/common/glob';
|
|
33
33
|
import { UriComponents } from '../common/uri-components';
|
|
34
|
-
import { Disposable, URI, WorkspaceEdit } from './types-impl';
|
|
34
|
+
import { Disposable, RelativePattern, URI, WorkspaceEdit } from './types-impl';
|
|
35
35
|
import { EditorsAndDocumentsExtImpl as ExtHostDocumentsAndEditors } from './editors-and-documents';
|
|
36
|
+
import { WorkspaceExtImpl as ExtHostWorkspace } from './workspace';
|
|
36
37
|
import type * as vscode from '@theia/plugin';
|
|
37
38
|
import * as typeConverter from './type-converters';
|
|
38
39
|
import { FileOperation } from '@theia/filesystem/lib/common/files';
|
|
@@ -68,8 +69,9 @@ export class FileSystemWatcher implements vscode.FileSystemWatcher {
|
|
|
68
69
|
return Boolean(this._config & 0b100);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
constructor(dispatcher: Event<FileSystemEvents>, globPattern: string |
|
|
72
|
-
ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean, excludes?: string[]
|
|
72
|
+
constructor(dispatcher: Event<FileSystemEvents>, globPattern: string | RelativePattern,
|
|
73
|
+
ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean, excludes?: string[],
|
|
74
|
+
filter: (uri: URI) => boolean = () => true, disposable: { dispose(): unknown } = Disposable.from()) {
|
|
73
75
|
|
|
74
76
|
this._config = 0;
|
|
75
77
|
if (ignoreCreateEvents) {
|
|
@@ -89,7 +91,7 @@ export class FileSystemWatcher implements vscode.FileSystemWatcher {
|
|
|
89
91
|
if (!ignoreCreateEvents) {
|
|
90
92
|
for (const created of events.created) {
|
|
91
93
|
const uri = URI.revive(created);
|
|
92
|
-
if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath))) {
|
|
94
|
+
if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath)) && filter(uri)) {
|
|
93
95
|
this._onDidCreate.fire(uri);
|
|
94
96
|
}
|
|
95
97
|
}
|
|
@@ -97,7 +99,7 @@ export class FileSystemWatcher implements vscode.FileSystemWatcher {
|
|
|
97
99
|
if (!ignoreChangeEvents) {
|
|
98
100
|
for (const changed of events.changed) {
|
|
99
101
|
const uri = URI.revive(changed);
|
|
100
|
-
if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath))) {
|
|
102
|
+
if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath)) && filter(uri)) {
|
|
101
103
|
this._onDidChange.fire(uri);
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -105,14 +107,14 @@ export class FileSystemWatcher implements vscode.FileSystemWatcher {
|
|
|
105
107
|
if (!ignoreDeleteEvents) {
|
|
106
108
|
for (const deleted of events.deleted) {
|
|
107
109
|
const uri = URI.revive(deleted);
|
|
108
|
-
if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath))) {
|
|
110
|
+
if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath)) && filter(uri)) {
|
|
109
111
|
this._onDidDelete.fire(uri);
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
});
|
|
114
116
|
|
|
115
|
-
this._disposable = Disposable.from(this._onDidCreate, this._onDidChange, this._onDidDelete, subscription);
|
|
117
|
+
this._disposable = Disposable.from(disposable, this._onDidCreate, this._onDidChange, this._onDidDelete, subscription);
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
dispose(): void {
|
|
@@ -155,16 +157,40 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
|
|
155
157
|
constructor(
|
|
156
158
|
rpc: RPCProtocol,
|
|
157
159
|
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
|
158
|
-
private readonly
|
|
160
|
+
private readonly _extHostWorkspace: ExtHostWorkspace,
|
|
161
|
+
private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = rpc.getProxy(PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN),
|
|
162
|
+
private readonly _mainThreadFileSystemEventService = rpc.getProxy(PLUGIN_RPC_CONTEXT.FILE_SYSTEM_EVENT_SERVICE_MAIN)
|
|
159
163
|
) {
|
|
160
|
-
//
|
|
164
|
+
// Language services often watch every component of source trees (including dependencies),
|
|
165
|
+
// which can result in hundreds of watchers in large projects.
|
|
166
|
+
// Disable the leak warning (maxListeners 0 = unbounded) to avoid false positives.
|
|
167
|
+
EventNamespace.setMaxListeners(this._onFileSystemEvent.event, 0);
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
// --- file events
|
|
164
171
|
|
|
165
|
-
createFileSystemWatcher(globPattern: string |
|
|
172
|
+
createFileSystemWatcher(globPattern: string | RelativePattern, ignoreCreateEvents?: boolean,
|
|
166
173
|
ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
|
|
167
|
-
|
|
174
|
+
const filter = typeof globPattern === 'string' ? // ignore events outside the workspace when only a string pattern is provided
|
|
175
|
+
(uri: URI) => !!this._extHostWorkspace.getWorkspaceFolder(uri) : undefined;
|
|
176
|
+
return new FileSystemWatcher(this._onFileSystemEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents,
|
|
177
|
+
undefined, filter, this.ensureWatching(globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private ensureWatching(globPattern: string | RelativePattern,
|
|
181
|
+
ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): Disposable | undefined {
|
|
182
|
+
if (typeof globPattern === 'string' || this._extHostWorkspace.getWorkspaceFolder(globPattern.baseUri)) {
|
|
183
|
+
return; // workspace is already watched by default, no need to watch again
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (ignoreChangeEvents && ignoreCreateEvents && ignoreDeleteEvents) {
|
|
187
|
+
return; // no need to watch if we ignore all events
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const session = Math.random();
|
|
191
|
+
const recursive = globPattern.pattern.includes(GLOBSTAR) || globPattern.pattern.includes(GLOB_SPLIT); // only watch recursively if pattern indicates the need for it
|
|
192
|
+
this._mainThreadFileSystemEventService.$watch(session, globPattern.baseUri, { recursive, excludes: [] });
|
|
193
|
+
return Disposable.from({ dispose: () => this._mainThreadFileSystemEventService.$unwatch(session) });
|
|
168
194
|
}
|
|
169
195
|
|
|
170
196
|
$onFileEvent(events: FileSystemEvents) {
|
|
@@ -257,7 +257,7 @@ import { DocumentsExtImpl } from './documents';
|
|
|
257
257
|
import { TextEditorCursorStyle } from '../common/editor-options';
|
|
258
258
|
import { PreferenceRegistryExtImpl } from './preference-registry';
|
|
259
259
|
import { OutputChannelRegistryExtImpl } from './output-channel-registry';
|
|
260
|
-
import { TerminalServiceExtImpl
|
|
260
|
+
import { TerminalServiceExtImpl } from './terminal-ext';
|
|
261
261
|
import { LanguagesExtImpl } from './languages';
|
|
262
262
|
import { fromDocumentSelector, pluginToPluginInfo, fromGlobPattern } from './type-converters';
|
|
263
263
|
import { DialogsExtImpl } from './dialogs';
|
|
@@ -348,7 +348,7 @@ export function createAPIFactory(
|
|
|
348
348
|
const connectionExt = rpc.set(MAIN_RPC_CONTEXT.CONNECTION_EXT, new ConnectionImpl(rpc.getProxy(PLUGIN_RPC_CONTEXT.CONNECTION_MAIN)));
|
|
349
349
|
const fileSystemExt = rpc.set(MAIN_RPC_CONTEXT.FILE_SYSTEM_EXT, new FileSystemExtImpl(rpc));
|
|
350
350
|
const languagesExt = rpc.set(MAIN_RPC_CONTEXT.LANGUAGES_EXT, new LanguagesExtImpl(rpc, documents, commandRegistry, fileSystemExt));
|
|
351
|
-
const extHostFileSystemEvent = rpc.set(MAIN_RPC_CONTEXT.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpc, editorsAndDocumentsExt));
|
|
351
|
+
const extHostFileSystemEvent = rpc.set(MAIN_RPC_CONTEXT.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpc, editorsAndDocumentsExt, workspaceExt));
|
|
352
352
|
const scmExt = rpc.set(MAIN_RPC_CONTEXT.SCM_EXT, new ScmExtImpl(rpc, commandRegistry));
|
|
353
353
|
const decorationsExt = rpc.set(MAIN_RPC_CONTEXT.DECORATIONS_EXT, new DecorationsExtImpl(rpc));
|
|
354
354
|
const labelServiceExt = rpc.set(MAIN_RPC_CONTEXT.LABEL_SERVICE_EXT, new LabelServiceExtImpl(rpc));
|
|
@@ -462,7 +462,7 @@ export function createAPIFactory(
|
|
|
462
462
|
const showErrorMessage = messageRegistryExt.showMessage.bind(messageRegistryExt, MainMessageType.Error);
|
|
463
463
|
const window: typeof theia.window = {
|
|
464
464
|
|
|
465
|
-
get activeTerminal():
|
|
465
|
+
get activeTerminal(): theia.Terminal | undefined {
|
|
466
466
|
return terminalExt.activeTerminal;
|
|
467
467
|
},
|
|
468
468
|
get activeTextEditor(): TextEditorExt | undefined {
|
|
@@ -471,7 +471,7 @@ export function createAPIFactory(
|
|
|
471
471
|
get visibleTextEditors(): theia.TextEditor[] {
|
|
472
472
|
return editors.getVisibleTextEditors();
|
|
473
473
|
},
|
|
474
|
-
get terminals():
|
|
474
|
+
get terminals(): theia.Terminal[] {
|
|
475
475
|
return terminalExt.terminals;
|
|
476
476
|
},
|
|
477
477
|
onDidChangeActiveTerminal,
|
|
@@ -633,7 +633,7 @@ export function createAPIFactory(
|
|
|
633
633
|
createTerminal(nameOrOptions: theia.TerminalOptions | theia.ExtensionTerminalOptions | theia.ExtensionTerminalOptions | (string | undefined),
|
|
634
634
|
shellPath?: string,
|
|
635
635
|
shellArgs?: string[] | string): theia.Terminal {
|
|
636
|
-
return
|
|
636
|
+
return terminalExt.createTerminal(plugin, nameOrOptions, shellPath, shellArgs, createAPIObject);
|
|
637
637
|
},
|
|
638
638
|
onDidChangeTerminalState,
|
|
639
639
|
onDidCloseTerminal,
|
|
@@ -1286,8 +1286,8 @@ export function createAPIFactory(
|
|
|
1286
1286
|
throw new Error('Input box not found!');
|
|
1287
1287
|
}
|
|
1288
1288
|
},
|
|
1289
|
-
createSourceControl(id: string, label: string, rootUri?: URI, iconPath?: theia.IconPath, parent?: theia.SourceControl): theia.SourceControl {
|
|
1290
|
-
return
|
|
1289
|
+
createSourceControl(id: string, label: string, rootUri?: URI, iconPath?: theia.IconPath, isHidden?: boolean, parent?: theia.SourceControl): theia.SourceControl {
|
|
1290
|
+
return scmExt.createSourceControl(plugin, id, label, rootUri, iconPath, isHidden, parent);
|
|
1291
1291
|
}
|
|
1292
1292
|
};
|
|
1293
1293
|
|