extensionEditor.ts 25.3 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

J
Joao Moreno 已提交
8
import 'vs/css!./media/extensionEditor';
J
Joao Moreno 已提交
9
import { localize } from 'vs/nls';
J
Joao Moreno 已提交
10 11
import { TPromise } from 'vs/base/common/winjs.base';
import { marked } from 'vs/base/common/marked/marked';
J
Joao Moreno 已提交
12
import { always } from 'vs/base/common/async';
J
Joao Moreno 已提交
13
import * as arrays from 'vs/base/common/arrays';
J
Joao Moreno 已提交
14
import Event, { Emitter, once, fromEventEmitter, chain } from 'vs/base/common/event';
J
Joao Moreno 已提交
15
import Cache from 'vs/base/common/cache';
J
Joao Moreno 已提交
16
import { Action } from 'vs/base/common/actions';
17
import { isPromiseCanceledError } from 'vs/base/common/errors';
18
import Severity from 'vs/base/common/severity';
J
Joao Moreno 已提交
19
import { IDisposable, empty, dispose, toDisposable } from 'vs/base/common/lifecycle';
J
Joao Moreno 已提交
20
import { Builder } from 'vs/base/browser/builder';
21
import { domEvent } from 'vs/base/browser/event';
22
import { append, $, addClass, removeClass, finalHandler, join } from 'vs/base/browser/dom';
J
Joao Moreno 已提交
23
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
B
Benjamin Pasero 已提交
24
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Joao Moreno 已提交
25 26
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
27
import { IExtensionGalleryService, IExtensionManifest, IKeyBinding } from 'vs/platform/extensionManagement/common/extensionManagement';
28
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/themeService';
29
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
30
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions';
31
import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer';
32
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
33 34
import { ITemplateData } from 'vs/workbench/parts/extensions/browser/extensionsList';
import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
J
Joao Moreno 已提交
35
import { EditorOptions } from 'vs/workbench/common/editor';
J
Joao Moreno 已提交
36
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
37
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
J
Joao Moreno 已提交
38
import WebView from 'vs/workbench/parts/html/browser/webview';
A
Alex Dima 已提交
39
import { createKeybinding } from 'vs/base/common/keyCodes';
40
import { KeybindingIO } from 'vs/platform/keybinding/common/keybindingIO';
J
Johannes Rieken 已提交
41
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
J
Joao Moreno 已提交
42
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
43
import { IMessageService } from 'vs/platform/message/common/message';
44
import { IOpenerService } from 'vs/platform/opener/common/opener';
S
Sandeep Somavarapu 已提交
45
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
J
Joao Moreno 已提交
46
import { Position } from 'vs/platform/editor/common/editor';
47
import { IListService } from 'vs/platform/list/browser/listService';
J
Joao Moreno 已提交
48 49

function renderBody(body: string): string {
M
Matt Bierner 已提交
50
	const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
J
Joao Moreno 已提交
51 52 53 54
	return `<!DOCTYPE html>
		<html>
			<head>
				<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
M
Matt Bierner 已提交
55 56
				<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src http: https: data:; media-src http: https: data:; script-src 'none'; style-src 'nonce-${nonce}'; child-src 'none'; frame-src 'none';">
				<link rel="stylesheet" type="text/css" href="${require.toUrl('./media/markdown.css')}" nonce="${nonce}" >
J
Joao Moreno 已提交
57
			</head>
M
Matt Bierner 已提交
58
			<body>${body}</body>
J
Joao Moreno 已提交
59 60
		</html>`;
}
J
Joao Moreno 已提交
61

J
Joao Moreno 已提交
62 63 64 65 66
class NavBar {

	private _onChange = new Emitter<string>();
	get onChange(): Event<string> { return this._onChange.event; }

J
Joao Moreno 已提交
67
	private currentId: string = null;
J
Joao Moreno 已提交
68 69 70 71 72 73 74 75 76 77
	private actions: Action[];
	private actionbar: ActionBar;

