Merge from vscode ec07311dab2556c9d66a4cb3eecdc21c524202e1 (#6739)

This commit is contained in:
Anthony Dresser
2019-08-13 19:14:03 -07:00
committed by GitHub
parent a645a09f42
commit 82c1c57c76
77 changed files with 1319 additions and 741 deletions

View File

@@ -134,29 +134,33 @@ interface IActiveElement {
focus(): void;
}
interface IExtensionEditorTemplate {
iconContainer: HTMLElement;
icon: HTMLImageElement;
name: HTMLElement;
identifier: HTMLElement;
preview: HTMLElement;
builtin: HTMLElement;
license: HTMLElement;
publisher: HTMLElement;
installCount: HTMLElement;
rating: HTMLElement;
repository: HTMLElement;
description: HTMLElement;
extensionActionBar: ActionBar;
navbar: NavBar;
content: HTMLElement;
subtextContainer: HTMLElement;
subtext: HTMLElement;
ignoreActionbar: ActionBar;
header: HTMLElement;
}
export class ExtensionEditor extends BaseEditor {
static readonly ID: string = 'workbench.editor.extension';
private iconContainer: HTMLElement;
private icon: HTMLImageElement;
private name: HTMLElement;
private identifier: HTMLElement;
private preview: HTMLElement;
private builtin: HTMLElement;
private license: HTMLElement;
private publisher: HTMLElement;
private installCount: HTMLElement;
private rating: HTMLElement;
private repository: HTMLElement;
private description: HTMLElement;
private extensionActionBar: ActionBar;
private navbar: NavBar;
private content: HTMLElement;
private subtextContainer: HTMLElement;
private subtext: HTMLElement;
private ignoreActionbar: ActionBar;
private header: HTMLElement;
private template: IExtensionEditorTemplate | undefined;
private extensionReadme: Cache<string> | null;
private extensionChangelog: Cache<string> | null;
@@ -165,7 +169,7 @@ export class ExtensionEditor extends BaseEditor {
private layoutParticipants: ILayoutParticipant[] = [];
private readonly contentDisposables = this._register(new DisposableStore());
private readonly transientDisposables = this._register(new DisposableStore());
private activeElement: IActiveElement | null;
private activeElement: IActiveElement | null = null;
private editorLoadComplete: boolean = false;
constructor(
@@ -193,43 +197,43 @@ export class ExtensionEditor extends BaseEditor {
const root = append(parent, $('.extension-editor'));
root.tabIndex = 0; // this is required for the focus tracker on the editor
root.style.outline = 'none';
this.header = append(root, $('.header'));
const header = append(root, $('.header'));
this.iconContainer = append(this.header, $('.icon-container'));
this.icon = append(this.iconContainer, $<HTMLImageElement>('img.icon', { draggable: false }));
const iconContainer = append(header, $('.icon-container'));
const icon = append(iconContainer, $<HTMLImageElement>('img.icon', { draggable: false }));
const details = append(this.header, $('.details'));
const details = append(header, $('.details'));
const title = append(details, $('.title'));
this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
const identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
this.preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
this.preview.textContent = localize('preview', "Preview");
const preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
preview.textContent = localize('preview', "Preview");
this.builtin = append(title, $('span.builtin'));
this.builtin.textContent = localize('builtin', "Built-in");
const builtin = append(title, $('span.builtin'));
builtin.textContent = localize('builtin', "Built-in");
const subtitle = append(details, $('.subtitle'));
this.publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 }));
const publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 }));
this.installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 }));
const installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 }));
this.rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 }));
const rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 }));
this.repository = append(subtitle, $('span.repository.clickable'));
this.repository.textContent = localize('repository', 'Repository');
this.repository.style.display = 'none';
this.repository.tabIndex = 0;
const repository = append(subtitle, $('span.repository.clickable'));
repository.textContent = localize('repository', 'Repository');
repository.style.display = 'none';
repository.tabIndex = 0;
this.license = append(subtitle, $('span.license.clickable'));
this.license.textContent = localize('license', 'License');
this.license.style.display = 'none';
this.license.tabIndex = 0;
const license = append(subtitle, $('span.license.clickable'));
license.textContent = localize('license', 'License');
license.style.display = 'none';
license.tabIndex = 0;
this.description = append(details, $('.description'));
const description = append(details, $('.description'));
const extensionActions = append(details, $('.actions'));
this.extensionActionBar = new ActionBar(extensionActions, {
const extensionActionBar = this._register(new ActionBar(extensionActions, {
animated: false,
actionViewItemProvider: (action: Action) => {
if (action instanceof ExtensionEditorDropDownAction) {
@@ -237,29 +241,48 @@ export class ExtensionEditor extends BaseEditor {
}
return undefined;
}
});
}));
this.subtextContainer = append(details, $('.subtext-container'));
this.subtext = append(this.subtextContainer, $('.subtext'));
this.ignoreActionbar = new ActionBar(this.subtextContainer, { animated: false });
const subtextContainer = append(details, $('.subtext-container'));
const subtext = append(subtextContainer, $('.subtext'));
const ignoreActionbar = this._register(new ActionBar(subtextContainer, { animated: false }));
this._register(this.extensionActionBar);
this._register(this.ignoreActionbar);
this._register(Event.chain(this.extensionActionBar.onDidRun)
this._register(Event.chain(extensionActionBar.onDidRun)
.map(({ error }) => error)
.filter(error => !!error)
.on(this.onError, this));
this._register(Event.chain(this.ignoreActionbar.onDidRun)
this._register(Event.chain(ignoreActionbar.onDidRun)
.map(({ error }) => error)
.filter(error => !!error)
.on(this.onError, this));
const body = append(root, $('.body'));
this.navbar = new NavBar(body);
const navbar = new NavBar(body);
this.content = append(body, $('.content'));
const content = append(body, $('.content'));
this.template = {
builtin,
content,
description,
extensionActionBar,
header,
icon,
iconContainer,
identifier,
ignoreActionbar,
installCount,
license,
name,
navbar,
preview,
publisher,
rating,
repository,
subtext,
subtextContainer
};
}
private onClick(element: HTMLElement, callback: () => void): IDisposable {
@@ -277,6 +300,13 @@ export class ExtensionEditor extends BaseEditor {
}
async setInput(input: ExtensionsInput, options: EditorOptions, token: CancellationToken): Promise<void> {
if (this.template) {
await this.updateTemplate(input, this.template);
}
return super.setInput(input, options, token);
}
private async updateTemplate(input: ExtensionsInput, template: IExtensionEditorTemplate): Promise<void> {
const runningExtensions = await this.extensionService.getExtensions();
const colorThemes = await this.workbenchThemeService.getColorThemes();
const fileIconThemes = await this.workbenchThemeService.getFileIconThemes();
@@ -291,18 +321,18 @@ export class ExtensionEditor extends BaseEditor {
this.extensionChangelog = new Cache(() => createCancelablePromise(token => extension.getChangelog(token)));
this.extensionManifest = new Cache(() => createCancelablePromise(token => extension.getManifest(token)));
const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, this.iconContainer, true);
const onError = Event.once(domEvent(this.icon, 'error'));
onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
this.icon.src = extension.iconUrl;
const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, template.iconContainer, true);
const onError = Event.once(domEvent(template.icon, 'error'));
onError(() => template.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
template.icon.src = extension.iconUrl;
this.name.textContent = extension.displayName;
this.identifier.textContent = extension.identifier.id;
this.preview.style.display = extension.preview ? 'inherit' : 'none';
this.builtin.style.display = extension.type === ExtensionType.System ? 'inherit' : 'none';
template.name.textContent = extension.displayName;
template.identifier.textContent = extension.identifier.id;
template.preview.style.display = extension.preview ? 'inherit' : 'none';
template.builtin.style.display = extension.type === ExtensionType.System ? 'inherit' : 'none';
this.publisher.textContent = extension.publisherDisplayName;
this.description.textContent = extension.description;
template.publisher.textContent = extension.publisherDisplayName;
template.description.textContent = extension.description;
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
let recommendationsData = {};
@@ -320,51 +350,50 @@ export class ExtensionEditor extends BaseEditor {
*/
this.telemetryService.publicLog('extensionGallery:openExtension', assign(extension.telemetryData, recommendationsData));
toggleClass(this.name, 'clickable', !!extension.url);
toggleClass(this.publisher, 'clickable', !!extension.url);
toggleClass(this.rating, 'clickable', !!extension.url);
toggleClass(template.name, 'clickable', !!extension.url);
toggleClass(template.publisher, 'clickable', !!extension.url);
toggleClass(template.rating, 'clickable', !!extension.url);
if (extension.url) {
this.transientDisposables.add(this.onClick(this.name, () => window.open(extension.url)));
this.transientDisposables.add(this.onClick(this.rating, () => window.open(`${extension.url}#review-details`)));
this.transientDisposables.add(this.onClick(this.publisher, () => {
this.transientDisposables.add(this.onClick(template.name, () => window.open(extension.url)));
this.transientDisposables.add(this.onClick(template.rating, () => window.open(`${extension.url}#review-details`)));
this.transientDisposables.add(this.onClick(template.publisher, () => {
this.viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet as IExtensionsViewlet)
.then(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`));
}));
if (extension.licenseUrl) {
this.transientDisposables.add(this.onClick(this.license, () => window.open(extension.licenseUrl)));
this.license.style.display = 'initial';
this.transientDisposables.add(this.onClick(template.license, () => window.open(extension.licenseUrl)));
template.license.style.display = 'initial';
} else {
this.license.style.display = 'none';
template.license.style.display = 'none';
}
} else {
this.license.style.display = 'none';
template.license.style.display = 'none';
}
// {{SQL CARBON EDIT}} add license url
if (extension.licenseUrl) {
this.license.onclick = finalHandler(() => window.open(extension.licenseUrl));
this.license.style.display = 'initial';
template.license.onclick = finalHandler(() => window.open(extension.licenseUrl));
template.license.style.display = 'initial';
} else {
this.license.onclick = null;
this.license.style.display = 'none';
template.license.onclick = null;
template.license.style.display = 'none';
}
// {{SQL CARBON EDIT}} - End
if (extension.repository) {
this.transientDisposables.add(this.onClick(this.repository, () => window.open(extension.repository)));
this.repository.style.display = 'initial';
this.transientDisposables.add(this.onClick(template.repository, () => window.open(extension.repository)));
template.repository.style.display = 'initial';
}
else {
this.repository.style.display = 'none';
template.repository.style.display = 'none';
}
const widgets = [
remoteBadge,
// {{SQL CARBON EDIT}} Remove the widgets
// this.instantiationService.createInstance(InstallCountWidget, this.installCount, false),
// this.instantiationService.createInstance(RatingsWidget, this.rating, false)
// this.instantiationService.createInstance(InstallCountWidget, template.installCount, false), {{SQL CARBON EDIT}} Remove the widgets
// this.instantiationService.createInstance(RatingsWidget, template.rating, false) {{SQL CARBON EDIT}} Remove the widgets
];
const reloadAction = this.instantiationService.createInstance(ReloadAction);
const combinedInstallAction = this.instantiationService.createInstance(CombinedInstallAction);
@@ -387,20 +416,20 @@ export class ExtensionEditor extends BaseEditor {
const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]);
extensionContainers.extension = extension;
this.extensionActionBar.clear();
this.extensionActionBar.push(actions, { icon: true, label: true });
template.extensionActionBar.clear();
template.extensionActionBar.push(actions, { icon: true, label: true });
for (const disposable of [...actions, ...widgets, extensionContainers]) {
this.transientDisposables.add(disposable);
}
this.setSubText(extension, reloadAction);
this.content.innerHTML = ''; // Clear content before setting navbar actions.
this.setSubText(extension, reloadAction, template);
template.content.innerHTML = ''; // Clear content before setting navbar actions.
this.navbar.clear();
this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
template.navbar.clear();
template.navbar.onChange(e => this.onNavbarChange(extension, e, template), this, this.transientDisposables);
if (extension.hasReadme()) {
this.navbar.push(NavbarSection.Readme, localize('details', "Details"), localize('detailstooltip', "Extension details, rendered from the extension's 'README.md' file"));
template.navbar.push(NavbarSection.Readme, localize('details', "Details"), localize('detailstooltip', "Extension details, rendered from the extension's 'README.md' file"));
}
this.extensionManifest.get()
.promise
@@ -409,25 +438,23 @@ export class ExtensionEditor extends BaseEditor {
combinedInstallAction.manifest = manifest;
}
if (extension.extensionPack.length) {
this.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together"));
template.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together"));
}
if (manifest && manifest.contributes) {
this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"), localize('contributionstooltip', "Lists contributions to VS Code by this extension"));
template.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"), localize('contributionstooltip', "Lists contributions to VS Code by this extension"));
}
if (extension.hasChangelog()) {
this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"), localize('changelogtooltip', "Extension update history, rendered from the extension's 'CHANGELOG.md' file"));
template.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"), localize('changelogtooltip', "Extension update history, rendered from the extension's 'CHANGELOG.md' file"));
}
if (extension.dependencies.length) {
this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"), localize('dependenciestooltip', "Lists extensions this extension depends on"));
template.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"), localize('dependenciestooltip', "Lists extensions this extension depends on"));
}
this.editorLoadComplete = true;
});
return super.setInput(input, options, token);
}
private setSubText(extension: IExtension, reloadAction: ReloadAction): void {
hide(this.subtextContainer);
private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void {
hide(template.subtextContainer);
const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction);
const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction);
@@ -436,23 +463,23 @@ export class ExtensionEditor extends BaseEditor {
ignoreAction.enabled = false;
undoIgnoreAction.enabled = false;
this.ignoreActionbar.clear();
this.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true });
template.ignoreActionbar.clear();
template.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true });
this.transientDisposables.add(ignoreAction);
this.transientDisposables.add(undoIgnoreAction);
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
if (extRecommendations[extension.identifier.id.toLowerCase()]) {
ignoreAction.enabled = true;
this.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText;
show(this.subtextContainer);
template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText;
show(template.subtextContainer);
} else if (this.extensionTipsService.getAllIgnoredRecommendations().global.indexOf(extension.identifier.id.toLowerCase()) !== -1) {
undoIgnoreAction.enabled = true;
this.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension.");
show(this.subtextContainer);
template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension.");
show(template.subtextContainer);
}
else {
this.subtext.textContent = '';
template.subtext.textContent = '';
}
this.extensionTipsService.onRecommendationChange(change => {
@@ -462,28 +489,28 @@ export class ExtensionEditor extends BaseEditor {
const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
if (extRecommendations[extension.identifier.id.toLowerCase()]) {
ignoreAction.enabled = true;
this.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText;
template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText;
}
} else {
undoIgnoreAction.enabled = true;
ignoreAction.enabled = false;
this.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension.");
template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension.");
}
}
});
this.transientDisposables.add(reloadAction.onDidChange(e => {
if (e.tooltip) {
this.subtext.textContent = reloadAction.tooltip;
show(this.subtextContainer);
template.subtext.textContent = reloadAction.tooltip;
show(template.subtextContainer);
ignoreAction.enabled = false;
undoIgnoreAction.enabled = false;
}
if (e.enabled === true) {
show(this.subtextContainer);
show(template.subtextContainer);
}
if (e.enabled === false) {
hide(this.subtextContainer);
hide(template.subtextContainer);
}
}));
}
@@ -507,7 +534,7 @@ export class ExtensionEditor extends BaseEditor {
}
}
private onNavbarChange(extension: IExtension, { id, focus }: { id: string, focus: boolean }): void {
private onNavbarChange(extension: IExtension, { id, focus }: { id: string | null, focus: boolean }, template: IExtensionEditorTemplate): void {
if (this.editorLoadComplete) {
/* __GDPR__
"extensionEditor:navbarChange" : {
@@ -521,30 +548,32 @@ export class ExtensionEditor extends BaseEditor {
}
this.contentDisposables.clear();
this.content.innerHTML = '';
template.content.innerHTML = '';
this.activeElement = null;
this.open(id, extension)
.then(activeElement => {
this.activeElement = activeElement;
if (focus) {
this.focus();
}
});
if (id) {
this.open(id, extension, template)
.then(activeElement => {
this.activeElement = activeElement;
if (focus) {
this.focus();
}
});
}
}
private open(id: string, extension: IExtension): Promise<IActiveElement | null> {
private open(id: string, extension: IExtension, template: IExtensionEditorTemplate): Promise<IActiveElement | null> {
switch (id) {
case NavbarSection.Readme: return this.openReadme();
case NavbarSection.Contributions: return this.openContributions();
case NavbarSection.Changelog: return this.openChangelog();
case NavbarSection.Dependencies: return this.openDependencies(extension);
case NavbarSection.ExtensionPack: return this.openExtensionPack(extension);
case NavbarSection.Readme: return this.openReadme(template);
case NavbarSection.Contributions: return this.openContributions(template);
case NavbarSection.Changelog: return this.openChangelog(template);
case NavbarSection.Dependencies: return this.openDependencies(extension, template);
case NavbarSection.ExtensionPack: return this.openExtensionPack(extension, template);
}
return Promise.resolve(null);
}
private openMarkdown(cacheResult: CacheResult<string>, noContentCopy: string): Promise<IActiveElement> {
return this.loadContents(() => cacheResult)
private openMarkdown(cacheResult: CacheResult<string>, noContentCopy: string, template: IExtensionEditorTemplate): Promise<IActiveElement> {
return this.loadContents(() => cacheResult, template)
.then(marked.parse)
.then(content => this.renderBody(content))
.then(removeEmbeddedSVGs)
@@ -556,7 +585,7 @@ export class ExtensionEditor extends BaseEditor {
{
svgWhiteList: this.extensionsWorkbenchService.allowedBadgeProviders,
});
webviewElement.mountTo(this.content);
webviewElement.mountTo(template.content);
this.contentDisposables.add(webviewElement.onDidFocus(() => this.fireOnDidFocus()));
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, webviewElement);
this.contentDisposables.add(toDisposable(removeLayoutParticipant));
@@ -575,7 +604,7 @@ export class ExtensionEditor extends BaseEditor {
return webviewElement;
})
.then(undefined, () => {
const p = append(this.content, $('p.nocontent'));
const p = append(template.content, $('p.nocontent'));
p.textContent = noContentCopy;
return p;
});
@@ -769,17 +798,17 @@ export class ExtensionEditor extends BaseEditor {
</html>`;
}
private openReadme(): Promise<IActiveElement> {
return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."));
private openReadme(template: IExtensionEditorTemplate): Promise<IActiveElement> {
return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template);
}
private openChangelog(): Promise<IActiveElement> {
return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."));
private openChangelog(template: IExtensionEditorTemplate): Promise<IActiveElement> {
return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template);
}
private openContributions(): Promise<IActiveElement> {
private openContributions(template: IExtensionEditorTemplate): Promise<IActiveElement> {
const content = $('div', { class: 'subcontent', tabindex: '0' });
return this.loadContents(() => this.extensionManifest!.get())
return this.loadContents(() => this.extensionManifest!.get(), template)
.then(manifest => {
if (!manifest) {
return content;
@@ -811,28 +840,28 @@ export class ExtensionEditor extends BaseEditor {
const isEmpty = !renders.some(x => x);
if (isEmpty) {
append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
append(this.content, content);
append(template.content, content);
} else {
append(this.content, scrollableContent.getDomNode());
append(template.content, scrollableContent.getDomNode());
this.contentDisposables.add(scrollableContent);
}
return content;
}, () => {
append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
append(this.content, content);
append(template.content, content);
return content;
});
}
private openDependencies(extension: IExtension): Promise<IActiveElement> {
private openDependencies(extension: IExtension, template: IExtensionEditorTemplate): Promise<IActiveElement> {
if (arrays.isFalsyOrEmpty(extension.dependencies)) {
append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
return Promise.resolve(this.content);
append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
return Promise.resolve(template.content);
}
const content = $('div', { class: 'subcontent' });
const scrollableContent = new DomScrollableElement(content, {});
append(this.content, scrollableContent.getDomNode());
append(template.content, scrollableContent.getDomNode());
this.contentDisposables.add(scrollableContent);
const dependenciesTree = this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension, null, extension => extension.dependencies || [], this.extensionsWorkbenchService), content);
@@ -849,10 +878,10 @@ export class ExtensionEditor extends BaseEditor {
return Promise.resolve({ focus() { dependenciesTree.domFocus(); } });
}
private openExtensionPack(extension: IExtension): Promise<IActiveElement> {
private openExtensionPack(extension: IExtension, template: IExtensionEditorTemplate): Promise<IActiveElement> {
const content = $('div', { class: 'subcontent' });
const scrollableContent = new DomScrollableElement(content, {});
append(this.content, scrollableContent.getDomNode());
append(template.content, scrollableContent.getDomNode());
this.contentDisposables.add(scrollableContent);
const extensionsPackTree = this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension, null, extension => extension.extensionPack || [], this.extensionsWorkbenchService), content);
@@ -1272,11 +1301,11 @@ export class ExtensionEditor extends BaseEditor {
return null;
}
private loadContents<T>(loadingTask: () => CacheResult<T>): Promise<T> {
addClass(this.content, 'loading');
private loadContents<T>(loadingTask: () => CacheResult<T>, template: IExtensionEditorTemplate): Promise<T> {
addClass(template.content, 'loading');
const result = loadingTask();
const onDone = () => removeClass(this.content, 'loading');
const onDone = () => removeClass(template.content, 'loading');
result.promise.then(onDone, onDone);
this.contentDisposables.add(toDisposable(() => result.dispose()));

View File

@@ -3097,7 +3097,10 @@ interface IExtensionPickItem extends IQuickPickItem {
export class InstallLocalExtensionsInRemoteAction extends Action {
private extensions: IExtension[] | undefined = undefined;
constructor(
private readonly selectAndInstall: boolean,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@@ -3109,32 +3112,56 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
) {
super('workbench.extensions.actions.installLocalExtensionsInRemote');
this.update();
this._register(this.extensionsWorkbenchService.onChange(() => this.update()));
this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions());
this._register(this.extensionsWorkbenchService.onChange(() => {
if (this.extensions) {
this.updateExtensions();
}
}));
}
get label(): string {
return this.extensionManagementServerService.remoteExtensionManagementServer ?
localize('install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label) : '';
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.selectAndInstall ?
localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label)
: localize('install local extensions', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label);
}
return '';
}
private updateExtensions(): void {
this.extensions = this.extensionsWorkbenchService.local;
this.update();
}
private update(): void {
this.enabled = this.getLocalExtensionsToInstall().length > 0;
this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0;
this.tooltip = this.label;
}
private getLocalExtensionsToInstall(): IExtension[] {
return this.extensionsWorkbenchService.local.filter(extension => {
async run(): Promise<void> {
if (this.selectAndInstall) {
return this.selectAndInstallLocalExtensions();
} else {
const extensionsToInstall = await this.queryExtensionsToInstall();
return this.installLocalExtensions(extensionsToInstall);
}
}
private async queryExtensionsToInstall(): Promise<IExtension[]> {
const local = await this.extensionsWorkbenchService.queryLocal();
return this.getExtensionsToInstall(local);
}
private getExtensionsToInstall(local: IExtension[]): IExtension[] {
return local.filter(extension => {
const action = this.instantiationService.createInstance(RemoteInstallAction);
action.extension = extension;
return action.enabled;
});
}
async run(): Promise<void> {
this.selectAndInstallLocalExtensions();
return Promise.resolve();
}
private selectAndInstallLocalExtensions(): void {
private async selectAndInstallLocalExtensions(): Promise<void> {
const quickPick = this.quickInputService.createQuickPick<IExtensionPickItem>();
quickPick.busy = true;
const disposable = quickPick.onDidAccept(() => {
@@ -3144,7 +3171,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
this.onDidAccept(quickPick.selectedItems);
});
quickPick.show();
const localExtensionsToInstall = this.getLocalExtensionsToInstall();
const localExtensionsToInstall = await this.queryExtensionsToInstall();
quickPick.busy = false;
if (localExtensionsToInstall.length) {
quickPick.title = localize('install local extensions title', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer!.label);

View File

@@ -156,7 +156,7 @@ export class UnknownExtensionRenderer implements IListRenderer<ITreeNode<IExtens
class OpenExtensionAction extends Action {
private _extensionData: IExtensionData;
private _extensionData: IExtensionData | undefined;
constructor(@IExtensionsWorkbenchService private readonly extensionsWorkdbenchService: IExtensionsWorkbenchService) {
super('extensions.action.openExtension', '');
@@ -166,12 +166,11 @@ class OpenExtensionAction extends Action {
this._extensionData = extension;
}
public get extensionData(): IExtensionData {
return this._extensionData;
}
run(sideByside: boolean): Promise<any> {
return this.extensionsWorkdbenchService.open(this.extensionData.extension, sideByside);
if (this._extensionData) {
return this.extensionsWorkdbenchService.open(this._extensionData.extension, sideByside);
}
return Promise.resolve();
}
}
@@ -263,4 +262,4 @@ export class ExtensionData implements IExtensionData {
}
return null;
}
}
}

View File

@@ -22,7 +22,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, AutoUpdate
import {
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction,
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, InstallLocalExtensionsInRemoteAction
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
@@ -323,7 +323,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensionsViewlet {
private onSearchChange: EventOf<string>;
private readonly _onSearchChange: Emitter<string> = this._register(new Emitter<string>());
private readonly onSearchChange: EventOf<string> = this._onSearchChange.event;
private nonEmptyWorkspaceContextKey: IContextKey<boolean>;
private defaultViewsContextKey: IContextKey<boolean>;
private searchMarketplaceExtensionsContextKey: IContextKey<boolean>;
@@ -337,12 +338,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
private defaultRecommendedExtensionsContextKey: IContextKey<boolean>;
private searchDelayer: Delayer<void>;
private root: HTMLElement;
private searchBox: SuggestEnabledInput;
private extensionsBox: HTMLElement;
private primaryActions: IAction[];
private secondaryActions: IAction[] | null;
private root: HTMLElement | undefined;
private searchBox: SuggestEnabledInput | undefined;
private primaryActions: IAction[] | undefined;
private secondaryActions: IAction[] | null = null;
private readonly searchViewletState: MementoObject;
constructor(
@@ -352,7 +351,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
@IInstantiationService instantiationService: IInstantiationService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@INotificationService private readonly notificationService: INotificationService,
@IViewletService private readonly viewletService: IViewletService,
@IThemeService themeService: IThemeService,
@@ -422,32 +420,35 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
this._register(attachSuggestEnabledInputBoxStyler(this.searchBox, this.themeService));
const _searchChange = new Emitter<string>();
this.onSearchChange = _searchChange.event;
this._register(this.searchBox.onInputDidChange(() => {
this.triggerSearch();
_searchChange.fire(this.searchBox.getValue());
this._onSearchChange.fire(this.searchBox!.getValue());
}, this));
this._register(this.searchBox.onShouldFocusResults(() => this.focusListView(), this));
this._register(this.onDidChangeVisibility(visible => {
if (visible) {
this.searchBox.focus();
this.searchBox!.focus();
}
}));
this.extensionsBox = append(this.root, $('.extensions'));
super.create(this.extensionsBox);
super.create(append(this.root, $('.extensions')));
}
focus(): void {
this.searchBox.focus();
if (this.searchBox) {
this.searchBox.focus();
}
}
layout(dimension: Dimension): void {
toggleClass(this.root, 'narrow', dimension.width <= 300);
this.searchBox.layout({ height: 20, width: dimension.width - 34 });
if (this.root) {
toggleClass(this.root, 'narrow', dimension.width <= 300);
}
if (this.searchBox) {
this.searchBox.layout({ height: 20, width: dimension.width - 34 });
}
super.layout(new Dimension(dimension.width, dimension.height - 38));
}
@@ -458,7 +459,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
getActions(): IAction[] {
if (!this.primaryActions) {
this.primaryActions = [
this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox.getValue())
this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : '')
];
}
return this.primaryActions;
@@ -484,7 +485,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL),
...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]),
this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL),
...(this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer ? [this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction)] : []),
new Separator(),
this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL),
this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL)
@@ -495,22 +495,24 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
}
search(value: string): void {
const event = new Event('input', { bubbles: true }) as SearchInputEvent;
event.immediate = true;
if (this.searchBox) {
const event = new Event('input', { bubbles: true }) as SearchInputEvent;
event.immediate = true;
this.searchBox.setValue(value);
this.searchBox.setValue(value);
}
}
private triggerSearch(immediate = false): void {
this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.getValue() ? 0 : 500).then(undefined, err => this.onError(err));
private triggerSearch(): void {
this.searchDelayer.trigger(() => this.doSearch(), this.searchBox && this.searchBox.getValue() ? 500 : 0).then(undefined, err => this.onError(err));
}
private normalizedQuery(): string {
return this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:');
return this.searchBox ? this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:') : '';
}
protected saveState(): void {
const value = this.searchBox.getValue();
const value = this.searchBox ? this.searchBox.getValue() : '';
if (ExtensionsListView.isLocalExtensionsQuery(value)) {
this.searchViewletState['query.value'] = value;
} else {

View File

@@ -14,11 +14,11 @@ import { IExtensionManagementServer, IExtensionManagementServerService, IExtensi
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { append, $, toggleClass } from 'vs/base/browser/dom';
import { append, $, toggleClass, addClass } from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList';
import { IExtension, IExtensionsWorkbenchService, ExtensionState } from '../common/extensions';
import { Query } from '../common/extensionQuery';
import { IExtension, IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions';
import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachBadgeStyler } from 'vs/platform/theme/common/styler';
@@ -27,8 +27,8 @@ import { OpenGlobalSettingsAction } from 'vs/workbench/contrib/preferences/brows
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { WorkbenchPagedList } from 'vs/platform/list/browser/listService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
@@ -74,15 +74,16 @@ class ExtensionListViewWarning extends Error { }
export class ExtensionsListView extends ViewletPanel {
private readonly server: IExtensionManagementServer | undefined;
private messageContainer: HTMLElement;
private messageSeverityIcon: HTMLElement;
private messageBox: HTMLElement;
private extensionsList: HTMLElement;
private badge: CountBadge;
protected badgeContainer: HTMLElement;
private list: WorkbenchPagedList<IExtension> | null;
private queryRequest: { query: string, request: CancelablePromise<IPagedModel<IExtension>> } | null;
protected readonly server: IExtensionManagementServer | undefined;
private bodyTemplate: {
messageContainer: HTMLElement;
messageSeverityIcon: HTMLElement;
messageBox: HTMLElement;
extensionsList: HTMLElement;
} | undefined;
private badge: CountBadge | undefined;
private list: WorkbenchPagedList<IExtension> | null = null;
private queryRequest: { query: string, request: CancelablePromise<IPagedModel<IExtension>> } | null = null;
constructor(
options: ExtensionsListViewOptions,
@@ -104,31 +105,27 @@ export class ExtensionsListView extends ViewletPanel {
@IProductService protected readonly productService: IProductService,
@IContextKeyService contextKeyService: IContextKeyService,
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService);
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService);
this.server = options.server;
}
protected renderHeader(container: HTMLElement): void {
this.renderHeaderTitle(container);
}
addClass(container, 'extension-view-header');
super.renderHeader(container);
renderHeaderTitle(container: HTMLElement): void {
super.renderHeaderTitle(container, this.title);
this.badgeContainer = append(container, $('.count-badge-wrapper'));
this.badge = new CountBadge(this.badgeContainer);
this.badge = new CountBadge(append(container, $('.count-badge-wrapper')));
this._register(attachBadgeStyler(this.badge, this.themeService));
}
renderBody(container: HTMLElement): void {
this.extensionsList = append(container, $('.extensions-list'));
this.messageContainer = append(container, $('.message-container'));
this.messageSeverityIcon = append(this.messageContainer, $(''));
this.messageBox = append(this.messageContainer, $('.message'));
const extensionsList = append(container, $('.extensions-list'));
const messageContainer = append(container, $('.message-container'));
const messageSeverityIcon = append(messageContainer, $(''));
const messageBox = append(messageContainer, $('.message'));
const delegate = new Delegate();
const extensionsViewState = new ExtensionsViewState();
const renderer = this.instantiationService.createInstance(Renderer, extensionsViewState);
this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], {
this.list = this.instantiationService.createInstance(WorkbenchPagedList, extensionsList, delegate, [renderer], {
ariaLabel: localize('extensions', "Extensions"),
multipleSelectionSupport: false,
setRowLineHeight: false,
@@ -148,10 +145,19 @@ export class ExtensionsListView extends ViewletPanel {
.map(e => e.elements[0])
.filter(e => !!e)
.on(this.pin, this));
this.bodyTemplate = {
extensionsList,
messageBox,
messageContainer,
messageSeverityIcon
};
}
protected layoutBody(height: number, width: number): void {
this.extensionsList.style.height = height + 'px';
if (this.bodyTemplate) {
this.bodyTemplate.extensionsList.style.height = height + 'px';
}
if (this.list) {
this.list.layout(height, width);
}
@@ -501,7 +507,7 @@ export class ExtensionsListView extends ViewletPanel {
}
private _searchExperiments: Promise<IExperiment[]>;
private _searchExperiments: Promise<IExperiment[]> | undefined;
private getSearchExperiments(): Promise<IExperiment[]> {
if (!this._searchExperiments) {
this._searchExperiments = this.experimentService.getExperimentsByType(ExperimentActionType.ExtensionSearchResults);
@@ -773,24 +779,27 @@ export class ExtensionsListView extends ViewletPanel {
this.list.scrollTop = 0;
const count = this.count();
toggleClass(this.extensionsList, 'hidden', count === 0);
toggleClass(this.messageContainer, 'hidden', count > 0);
this.badge.setCount(count);
if (this.bodyTemplate && this.badge) {
if (count === 0 && this.isBodyVisible()) {
if (error) {
if (error instanceof ExtensionListViewWarning) {
this.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning);
this.messageBox.textContent = getErrorMessage(error);
toggleClass(this.bodyTemplate.extensionsList, 'hidden', count === 0);
toggleClass(this.bodyTemplate.messageContainer, 'hidden', count > 0);
this.badge.setCount(count);
if (count === 0 && this.isBodyVisible()) {
if (error) {
if (error instanceof ExtensionListViewWarning) {
this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning);
this.bodyTemplate.messageBox.textContent = getErrorMessage(error);
} else {
this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Error);
this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error));
}
} else {
this.messageSeverityIcon.className = SeverityIcon.className(Severity.Error);
this.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error));
this.bodyTemplate.messageSeverityIcon.className = '';
this.bodyTemplate.messageBox.textContent = localize('no extensions found', "No extensions found.");
}
} else {
this.messageSeverityIcon.className = '';
this.messageBox.textContent = localize('no extensions found', "No extensions found.");
alert(this.bodyTemplate.messageBox.textContent);
}
alert(this.messageBox.textContent);
}
}
}
@@ -949,6 +958,15 @@ export class ServerExtensionsView extends ExtensionsListView {
}
return super.show(query.trim());
}
getActions(): IAction[] {
if (this.extensionManagementServerService.localExtensionManagementServer === this.server) {
const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false));
installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download';
return [installLocalExtensionsInRemoteAction];
}
return [];
}
}
export class EnabledExtensionsView extends ExtensionsListView {
@@ -1030,7 +1048,7 @@ export class RecommendedExtensionsView extends ExtensionsListView {
export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
private readonly recommendedExtensionsQuery = '@recommended:workspace';
private installAllAction: InstallWorkspaceRecommendedExtensionsAction;
private installAllAction: InstallWorkspaceRecommendedExtensionsAction | undefined;
renderBody(container: HTMLElement): void {
super.renderBody(container);
@@ -1040,25 +1058,15 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
this._register(this.contextService.onDidChangeWorkbenchState(() => this.update()));
}
renderHeader(container: HTMLElement): void {
super.renderHeader(container);
getActions(): IAction[] {
if (!this.installAllAction) {
this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL, []));
this.installAllAction.class = 'octicon octicon-cloud-download';
}
const listActionBar = $('.list-actionbar-container');
container.insertBefore(listActionBar, this.badgeContainer);
const actionbar = this._register(new ActionBar(listActionBar, {
animated: false
}));
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL, []));
const configureWorkspaceFolderAction = this._register(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL));
this.installAllAction.class = 'octicon octicon-cloud-download';
configureWorkspaceFolderAction.class = 'octicon octicon-pencil';
actionbar.push([this.installAllAction], { icon: true, label: false });
actionbar.push([configureWorkspaceFolderAction], { icon: true, label: false });
return [this.installAllAction, configureWorkspaceFolderAction];
}
async show(query: string): Promise<IPagedModel<IExtension>> {
@@ -1073,9 +1081,11 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
this.setRecommendationsToInstall();
}
private setRecommendationsToInstall(): Promise<void> {
return this.getRecommendationsToInstall()
.then(recommendations => { this.installAllAction.recommendations = recommendations; });
private async setRecommendationsToInstall(): Promise<void> {
const recommendations = await this.getRecommendationsToInstall();
if (this.installAllAction) {
this.installAllAction.recommendations = recommendations;
}
}
private getRecommendationsToInstall(): Promise<IExtensionRecommendation[]> {

View File

@@ -18,9 +18,9 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
private _extension: IExtension;
get extension(): IExtension { return this._extension; }
set extension(extension: IExtension) { this._extension = extension; this.update(); }
private _extension: IExtension | null = null;
get extension(): IExtension | null { return this._extension; }
set extension(extension: IExtension | null) { this._extension = extension; this.update(); }
update(): void { this.render(); }
abstract render(): void;
}
@@ -183,7 +183,7 @@ export class RecommendationWidget extends ExtensionWidget {
private element?: HTMLElement;
private readonly disposables = this._register(new DisposableStore());
private _tooltip: string;
private _tooltip: string = '';
get tooltip(): string { return this._tooltip; }
set tooltip(tooltip: string) {
if (this._tooltip !== tooltip) {
@@ -314,4 +314,4 @@ class RemoteBadge extends Disposable {
updateTitle();
}
}
}
}

View File

@@ -500,7 +500,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
private readonly _onChange: Emitter<IExtension | undefined> = new Emitter<IExtension | undefined>();
get onChange(): Event<IExtension | undefined> { return this._onChange.event; }
private _extensionAllowedBadgeProviders: string[];
private _extensionAllowedBadgeProviders: string[] | undefined;
private installing: IExtension[] = [];
constructor(
@@ -1151,12 +1151,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
private _ignoredAutoUpdateExtensions: string[];
private _ignoredAutoUpdateExtensions: string[] | undefined;
private get ignoredAutoUpdateExtensions(): string[] {
if (!this._ignoredAutoUpdateExtensions) {
this._ignoredAutoUpdateExtensions = JSON.parse(this.storageService.get('extensions.ignoredAutoUpdateExtension', StorageScope.GLOBAL, '[]') || '[]');
}
return this._ignoredAutoUpdateExtensions;
return this._ignoredAutoUpdateExtensions!;
}
private set ignoredAutoUpdateExtensions(extensionIds: string[]) {

View File

@@ -6,8 +6,3 @@
.monaco-workbench .activitybar > .content .monaco-action-bar .action-label.extensions {
-webkit-mask: url('extensions-activity-bar.svg') no-repeat 50% 50%;
}
.extensions .split-view-view .panel-header .count-badge-wrapper {
position: absolute;
right: 12px;
}

View File

@@ -27,13 +27,16 @@
height: calc(100% - 38px);
}
.extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item > .octicon {
font-size: 12px;
line-height: 1;
margin-right: 10px;
.extensions-viewlet > .extensions .extension-view-header .monaco-action-bar {
margin-right: 4px;
}
.extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item.disabled {
.extensions-viewlet > .extensions .extension-view-header .monaco-action-bar .action-item > .action-label.icon.octicon {
vertical-align: middle;
line-height: 22px;
}
.extensions-viewlet > .extensions .extension-view-header .monaco-action-bar .action-item.disabled {
display: none;
}
@@ -44,7 +47,7 @@
}
.extensions-viewlet > .extensions .panel-header {
padding-right: 28px;
padding-right: 6px;
}
.extensions-viewlet > .extensions .panel-header > .title {

View File

@@ -22,7 +22,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC
) {
super();
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction);
const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, true);
CommandsRegistry.registerCommand('workbench.extensions.installLocalExtensions', () => installLocalExtensionsInRemoteAction.run());
let disposable = Disposable.None;
const appendMenuItem = () => {