@theia/vsx-registry 1.60.2 → 1.61.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/README.md +1 -1
- package/lib/browser/vsx-extension-commands.d.ts +2 -0
- package/lib/browser/vsx-extension-commands.d.ts.map +1 -1
- package/lib/browser/vsx-extension-commands.js +6 -0
- package/lib/browser/vsx-extension-commands.js.map +1 -1
- package/lib/browser/vsx-extension-editor-manager.d.ts +3 -2
- package/lib/browser/vsx-extension-editor-manager.d.ts.map +1 -1
- package/lib/browser/vsx-extension-editor-manager.js.map +1 -1
- package/lib/browser/vsx-extension.d.ts +16 -2
- package/lib/browser/vsx-extension.d.ts.map +1 -1
- package/lib/browser/vsx-extension.js +80 -49
- package/lib/browser/vsx-extension.js.map +1 -1
- package/lib/browser/vsx-extensions-contribution.d.ts.map +1 -1
- package/lib/browser/vsx-extensions-contribution.js +18 -0
- package/lib/browser/vsx-extensions-contribution.js.map +1 -1
- package/lib/browser/vsx-extensions-model.d.ts +14 -2
- package/lib/browser/vsx-extensions-model.d.ts.map +1 -1
- package/lib/browser/vsx-extensions-model.js +88 -19
- package/lib/browser/vsx-extensions-model.js.map +1 -1
- package/lib/browser/vsx-language-quick-pick-service.js +1 -1
- package/lib/browser/vsx-language-quick-pick-service.js.map +1 -1
- package/package.json +11 -11
- package/src/browser/style/index.css +27 -41
- package/src/browser/vsx-extension-commands.ts +8 -0
- package/src/browser/vsx-extension-editor-manager.ts +1 -2
- package/src/browser/vsx-extension.tsx +103 -52
- package/src/browser/vsx-extensions-contribution.ts +21 -0
- package/src/browser/vsx-extensions-model.ts +95 -23
- package/src/browser/vsx-language-quick-pick-service.ts +1 -1
|
@@ -21,7 +21,7 @@ import URI from '@theia/core/lib/common/uri';
|
|
|
21
21
|
import { TreeElement, TreeElementNode } from '@theia/core/lib/browser/source-tree';
|
|
22
22
|
import { OpenerService, open, OpenerOptions } from '@theia/core/lib/browser/opener-service';
|
|
23
23
|
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
|
24
|
-
import { PluginServer, DeployedPlugin,
|
|
24
|
+
import { PluginServer, DeployedPlugin, PluginIdentifiers, PluginDeployOptions } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
|
25
25
|
import { VSCodeExtensionUri } from '@theia/plugin-ext-vscode/lib/common/plugin-vscode-uri';
|
|
26
26
|
import { ProgressService } from '@theia/core/lib/common/progress-service';
|
|
27
27
|
import { Endpoint } from '@theia/core/lib/browser/endpoint';
|
|
@@ -32,13 +32,16 @@ import { codicon, ConfirmDialog, ContextMenuRenderer, HoverService, TreeWidget }
|
|
|
32
32
|
import { VSXExtensionNamespaceAccess, VSXUser } from '@theia/ovsx-client/lib/ovsx-types';
|
|
33
33
|
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
|
34
34
|
import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
|
|
35
|
+
import { VSXExtensionsModel } from './vsx-extensions-model';
|
|
35
36
|
|
|
36
37
|
export const EXTENSIONS_CONTEXT_MENU: MenuPath = ['extensions_context_menu'];
|
|
37
38
|
|
|
38
39
|
export namespace VSXExtensionsContextMenu {
|
|
39
40
|
export const INSTALL = [...EXTENSIONS_CONTEXT_MENU, '1_install'];
|
|
40
|
-
export const
|
|
41
|
-
export const
|
|
41
|
+
export const DISABLE = [...EXTENSIONS_CONTEXT_MENU, '2_disable'];
|
|
42
|
+
export const ENABLE = [...EXTENSIONS_CONTEXT_MENU, '2_enable'];
|
|
43
|
+
export const COPY = [...EXTENSIONS_CONTEXT_MENU, '3_copy'];
|
|
44
|
+
export const CONTRIBUTION = [...EXTENSIONS_CONTEXT_MENU, '4_contribution'];
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
@injectable()
|
|
@@ -86,6 +89,8 @@ export class VSXExtensionData {
|
|
|
86
89
|
@injectable()
|
|
87
90
|
export class VSXExtensionOptions {
|
|
88
91
|
readonly id: string;
|
|
92
|
+
readonly version?: string;
|
|
93
|
+
readonly model: VSXExtensionsModel;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
export const VSXExtensionFactory = Symbol('VSXExtensionFactory');
|
|
@@ -153,8 +158,16 @@ export class VSXExtension implements VSXExtensionData, TreeElement {
|
|
|
153
158
|
return this.options.id;
|
|
154
159
|
}
|
|
155
160
|
|
|
161
|
+
get installedVersion(): string | undefined {
|
|
162
|
+
return this.plugin?.metadata.model.version || this.options.version;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get model(): VSXExtensionsModel {
|
|
166
|
+
return this.options.model;
|
|
167
|
+
}
|
|
168
|
+
|
|
156
169
|
get visible(): boolean {
|
|
157
|
-
return
|
|
170
|
+
return true;
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
get plugin(): DeployedPlugin | undefined {
|
|
@@ -162,11 +175,23 @@ export class VSXExtension implements VSXExtensionData, TreeElement {
|
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
get installed(): boolean {
|
|
165
|
-
return
|
|
178
|
+
return this.model.isInstalled(this.id);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
get uninstalled(): boolean {
|
|
182
|
+
return this.model.isUninstalled(this.id);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
get deployed(): boolean {
|
|
186
|
+
return this.model.isDeployed(this.id);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
get disabled(): boolean {
|
|
190
|
+
return this.model.isDisabled(this.id);
|
|
166
191
|
}
|
|
167
192
|
|
|
168
193
|
get builtin(): boolean {
|
|
169
|
-
return this.
|
|
194
|
+
return this.model.isBuiltIn(this.id);
|
|
170
195
|
}
|
|
171
196
|
|
|
172
197
|
update(data: Partial<VSXExtensionData>): void {
|
|
@@ -298,9 +323,23 @@ export class VSXExtension implements VSXExtensionData, TreeElement {
|
|
|
298
323
|
return md;
|
|
299
324
|
}
|
|
300
325
|
|
|
301
|
-
protected
|
|
302
|
-
get
|
|
303
|
-
return
|
|
326
|
+
protected _currentTaskName: string | undefined;
|
|
327
|
+
get currentTask(): string | undefined {
|
|
328
|
+
return this._currentTaskName;
|
|
329
|
+
}
|
|
330
|
+
protected _currentTask: Promise<void> | undefined;
|
|
331
|
+
|
|
332
|
+
protected runTask(name: string, task: () => Promise<void>): Promise<void> {
|
|
333
|
+
if (this._currentTask) {
|
|
334
|
+
return Promise.reject('busy');
|
|
335
|
+
}
|
|
336
|
+
this._currentTaskName = name;
|
|
337
|
+
this._currentTask = task();
|
|
338
|
+
this._currentTask.finally(() => {
|
|
339
|
+
this._currentTask = undefined;
|
|
340
|
+
this._currentTaskName = undefined;
|
|
341
|
+
});
|
|
342
|
+
return this._currentTask;
|
|
304
343
|
}
|
|
305
344
|
|
|
306
345
|
async install(options?: PluginDeployOptions): Promise<void> {
|
|
@@ -310,39 +349,56 @@ export class VSXExtension implements VSXExtensionData, TreeElement {
|
|
|
310
349
|
msg: nls.localize('theia/vsx-registry/confirmDialogMessage', 'The extension "{0}" is unverified and might pose a security risk.', this.displayName)
|
|
311
350
|
}).open();
|
|
312
351
|
if (choice) {
|
|
313
|
-
this.doInstall(options);
|
|
352
|
+
await this.doInstall(options);
|
|
314
353
|
}
|
|
315
354
|
} else {
|
|
316
|
-
this.doInstall(options);
|
|
355
|
+
await this.doInstall(options);
|
|
317
356
|
}
|
|
318
357
|
}
|
|
319
358
|
|
|
320
359
|
async uninstall(): Promise<void> {
|
|
321
|
-
this
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
await this.progressService.withProgress(
|
|
360
|
+
const { id, installedVersion } = this;
|
|
361
|
+
if (id && installedVersion) {
|
|
362
|
+
await this.runTask(nls.localizeByDefault('Uninstalling'),
|
|
363
|
+
async () => await this.progressService.withProgress(
|
|
326
364
|
nls.localizeByDefault('Uninstalling {0}...', this.id), 'extensions',
|
|
327
|
-
() => this.pluginServer.uninstall(PluginIdentifiers.
|
|
365
|
+
() => this.pluginServer.uninstall(PluginIdentifiers.idAndVersionToVersionedId({ id: (id as PluginIdentifiers.UnversionedId), version: installedVersion }))
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async disable(): Promise<void> {
|
|
372
|
+
const { id, installedVersion } = this;
|
|
373
|
+
if (id && installedVersion) {
|
|
374
|
+
await this.runTask(nls.localize('vsx.disabling', 'Disabling'), async () => {
|
|
375
|
+
await this.progressService.withProgress(
|
|
376
|
+
nls.localize('vsx.disabling.extensions', 'Disabling {0}...', this.id), 'extensions',
|
|
377
|
+
() => this.pluginServer.disablePlugin(PluginIdentifiers.idAndVersionToVersionedId({ id: (id as PluginIdentifiers.UnversionedId), version: installedVersion }))
|
|
328
378
|
);
|
|
329
|
-
}
|
|
330
|
-
} finally {
|
|
331
|
-
this._busy--;
|
|
379
|
+
});
|
|
332
380
|
}
|
|
333
381
|
}
|
|
334
382
|
|
|
335
|
-
|
|
336
|
-
this
|
|
337
|
-
|
|
338
|
-
await this.
|
|
339
|
-
this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
383
|
+
async enable(): Promise<void> {
|
|
384
|
+
const { id, installedVersion } = this;
|
|
385
|
+
if (id && installedVersion) {
|
|
386
|
+
await this.runTask(nls.localize('vsx.enabling', 'Enabling'), async () => {
|
|
387
|
+
await this.progressService.withProgress(
|
|
388
|
+
nls.localize('vsx.enabling.extension', 'Enabling {0}...', this.id), 'extensions',
|
|
389
|
+
() => this.pluginServer.enablePlugin(PluginIdentifiers.idAndVersionToVersionedId({ id: (id as PluginIdentifiers.UnversionedId), version: installedVersion }))
|
|
390
|
+
);
|
|
391
|
+
});
|
|
343
392
|
}
|
|
344
393
|
}
|
|
345
394
|
|
|
395
|
+
protected async doInstall(options?: PluginDeployOptions): Promise<void> {
|
|
396
|
+
await this.runTask(nls.localizeByDefault('Installing'),
|
|
397
|
+
() => this.progressService.withProgress(nls.localizeByDefault("Installing extension '{0}' v{1}...", this.id, this.version ?? 0), 'extensions', () =>
|
|
398
|
+
this.pluginServer.install(this.uri.toString(), undefined, options)
|
|
399
|
+
));
|
|
400
|
+
}
|
|
401
|
+
|
|
346
402
|
handleContextMenu(e: React.MouseEvent<HTMLElement, MouseEvent>): void {
|
|
347
403
|
e.preventDefault();
|
|
348
404
|
this.contextMenuRenderer.render({
|
|
@@ -435,32 +491,24 @@ export abstract class AbstractVSXExtensionComponent<Props extends AbstractVSXExt
|
|
|
435
491
|
};
|
|
436
492
|
|
|
437
493
|
protected renderAction(host?: TreeWidget): React.ReactNode {
|
|
438
|
-
const { builtin,
|
|
494
|
+
const { builtin, currentTask, disabled, uninstalled, installed, deployed } = this.props.extension;
|
|
439
495
|
const isFocused = (host?.model.getFocusedNode() as TreeElementNode)?.element === this.props.extension;
|
|
440
496
|
const tabIndex = (!host || isFocused) ? 0 : undefined;
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
return <div className="codicon codicon-settings-gear action" tabIndex={tabIndex} onClick={this.manage}></div>;
|
|
497
|
+
const outOfSync = installed && (deployed ? (disabled || uninstalled) : !(disabled || uninstalled));
|
|
498
|
+
if (currentTask) {
|
|
499
|
+
return <button className="theia-button action prominent theia-mod-disabled">{currentTask}</button>;
|
|
445
500
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
501
|
+
return <div>
|
|
502
|
+
{
|
|
503
|
+
outOfSync && <button className="theia-button action" onClick={this.reloadWindow}>{nls.localizeByDefault('Reload Window')}</button>
|
|
449
504
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
: <button className="theia-button action" onClick={this.uninstall}>{nls.localizeByDefault('Uninstall')}</button>
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
<div className="codicon codicon-settings-gear action" onClick={this.manage}></div>
|
|
461
|
-
</div>;
|
|
462
|
-
}
|
|
463
|
-
return <button className="theia-button prominent action" onClick={this.install}>{nls.localizeByDefault('Install')}</button>;
|
|
505
|
+
{
|
|
506
|
+
!builtin && ((installed && !uninstalled) ?
|
|
507
|
+
<button className="theia-button action" onClick={this.uninstall}>{nls.localizeByDefault('Uninstall')}</button> :
|
|
508
|
+
<button className="theia-button prominent action" onClick={this.install}>{nls.localizeByDefault('Install')}</button>)
|
|
509
|
+
}
|
|
510
|
+
<div className="codicon codicon-settings-gear action" tabIndex={tabIndex} onClick={this.manage}></div>
|
|
511
|
+
</div>;
|
|
464
512
|
}
|
|
465
513
|
|
|
466
514
|
}
|
|
@@ -486,7 +534,7 @@ export namespace VSXExtensionComponent {
|
|
|
486
534
|
|
|
487
535
|
export class VSXExtensionComponent<Props extends VSXExtensionComponent.Props = VSXExtensionComponent.Props> extends AbstractVSXExtensionComponent<Props> {
|
|
488
536
|
override render(): React.ReactNode {
|
|
489
|
-
const { iconUrl, publisher, displayName, description, version, downloadCount, averageRating, tooltip, verified } = this.props.extension;
|
|
537
|
+
const { iconUrl, publisher, displayName, description, version, downloadCount, averageRating, tooltip, verified, disabled } = this.props.extension;
|
|
490
538
|
|
|
491
539
|
return <div
|
|
492
540
|
className='theia-vsx-extension noselect'
|
|
@@ -509,7 +557,9 @@ export class VSXExtensionComponent<Props extends VSXExtensionComponent.Props = V
|
|
|
509
557
|
<div className='theia-vsx-extension-content'>
|
|
510
558
|
<div className='title'>
|
|
511
559
|
<div className='noWrapInfo'>
|
|
512
|
-
<span className='name'>{displayName}</span
|
|
560
|
+
<span className='name'>{displayName}</span>
|
|
561
|
+
<span className='version'>{VSXExtension.formatVersion(version)}
|
|
562
|
+
</span>{disabled && <span className='disabled'>({nls.localizeByDefault('disabled')})</span>}
|
|
513
563
|
</div>
|
|
514
564
|
<div className='stat'>
|
|
515
565
|
{!!downloadCount && <span className='download-count'><i className={codicon('cloud-download')} />{downloadCompactFormatter.format(downloadCount)}</span>}
|
|
@@ -517,6 +567,7 @@ export class VSXExtensionComponent<Props extends VSXExtensionComponent.Props = V
|
|
|
517
567
|
</div>
|
|
518
568
|
</div>
|
|
519
569
|
<div className='noWrapInfo theia-vsx-extension-description'>{description}</div>
|
|
570
|
+
|
|
520
571
|
<div className='theia-vsx-extension-action-bar'>
|
|
521
572
|
<div className='theia-vsx-extension-publisher-container'>
|
|
522
573
|
{verified === true ? (
|
|
@@ -114,6 +114,18 @@ export class VSXExtensionsContribution extends AbstractViewContribution<VSXExten
|
|
|
114
114
|
execute: async (extension: VSXExtension) => this.installAnotherVersion(extension),
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
commands.registerCommand(VSXExtensionsCommands.DISABLE, {
|
|
118
|
+
isVisible: (extension: VSXExtension) => extension.installed && !extension.disabled,
|
|
119
|
+
isEnabled: (extension: VSXExtension) => extension.installed && !extension.disabled,
|
|
120
|
+
execute: async (extension: VSXExtension) => extension.disable(),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
commands.registerCommand(VSXExtensionsCommands.ENABLE, {
|
|
124
|
+
isVisible: (extension: VSXExtension) => extension.installed && extension.disabled,
|
|
125
|
+
isEnabled: (extension: VSXExtension) => extension.installed && extension.disabled,
|
|
126
|
+
execute: async (extension: VSXExtension) => extension.enable(),
|
|
127
|
+
});
|
|
128
|
+
|
|
117
129
|
commands.registerCommand(VSXExtensionsCommands.COPY, {
|
|
118
130
|
execute: (extension: VSXExtension) => this.copy(extension)
|
|
119
131
|
});
|
|
@@ -152,6 +164,15 @@ export class VSXExtensionsContribution extends AbstractViewContribution<VSXExten
|
|
|
152
164
|
label: nls.localizeByDefault('Copy Extension ID'),
|
|
153
165
|
order: '1'
|
|
154
166
|
});
|
|
167
|
+
menus.registerMenuAction(VSXExtensionsContextMenu.DISABLE, {
|
|
168
|
+
commandId: VSXExtensionsCommands.DISABLE.id,
|
|
169
|
+
label: nls.localizeByDefault('Disable')
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
menus.registerMenuAction(VSXExtensionsContextMenu.ENABLE, {
|
|
173
|
+
commandId: VSXExtensionsCommands.ENABLE.id,
|
|
174
|
+
label: nls.localizeByDefault('Enable')
|
|
175
|
+
});
|
|
155
176
|
menus.registerMenuAction(VSXExtensionsContextMenu.INSTALL, {
|
|
156
177
|
commandId: VSXExtensionsCommands.INSTALL_ANOTHER_VERSION.id,
|
|
157
178
|
label: nls.localizeByDefault('Install Specific Version...'),
|
|
@@ -34,19 +34,24 @@ import { RequestContext, RequestService } from '@theia/core/shared/@theia/reques
|
|
|
34
34
|
import { OVSXApiFilterProvider } from '@theia/ovsx-client';
|
|
35
35
|
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
|
|
36
36
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
37
|
+
import { HostedPluginServer, PluginIdentifiers, PluginType } from '@theia/plugin-ext';
|
|
38
|
+
import { HostedPluginWatcher } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin-watcher';
|
|
37
39
|
|
|
38
40
|
@injectable()
|
|
39
41
|
export class VSXExtensionsModel {
|
|
40
|
-
|
|
41
42
|
protected initialized: Promise<void>;
|
|
42
43
|
/**
|
|
43
44
|
* Single source for all extensions
|
|
44
45
|
*/
|
|
45
46
|
protected readonly extensions = new Map<string, VSXExtension>();
|
|
46
47
|
protected readonly onDidChangeEmitter = new Emitter<void>();
|
|
47
|
-
protected
|
|
48
|
+
protected disabled = new Set<PluginIdentifiers.UnversionedId>();
|
|
49
|
+
protected uninstalled = new Set<PluginIdentifiers.UnversionedId>();
|
|
50
|
+
protected deployed = new Set<PluginIdentifiers.UnversionedId>();
|
|
51
|
+
protected _installed = new Set<PluginIdentifiers.UnversionedId>();
|
|
48
52
|
protected _recommended = new Set<string>();
|
|
49
53
|
protected _searchResult = new Set<string>();
|
|
54
|
+
protected builtins = new Set<PluginIdentifiers.UnversionedId>();
|
|
50
55
|
protected _searchError?: string;
|
|
51
56
|
|
|
52
57
|
protected searchCancellationTokenSource = new CancellationTokenSource();
|
|
@@ -61,6 +66,12 @@ export class VSXExtensionsModel {
|
|
|
61
66
|
@inject(HostedPluginSupport)
|
|
62
67
|
protected readonly pluginSupport: HostedPluginSupport;
|
|
63
68
|
|
|
69
|
+
@inject(HostedPluginWatcher)
|
|
70
|
+
protected pluginWatcher: HostedPluginWatcher;
|
|
71
|
+
|
|
72
|
+
@inject(HostedPluginServer)
|
|
73
|
+
protected readonly pluginServer: HostedPluginServer;
|
|
74
|
+
|
|
64
75
|
@inject(VSXExtensionFactory)
|
|
65
76
|
protected readonly extensionFactory: VSXExtensionFactory;
|
|
66
77
|
|
|
@@ -128,8 +139,24 @@ export class VSXExtensionsModel {
|
|
|
128
139
|
this.updateSearchResult();
|
|
129
140
|
}
|
|
130
141
|
|
|
142
|
+
isBuiltIn(id: string): boolean {
|
|
143
|
+
return this.builtins.has(id as PluginIdentifiers.UnversionedId);
|
|
144
|
+
}
|
|
145
|
+
|
|
131
146
|
isInstalled(id: string): boolean {
|
|
132
|
-
return this._installed.has(id);
|
|
147
|
+
return this._installed.has(id as PluginIdentifiers.UnversionedId);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
isUninstalled(id: string): boolean {
|
|
151
|
+
return this.uninstalled.has(id as PluginIdentifiers.UnversionedId);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
isDeployed(id: string): boolean {
|
|
155
|
+
return this.deployed.has(id as PluginIdentifiers.UnversionedId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
isDisabled(id: string): boolean {
|
|
159
|
+
return this.disabled.has(id as PluginIdentifiers.UnversionedId);
|
|
133
160
|
}
|
|
134
161
|
|
|
135
162
|
getExtension(id: string): VSXExtension | undefined {
|
|
@@ -187,12 +214,15 @@ export class VSXExtensionsModel {
|
|
|
187
214
|
|
|
188
215
|
protected async initInstalled(): Promise<void> {
|
|
189
216
|
await this.pluginSupport.willStart;
|
|
190
|
-
this.pluginSupport.onDidChangePlugins(() => this.updateInstalled());
|
|
191
217
|
try {
|
|
192
218
|
await this.updateInstalled();
|
|
193
219
|
} catch (e) {
|
|
194
220
|
console.error(e);
|
|
195
221
|
}
|
|
222
|
+
|
|
223
|
+
this.pluginWatcher.onDidDeploy(() => {
|
|
224
|
+
this.updateInstalled();
|
|
225
|
+
});
|
|
196
226
|
}
|
|
197
227
|
|
|
198
228
|
protected async initSearchResult(): Promise<void> {
|
|
@@ -223,10 +253,10 @@ export class VSXExtensionsModel {
|
|
|
223
253
|
return this.searchCancellationTokenSource = new CancellationTokenSource();
|
|
224
254
|
}
|
|
225
255
|
|
|
226
|
-
protected setExtension(id: string): VSXExtension {
|
|
256
|
+
protected setExtension(id: string, version?: string): VSXExtension {
|
|
227
257
|
let extension = this.extensions.get(id);
|
|
228
258
|
if (!extension) {
|
|
229
|
-
extension = this.extensionFactory({ id });
|
|
259
|
+
extension = this.extensionFactory({ id, version, model: this });
|
|
230
260
|
this.extensions.set(id, extension);
|
|
231
261
|
}
|
|
232
262
|
return extension;
|
|
@@ -328,19 +358,41 @@ export class VSXExtensionsModel {
|
|
|
328
358
|
}
|
|
329
359
|
|
|
330
360
|
protected async updateInstalled(): Promise<void> {
|
|
361
|
+
const [deployed, uninstalled, disabled] = await Promise.all(
|
|
362
|
+
[this.pluginServer.getDeployedPluginIds(), this.pluginServer.getUninstalledPluginIds(), this.pluginServer.getDisabledPluginIds()]);
|
|
363
|
+
|
|
364
|
+
this.uninstalled = new Set();
|
|
365
|
+
uninstalled.forEach(id => this.uninstalled.add(PluginIdentifiers.unversionedFromVersioned(id)));
|
|
366
|
+
this.disabled = new Set();
|
|
367
|
+
disabled.forEach(id => this.disabled.add(PluginIdentifiers.unversionedFromVersioned(id)));
|
|
368
|
+
this.deployed = new Set();
|
|
369
|
+
deployed.forEach(id => this.deployed.add(PluginIdentifiers.unversionedFromVersioned(id)));
|
|
370
|
+
|
|
331
371
|
const prevInstalled = this._installed;
|
|
372
|
+
const installedVersioned = new Set<PluginIdentifiers.VersionedId>();
|
|
332
373
|
return this.doChange(async () => {
|
|
333
|
-
const
|
|
334
|
-
const currInstalled = new Set<string>();
|
|
374
|
+
const currInstalled = new Set<PluginIdentifiers.UnversionedId>();
|
|
335
375
|
const refreshing = [];
|
|
336
|
-
for (const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
this._installed.delete(id);
|
|
341
|
-
|
|
342
|
-
currInstalled.add(
|
|
343
|
-
refreshing.push(this.refresh(id, version));
|
|
376
|
+
for (const versionedId of deployed) {
|
|
377
|
+
installedVersioned.add(versionedId);
|
|
378
|
+
const idAndVersion = PluginIdentifiers.idAndVersionFromVersionedId(versionedId);
|
|
379
|
+
if (idAndVersion) {
|
|
380
|
+
this._installed.delete(idAndVersion.id);
|
|
381
|
+
this.setExtension(idAndVersion.id, idAndVersion.version);
|
|
382
|
+
currInstalled.add(idAndVersion.id);
|
|
383
|
+
refreshing.push(this.refresh(idAndVersion.id, idAndVersion.version));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
for (const versionedId of disabled) {
|
|
387
|
+
const idAndVersion = PluginIdentifiers.idAndVersionFromVersionedId(versionedId);
|
|
388
|
+
installedVersioned.add(versionedId);
|
|
389
|
+
if (idAndVersion && !this.isUninstalled(idAndVersion.id)) {
|
|
390
|
+
if (!currInstalled.has(idAndVersion.id)) {
|
|
391
|
+
this._installed.delete(idAndVersion.id);
|
|
392
|
+
this.setExtension(idAndVersion.id, idAndVersion.version);
|
|
393
|
+
currInstalled.add(idAndVersion.id);
|
|
394
|
+
refreshing.push(this.refresh(idAndVersion.id, idAndVersion.version));
|
|
395
|
+
}
|
|
344
396
|
}
|
|
345
397
|
}
|
|
346
398
|
for (const id of this._installed) {
|
|
@@ -348,10 +400,33 @@ export class VSXExtensionsModel {
|
|
|
348
400
|
if (!extension) { continue; }
|
|
349
401
|
refreshing.push(this.refresh(id, extension.version));
|
|
350
402
|
}
|
|
403
|
+
await Promise.all(refreshing);
|
|
351
404
|
const installed = new Set([...prevInstalled, ...currInstalled]);
|
|
352
405
|
const installedSorted = Array.from(installed).sort((a, b) => this.compareExtensions(a, b));
|
|
353
406
|
this._installed = new Set(installedSorted.values());
|
|
354
|
-
|
|
407
|
+
|
|
408
|
+
const missingIds = new Set<PluginIdentifiers.VersionedId>();
|
|
409
|
+
for (const id of installedVersioned) {
|
|
410
|
+
const unversionedId = PluginIdentifiers.unversionedFromVersioned(id);
|
|
411
|
+
const plugin = this.pluginSupport.getPlugin(unversionedId);
|
|
412
|
+
if (plugin) {
|
|
413
|
+
if (plugin.type === PluginType.System) {
|
|
414
|
+
this.builtins.add(unversionedId);
|
|
415
|
+
} else {
|
|
416
|
+
this.builtins.delete(unversionedId);
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
missingIds.add(id);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const missing = await this.pluginServer.getDeployedPlugins([...missingIds.values()]);
|
|
423
|
+
for (const plugin of missing) {
|
|
424
|
+
if (plugin.type === PluginType.System) {
|
|
425
|
+
this.builtins.add(PluginIdentifiers.componentsToUnversionedId(plugin.metadata.model));
|
|
426
|
+
} else {
|
|
427
|
+
this.builtins.delete(PluginIdentifiers.componentsToUnversionedId(plugin.metadata.model));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
355
430
|
});
|
|
356
431
|
}
|
|
357
432
|
|
|
@@ -362,7 +437,7 @@ export class VSXExtensionsModel {
|
|
|
362
437
|
|
|
363
438
|
const updateRecommendationsForScope = (scope: PreferenceInspectionScope, root?: URI) => {
|
|
364
439
|
const { recommendations, unwantedRecommendations } = this.getRecommendationsForScope(scope, root);
|
|
365
|
-
recommendations.forEach(recommendation => allRecommendations.add(recommendation));
|
|
440
|
+
recommendations.forEach(recommendation => allRecommendations.add(recommendation.toLowerCase()));
|
|
366
441
|
unwantedRecommendations.forEach(unwantedRecommendation => allUnwantedRecommendations.add(unwantedRecommendation));
|
|
367
442
|
};
|
|
368
443
|
|
|
@@ -449,15 +524,12 @@ export class VSXExtensionsModel {
|
|
|
449
524
|
* @param extension the extension to refresh.
|
|
450
525
|
*/
|
|
451
526
|
protected shouldRefresh(extension?: VSXExtension): boolean {
|
|
452
|
-
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
|
-
return !extension.builtin;
|
|
527
|
+
return extension === undefined || extension.plugin === undefined;
|
|
456
528
|
}
|
|
457
529
|
|
|
458
530
|
protected onDidFailRefresh(id: string, error: unknown): VSXExtension | undefined {
|
|
459
531
|
const cached = this.getExtension(id);
|
|
460
|
-
if (cached && cached.
|
|
532
|
+
if (cached && cached.deployed) {
|
|
461
533
|
return cached;
|
|
462
534
|
}
|
|
463
535
|
console.error(`[${id}]: failed to refresh, reason:`, error);
|
|
@@ -71,7 +71,7 @@ export class VSXLanguageQuickPickService extends LanguageQuickPickService {
|
|
|
71
71
|
});
|
|
72
72
|
try {
|
|
73
73
|
const extensionUri = VSCodeExtensionUri.fromId(`${extension.extension.namespace}.${extension.extension.name}`).toString();
|
|
74
|
-
await this.pluginServer.
|
|
74
|
+
await this.pluginServer.install(extensionUri);
|
|
75
75
|
} finally {
|
|
76
76
|
progress.cancel();
|
|
77
77
|
}
|