	constructor(container: HTMLElement) {
		const element = append(container, $('.navbar'));
		this.actions = [];
		this.actionbar = new ActionBar(element, { animated: false });
	}

	push(id: string, label: string): void {
J
Joao Moreno 已提交
78
		const run = () => this._update(id);
J
Joao Moreno 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		const action = new Action(id, label, null, true, run);

		this.actions.push(action);
		this.actionbar.push(action);

		if (this.actions.length === 1) {
			run();
		}
	}

	clear(): void {
		this.actions = dispose(this.actions);
		this.actionbar.clear();
	}

J
Joao Moreno 已提交
94 95 96 97 98 99 100 101 102 103 104
	update(): void {
		this._update(this.currentId);
	}

	_update(id: string = this.currentId): TPromise<void> {
		this.currentId = id;
		this._onChange.fire(id);
		this.actions.forEach(a => a.enabled = a.id !== id);
		return TPromise.as(null);
	}

J
Joao Moreno 已提交
105 106 107 108 109 110 111
	dispose(): void {
		this.actionbar = dispose(this.actionbar);
	}
}

const NavbarSection = {
	Readme: 'readme',
X
XVincentX 已提交
112
	Contributions: 'contributions',
S
Sandeep Somavarapu 已提交
113 114
	Changelog: 'changelog',
	Dependencies: 'dependencies'
J
Joao Moreno 已提交
115 116
};

J
Joao Moreno 已提交
117 118 119 120
interface ILayoutParticipant {
	layout(): void;
}

J
Joao Moreno 已提交
121 122 123 124
export class ExtensionEditor extends BaseEditor {

	static ID: string = 'workbench.editor.extension';

125
	private icon: HTMLImageElement;
J
Joao Moreno 已提交
126
	private name: HTMLElement;
S
Sandeep Somavarapu 已提交
127
	private identifier: HTMLElement;
J
Joao Moreno 已提交
128 129
	private license: HTMLElement;
	private publisher: HTMLElement;
J
Joao Moreno 已提交
130
	private installCount: HTMLElement;
J
Joao Moreno 已提交
131
	private rating: HTMLElement;
J
Joao Moreno 已提交
132
	private description: HTMLElement;
J
Joao Moreno 已提交
133 134 135
	private extensionActionBar: ActionBar;
	private navbar: NavBar;
	private content: HTMLElement;
J
Joao Moreno 已提交
136 137 138 139

	private _highlight: ITemplateData;
	private highlightDisposable: IDisposable;

J
Joao Moreno 已提交
140
	private extensionReadme: Cache<string>;
X
XVincentX 已提交
141
	private extensionChangelog: Cache<string>;
J
Joao Moreno 已提交
142
	private extensionManifest: Cache<IExtensionManifest>;
S
Sandeep Somavarapu 已提交
143
	private extensionDependencies: Cache<IExtensionDependencies>;
J
Joao Moreno 已提交
144

J
Joao Moreno 已提交
145
	private layoutParticipants: ILayoutParticipant[] = [];
J
Joao Moreno 已提交
146 147
	private contentDisposables: IDisposable[] = [];
	private transientDisposables: IDisposable[] = [];
J
Joao Moreno 已提交
148
	private disposables: IDisposable[];
J
Joao Moreno 已提交
149 150 151

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
152 153
		@IExtensionGalleryService private galleryService: IExtensionGalleryService,
		@IConfigurationService private configurationService: IConfigurationService,
J
Joao Moreno 已提交
154
		@IInstantiationService private instantiationService: IInstantiationService,
155
		@IViewletService private viewletService: IViewletService,
J
Joao Moreno 已提交
156
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
157
		@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
158
		@IKeybindingService private keybindingService: IKeybindingService,
159
		@IMessageService private messageService: IMessageService,
160 161
		@IOpenerService private openerService: IOpenerService,
		@IListService private listService: IListService
J
Joao Moreno 已提交
162 163 164 165
	) {
		super(ExtensionEditor.ID, telemetryService);
		this._highlight = null;
		this.highlightDisposable = empty;
J
Joao Moreno 已提交
166
		this.disposables = [];
J
Joao Moreno 已提交
167
		this.extensionReadme = null;
X
XVincentX 已提交
168
		this.extensionChangelog = null;
J
Joao Moreno 已提交
169
		this.extensionManifest = null;
S
Sandeep Somavarapu 已提交
170
		this.extensionDependencies = null;
J
Joao Moreno 已提交
171 172 173 174
	}

	createEditor(parent: Builder): void {
		const container = parent.getHTMLElement();
J
Joao Moreno 已提交
175 176 177

		const root = append(container, $('.extension-editor'));
		const header = append(root, $('.header'));
J
Joao Moreno 已提交
178

J
Joao Moreno 已提交
179
		this.icon = append(header, $<HTMLImageElement>('img.icon', { draggable: false }));
J
Joao Moreno 已提交
180

J
Joao Moreno 已提交
181
		const details = append(header, $('.details'));
J
Joao Moreno 已提交
182
		const title = append(details, $('.title'));
J
Joao Moreno 已提交
183
		this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
J
Joao Moreno 已提交
184
		this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
J
Joao Moreno 已提交
185 186

		const subtitle = append(details, $('.subtitle'));
J
Joao Moreno 已提交
187
		this.publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name") }));
J
Joao Moreno 已提交
188

J
Joao Moreno 已提交
189
		this.installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count") }));
J
Joao Moreno 已提交
190

J
Joao Moreno 已提交
191
		this.rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating") }));
J
Joao Moreno 已提交
192

J
Joao Moreno 已提交
193
		this.license = append(subtitle, $('span.license.clickable'));
J
Joao Moreno 已提交
194
		this.license.textContent = localize('license', 'License');
195
		this.license.style.display = 'none';
J
Joao Moreno 已提交
196

J
Joao Moreno 已提交
197 198
		this.description = append(details, $('.description'));

J
Joao Moreno 已提交
199
		const extensionActions = append(details, $('.actions'));
200 201 202 203 204 205 206 207 208 209 210 211
		this.extensionActionBar = new ActionBar(extensionActions, {
			animated: false,
			actionItemProvider: (action: Action) => {
				if (action.id === EnableAction.ID) {
					return (<EnableAction>action).actionItem;
				}
				if (action.id === DisableAction.ID) {
					return (<DisableAction>action).actionItem;
				}
				return null;
			}
		});
J
Joao Moreno 已提交
212 213
		this.disposables.push(this.extensionActionBar);

J
Joao Moreno 已提交
214 215 216 217
		chain(fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run'))
			.map(({ error }) => error)
			.filter(error => !!error)
			.on(this.onError, this, this.disposables);
218

J
Joao Moreno 已提交
219 220
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
221

J
Joao Moreno 已提交
222
		this.content = append(body, $('.content'));
J
Joao Moreno 已提交
223 224 225
	}

	setInput(input: ExtensionsInput, options: EditorOptions): TPromise<void> {
J
Joao Moreno 已提交
226 227
		const extension = input.extension;

J
Joao Moreno 已提交
228 229
		this.transientDisposables = dispose(this.transientDisposables);

230
		this.telemetryService.publicLog('extensionGallery:openExtension', extension.telemetryData);
J
Joao Moreno 已提交
231

J
Joao Moreno 已提交
232
		this.extensionReadme = new Cache(() => extension.getReadme());
X
XVincentX 已提交
233
		this.extensionChangelog = new Cache(() => extension.getChangelog());
J
Joao Moreno 已提交
234
		this.extensionManifest = new Cache(() => extension.getManifest());
S
Sandeep Somavarapu 已提交
235
		this.extensionDependencies = new Cache(() => this.extensionsWorkbenchService.loadDependencies(extension));
J
Joao Moreno 已提交
236

237 238 239 240
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

J
Joao Moreno 已提交
241
		this.name.textContent = extension.displayName;
S
Sandeep Somavarapu 已提交
242
		this.identifier.textContent = extension.id;
243

J
Joao Moreno 已提交
244 245
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
246

247 248 249
		if (extension.url) {
			this.name.onclick = finalHandler(() => window.open(extension.url));
			this.rating.onclick = finalHandler(() => window.open(`${extension.url}#review-details`));
J
Joao Moreno 已提交
250
			this.publisher.onclick = finalHandler(() => {
251
				this.viewletService.openViewlet(VIEWLET_ID, true)
J
Joao Moreno 已提交
252
					.then(viewlet => viewlet as IExtensionsViewlet)
J
Johannes Rieken 已提交
253
					.done(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`));
J
Joao Moreno 已提交
254
			});
255 256

			if (extension.licenseUrl) {
B
Benjamin Pasero 已提交
257
				this.license.onclick = finalHandler(() => window.open(extension.licenseUrl));
258 259 260 261 262
				this.license.style.display = 'initial';
			} else {
				this.license.onclick = null;
				this.license.style.display = 'none';
			}
J
Joao Moreno 已提交
263 264
		}

J
Joao Moreno 已提交
265
		const install = this.instantiationService.createInstance(InstallWidget, this.installCount, { extension });
J
Joao Moreno 已提交
266 267
		this.transientDisposables.push(install);

J
Joao Moreno 已提交
268
		const ratings = this.instantiationService.createInstance(RatingsWidget, this.rating, { extension });
J
Joao Moreno 已提交
269
		this.transientDisposables.push(ratings);
J
Joao Moreno 已提交
270

J
Joao Moreno 已提交
271
		const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
J
Joao Moreno 已提交
272 273 274
		const installAction = this.instantiationService.createInstance(CombinedInstallAction);
		const updateAction = this.instantiationService.createInstance(UpdateAction);
		const enableAction = this.instantiationService.createInstance(EnableAction);
275
		const disableAction = this.instantiationService.createInstance(DisableAction);
276
		const reloadAction = this.instantiationService.createInstance(ReloadAction);
J
Joao Moreno 已提交
277 278

		installAction.extension = extension;
J
Joao Moreno 已提交
279
		builtinStatusAction.extension = extension;
J
Joao Moreno 已提交
280 281
		updateAction.extension = extension;
		enableAction.extension = extension;
282
		disableAction.extension = extension;
283
		reloadAction.extension = extension;
J
Joao Moreno 已提交
284

J
Joao Moreno 已提交
285
		this.extensionActionBar.clear();
286
		this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, builtinStatusAction], { icon: true, label: true });
287
		this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction);
J
Joao Moreno 已提交
288

J
Joao Moreno 已提交
289 290
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
291 292
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"));
293 294
		this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"));
		this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"));
J
Joao Moreno 已提交
295 296 297 298 299 300

		this.content.innerHTML = '';

		return super.setInput(input, options);
	}

J
Joao Moreno 已提交
301 302 303 304 305
	changePosition(position: Position): void {
		this.navbar.update();
		super.changePosition(position);
	}

J
Joao Moreno 已提交
306
	private onNavbarChange(extension: IExtension, id: string): void {
S
Sandeep Somavarapu 已提交
307 308
		this.contentDisposables = dispose(this.contentDisposables);
		this.content.innerHTML = '';
J
Joao Moreno 已提交
309
		switch (id) {
310 311 312
			case NavbarSection.Readme: return this.openReadme();
			case NavbarSection.Contributions: return this.openContributions();
			case NavbarSection.Changelog: return this.openChangelog();
313
			case NavbarSection.Dependencies: return this.openDependencies(extension);
J
Joao Moreno 已提交
314
		}
J
Joao Moreno 已提交
315
	}
J
Joao Moreno 已提交
316

317
	private openMarkdown(content: TPromise<string>, noContentCopy: string) {
318
		return this.loadContents(() => content
J
Joao Moreno 已提交
319
			.then(marked.parse)
J
Joao Moreno 已提交
320
			.then(renderBody)
J
Joao Moreno 已提交
321 322 323
			.then<void>(body => {
				const webview = new WebView(
					this.content,
M
Matt Bierner 已提交
324
					document.querySelector('.monaco-editor-background')
J
Joao Moreno 已提交
325 326 327
				);

				webview.style(this.themeService.getColorTheme());
J
Joao Moreno 已提交
328
				webview.contents = [body];
J
Joao Moreno 已提交
329

330
				webview.onDidClickLink(link => this.openerService.open(link), null, this.contentDisposables);
M
Martin Aeschlimann 已提交
331
				this.themeService.onDidColorThemeChange(theme => webview.style(theme), null, this.contentDisposables);
332
				this.contentDisposables.push(webview);
J
Joao Moreno 已提交
333
			})
J
Joao Moreno 已提交
334
			.then(null, () => {
335
				const p = append(this.content, $('p.nocontent'));
X
XVincentX 已提交
336
				p.textContent = noContentCopy;
J
Joao Moreno 已提交
337
			}));
J
Joao Moreno 已提交
338
	}
J
Joao Moreno 已提交
339

340 341
	private openReadme() {
		return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available."));
342 343
	}

344
	private openChangelog() {
345
		return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available."));
346 347
	}

348
	private openContributions() {
J
Joao Moreno 已提交
349
		return this.loadContents(() => this.extensionManifest.get()
350
			.then(manifest => {
J
Joao Moreno 已提交
351 352 353 354 355 356 357
				const content = $('div', { class: 'subcontent' });
				const scrollableContent = new DomScrollableElement(content, { canUseTranslate3d: false });

				const layout = () => scrollableContent.scanDomNode();
				const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
				this.contentDisposables.push(toDisposable(removeLayoutParticipant));

J
Joao Moreno 已提交
358
				const renders = [
B
Benjamin Pasero 已提交
359
					this.renderSettings(content, manifest, layout),
J
Joao Moreno 已提交
360
					this.renderCommands(content, manifest, layout),
B
Benjamin Pasero 已提交
361 362 363 364
					this.renderLanguages(content, manifest, layout),
					this.renderThemes(content, manifest, layout),
					this.renderJSONValidation(content, manifest, layout),
					this.renderDebuggers(content, manifest, layout)
J
Joao Moreno 已提交
365 366 367
				];

				const isEmpty = !renders.reduce((v, r) => r || v, false);
J
Joao Moreno 已提交
368
				scrollableContent.scanDomNode();
369 370 371 372 373 374 375 376

				if (isEmpty) {
					append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
					return;
				} else {
					append(this.content, scrollableContent.getDomNode());
					this.contentDisposables.push(scrollableContent);
				}
377
			}));
J
Joao Moreno 已提交
378 379
	}

380 381 382 383 384
	private openDependencies(extension: IExtension) {
		if (extension.dependencies.length === 0) {
			append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
			return;
		}
S
Sandeep Somavarapu 已提交
385

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
		return this.loadContents(() => {
			return this.extensionDependencies.get().then(extensionDependencies => {
				const content = $('div', { class: 'subcontent' });
				const scrollableContent = new DomScrollableElement(content, { canUseTranslate3d: false });
				append(this.content, scrollableContent.getDomNode());
				this.contentDisposables.push(scrollableContent);

				const tree = this.renderDependencies(content, extensionDependencies);
				const layout = () => {
					scrollableContent.scanDomNode();
					const scrollState = scrollableContent.getScrollState();
					tree.layout(scrollState.height);
				};
				const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
				this.contentDisposables.push(toDisposable(removeLayoutParticipant));
S
Sandeep Somavarapu 已提交
401

402
				this.contentDisposables.push(tree);
403
				scrollableContent.scanDomNode();
404 405 406 407
			}, error => {
				append(this.content, $('p.nocontent')).textContent = error;
				this.messageService.show(Severity.Error, error);
			});
S
Sandeep Somavarapu 已提交
408 409 410
		});
	}

411 412 413
	private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree {
		const renderer = this.instantiationService.createInstance(Renderer);
		const controller = this.instantiationService.createInstance(Controller);
S
Sandeep Somavarapu 已提交
414 415
		const tree = new Tree(container, {
			dataSource: new DataSource(),
416 417
			renderer,
			controller
S
Sandeep Somavarapu 已提交
418 419
		}, {
				indentPixels: 40,
420 421
				twistiePixels: 20,
				keyboardSupport: false
S
Sandeep Somavarapu 已提交
422 423
			});
		tree.setInput(extensionDependencies);
424 425 426 427 428 429 430 431 432

		this.contentDisposables.push(tree.addListener2('selection', event => {
			if (event && event.payload && event.payload.origin === 'keyboard') {
				controller.openExtension(tree, false);
			}
		}));

		this.contentDisposables.push(this.listService.register(tree));

S
Sandeep Somavarapu 已提交
433 434 435
		return tree;
	}

B
Benjamin Pasero 已提交
436
	private renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
437 438
		const contributes = manifest.contributes;
		const configuration = contributes && contributes.configuration;
J
Joao Moreno 已提交
439
		const properties = configuration && configuration.properties;
J
Joao Moreno 已提交
440
		const contrib = properties ? Object.keys(properties) : [];
J
Joao Moreno 已提交
441

J
Joao Moreno 已提交
442
		if (!contrib.length) {
443
			return false;
J
Joao Moreno 已提交
444 445
		}

J
Joao Moreno 已提交
446
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
447
			$('summary', null, localize('settings', "Settings ({0})", contrib.length)),
J
Joao Moreno 已提交
448
			$('table', null,
J
Joao Moreno 已提交
449 450 451
				$('tr', null,
					$('th', null, localize('setting name', "Name")),
					$('th', null, localize('description', "Description")),
J
nls  
Joao Moreno 已提交
452
					$('th', null, localize('default', "Default"))
J
Joao Moreno 已提交
453 454 455 456 457 458
				),
				...contrib.map(key => $('tr', null,
					$('td', null, $('code', null, key)),
					$('td', null, properties[key].description),
					$('td', null, $('code', null, properties[key].default))
				))
J
Joao Moreno 已提交
459
			)
J
Joao Moreno 已提交
460 461 462
		);

		append(container, details);
463
		return true;
J
Joao Moreno 已提交
464 465
	}

B
Benjamin Pasero 已提交
466
	private renderDebuggers(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
467 468
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.debuggers || [];
J
Joao Moreno 已提交
469

J
Joao Moreno 已提交
470
		if (!contrib.length) {
471
			return false;
J
Joao Moreno 已提交
472 473
		}

J
Joao Moreno 已提交
474
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
475
			$('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)),
J
Joao Moreno 已提交
476
			$('table', null,
J
Joao Moreno 已提交
477 478
				$('tr', null, $('th', null, localize('debugger name', "Name"))),
				...contrib.map(d => $('tr', null, $('td', null, d.label || d.type)))
J
Joao Moreno 已提交
479
			)
J
Joao Moreno 已提交
480 481 482
		);

		append(container, details);
483
		return true;
J
Joao Moreno 已提交
484 485
	}

B
Benjamin Pasero 已提交
486
	private renderThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
487 488
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.themes || [];
J
Joao Moreno 已提交
489

J
Joao Moreno 已提交
490
		if (!contrib.length) {
491
			return false;
J
Joao Moreno 已提交
492 493
		}

J
Joao Moreno 已提交
494
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
495
			$('summary', null, localize('themes', "Themes ({0})", contrib.length)),
J
Joao Moreno 已提交
496 497 498 499
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
500
		return true;
J
Joao Moreno 已提交
501 502
	}

B
Benjamin Pasero 已提交
503
	private renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
504 505
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
506 507

		if (!contrib.length) {
508
			return false;
J
Joao Moreno 已提交
509 510
		}

J
Joao Moreno 已提交
511
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
512
			$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
J
Joao Moreno 已提交
513 514 515 516
			$('ul', null, ...contrib.map(v => $('li', null, v.fileMatch)))
		);

		append(container, details);
517
		return true;
J
Joao Moreno 已提交
518 519
	}

520
	private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
521 522
		const contributes = manifest.contributes;
		const rawCommands = contributes && contributes.commands || [];
J
Joao Moreno 已提交
523
		const commands = rawCommands.map(c => ({
524 525
			id: c.command,
			title: c.title,
J
Joao Moreno 已提交
526
			keybindings: [],
527 528 529
			menus: []
		}));

J
Joao Moreno 已提交
530
		const byId = arrays.index(commands, c => c.id);
531

J
Joao Moreno 已提交
532
		const menus = contributes && contributes.menus || {};
J
Joao Moreno 已提交
533

534 535
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
536
				let command = byId[menu.command];
537 538

				if (!command) {
J
Joao Moreno 已提交
539
					command = { id: menu.command, title: '', keybindings: [], menus: [context] };
J
Joao Moreno 已提交
540
					byId[command.id] = command;
541 542 543 544 545 546 547
					commands.push(command);
				} else {
					command.menus.push(context);
				}
			});
		});

J
Joao Moreno 已提交
548
		const rawKeybindings = contributes && contributes.keybindings || [];
J
Joao Moreno 已提交
549

550 551
		rawKeybindings.forEach(rawKeybinding => {
			const keyLabel = this.keybindingToLabel(rawKeybinding);
J
Joao Moreno 已提交
552 553 554 555 556

			if (!keyLabel) {
				return;
			}

J
Joao Moreno 已提交
557
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
558 559

			if (!command) {
560
				command = { id: rawKeybinding.command, title: '', keybindings: [keyLabel], menus: [] };
J
Joao Moreno 已提交
561
				byId[command.id] = command;
J
Joao Moreno 已提交
562 563
				commands.push(command);
			} else {
564
				command.keybindings.push(keyLabel);
J
Joao Moreno 已提交
565 566 567
			}
		});

568
		if (!commands.length) {
569
			return false;
570 571
		}

J
Joao Moreno 已提交
572
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
573 574 575 576 577
			$('summary', null, localize('commands', "Commands ({0})", commands.length)),
			$('table', null,
				$('tr', null,
					$('th', null, localize('command name', "Name")),
					$('th', null, localize('description', "Description")),
J
Joao Moreno 已提交
578
					$('th', null, localize('keyboard shortcuts', "Keyboard Shortcuts")),
579 580 581
					$('th', null, localize('menuContexts', "Menu Contexts"))
				),
				...commands.map(c => $('tr', null,
582
					$('td', null, $('code', null, c.id)),
583
					$('td', null, c.title),
584
					$('td', null, ...join(c.keybindings.map(keybinding => $('code', null, keybinding)), ' ')),
585 586 587
					$('td', null, ...c.menus.map(context => $('code', null, context)))
				))
			)
J
Joao Moreno 已提交
588 589 590
		);

		append(container, details);
591
		return true;
592 593
	}

B
Benjamin Pasero 已提交
594
	private renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
595 596
		const contributes = manifest.contributes;
		const rawLanguages = contributes && contributes.languages || [];
J
Joao Moreno 已提交
597 598
		const languages = rawLanguages.map(l => ({
			id: l.id,
J
Joao Moreno 已提交
599
			name: (l.aliases || [])[0] || l.id,
J
Joao Moreno 已提交
600 601 602
			extensions: l.extensions || [],
			hasGrammar: false,
			hasSnippets: false
J
Joao Moreno 已提交
603 604
		}));

J
Joao Moreno 已提交
605 606
		const byId = arrays.index(languages, l => l.id);

J
Joao Moreno 已提交
607
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
608 609 610 611 612

		grammars.forEach(grammar => {
			let language = byId[grammar.language];

			if (!language) {
J
Joao Moreno 已提交
613
				language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false };
J
Joao Moreno 已提交
614 615 616 617 618 619 620
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasGrammar = true;
			}
		});

J
Joao Moreno 已提交
621 622 623 624 625 626 627 628 629 630 631 632 633 634
		const snippets = contributes && contributes.snippets || [];

		snippets.forEach(snippet => {
			let language = byId[snippet.language];

			if (!language) {
				language = { id: snippet.language, name: snippet.language, extensions: [], hasGrammar: false, hasSnippets: true };
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasSnippets = true;
			}
		});

J
Joao Moreno 已提交
635
		if (!languages.length) {
636
			return false;
J
Joao Moreno 已提交
637 638
		}

J
Joao Moreno 已提交
639
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
640 641 642
			$('summary', null, localize('languages', "Languages ({0})", languages.length)),
			$('table', null,
				$('tr', null,
J
Joao Moreno 已提交
643 644
					$('th', null, localize('language id', "ID")),
					$('th', null, localize('language name', "Name")),
J
Joao Moreno 已提交
645
					$('th', null, localize('file extensions', "File Extensions")),
J
Joao Moreno 已提交
646 647
					$('th', null, localize('grammar', "Grammar")),
					$('th', null, localize('snippets', "Snippets"))
J
Joao Moreno 已提交
648 649
				),
				...languages.map(l => $('tr', null,
J
Joao Moreno 已提交
650
					$('td', null, l.id),
J
Joao Moreno 已提交
651
					$('td', null, l.name),
J
Joao Moreno 已提交
652
					$('td', null, ...join(l.extensions.map(ext => $('code', null, ext)), ' ')),
J
Joao Moreno 已提交
653 654
					$('td', null, document.createTextNode(l.hasGrammar ? '✔︎' : '')),
					$('td', null, document.createTextNode(l.hasSnippets ? '✔︎' : ''))
J
Joao Moreno 已提交
655 656
				))
			)
J
Joao Moreno 已提交
657 658 659
		);

		append(container, details);
660
		return true;
J
Joao Moreno 已提交
661 662
	}

663 664 665
	private keybindingToLabel(rawKeyBinding: IKeyBinding): string {
		let key: string;

J
Johannes Rieken 已提交
666
		switch (process.platform) {
667 668 669 670 671
			case 'win32': key = rawKeyBinding.win; break;
			case 'linux': key = rawKeyBinding.linux; break;
			case 'darwin': key = rawKeyBinding.mac; break;
		}

672
		const keyBinding = createKeybinding(KeybindingIO.readKeybinding(key || rawKeyBinding.key));
673 674
		const resolvedKeybinding = this.keybindingService.resolveKeybinding(keyBinding);
		const result = resolvedKeybinding.getLabel();
J
Joao Moreno 已提交
675
		return result === 'unknown' ? null : result;
676 677
	}

J
Johannes Rieken 已提交
678
	private loadContents(loadingTask: () => TPromise<any>): void {
J
Joao Moreno 已提交
679
		addClass(this.content, 'loading');
J
Joao Moreno 已提交
680

J
Joao Moreno 已提交
681 682
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
683

J
Joao Moreno 已提交
684
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
685 686 687
	}

	layout(): void {
J
Joao Moreno 已提交
688
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
689 690
	}

691 692 693 694 695 696 697 698
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

		this.messageService.show(Severity.Error, err);
	}

J
Joao Moreno 已提交
699 700
	dispose(): void {
		this._highlight = null;
J
Joao Moreno 已提交
701 702
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
703 704 705
		super.dispose();
	}
}