extensionEditor.ts 23.0 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';
J
Joao Moreno 已提交
24
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
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';
J
Joao Moreno 已提交
28
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
29
import { ExtensionsInput } from './extensionsInput';
S
Sandeep Somavarapu 已提交
30
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from './extensions';
31
import { Renderer, DataSource, Controller } from './dependenciesViewer';
32
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
J
Joao Moreno 已提交
33
import { ITemplateData } from './extensionsList';
34
import { RatingsWidget, InstallWidget } from './extensionsWidgets';
J
Joao Moreno 已提交
35
import { EditorOptions } from 'vs/workbench/common/editor';
J
Joao Moreno 已提交
36 37
import { shell } from 'electron';
import product from 'vs/platform/product';
J
Joao Moreno 已提交
38
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
J
Joao Moreno 已提交
39
import { CombinedInstallAction, UpdateAction, EnableAction, BuiltinStatusLabelAction } from './extensionsActions';
J
Joao Moreno 已提交
40
import WebView from 'vs/workbench/parts/html/browser/webview';
A
Alexandru Dima 已提交
41
import { Keybinding } from 'vs/base/common/keybinding';
J
Joao Moreno 已提交
42
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
J
Joao Moreno 已提交
43
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
44
import { IMessageService } from 'vs/platform/message/common/message';
45
import { IOpenerService } from 'vs/platform/opener/common/opener';
S
Sandeep Somavarapu 已提交
46
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
J
Joao Moreno 已提交
47 48 49 50 51 52 53 54 55 56 57

function renderBody(body: string): string {
	return `<!DOCTYPE html>
		<html>
			<head>
				<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
				<link rel="stylesheet" type="text/css" href="${ require.toUrl('./media/markdown.css') }" >
			</head>
			<body>${ body }</body>
		</html>`;
}
J
Joao Moreno 已提交
58

J
Joao Moreno 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
class NavBar {

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

	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 {
		const run = () => {
			this._onChange.fire(id);
			this.actions.forEach(a => a.enabled = a.id !== action.id);
			return TPromise.as(null);
		};

		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();
	}

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

const NavbarSection = {
	Readme: 'readme',
X
XVincentX 已提交
102
	Contributions: 'contributions',
S
Sandeep Somavarapu 已提交
103 104
	Changelog: 'changelog',
	Dependencies: 'dependencies'
J
Joao Moreno 已提交
105 106
};

J
Joao Moreno 已提交
107 108 109 110
interface ILayoutParticipant {
	layout(): void;
}

J
Joao Moreno 已提交
111 112 113 114
export class ExtensionEditor extends BaseEditor {

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

115
	private icon: HTMLImageElement;
J
Joao Moreno 已提交
116
	private name: HTMLElement;
S
Sandeep Somavarapu 已提交
117
	private identifier: HTMLElement;
J
Joao Moreno 已提交
118 119
	private license: HTMLElement;
	private publisher: HTMLElement;
J
Joao Moreno 已提交
120
	private installCount: HTMLElement;
J
Joao Moreno 已提交
121
	private rating: HTMLElement;
J
Joao Moreno 已提交
122
	private description: HTMLElement;
J
Joao Moreno 已提交
123 124 125
	private extensionActionBar: ActionBar;
	private navbar: NavBar;
	private content: HTMLElement;
J
Joao Moreno 已提交
126 127 128 129

	private _highlight: ITemplateData;
	private highlightDisposable: IDisposable;

J
Joao Moreno 已提交
130
	private extensionReadme: Cache<string>;
X
XVincentX 已提交
131
	private extensionChangelog: Cache<string>;
J
Joao Moreno 已提交
132
	private extensionManifest: Cache<IExtensionManifest>;
S
Sandeep Somavarapu 已提交
133
	private extensionDependencies: Cache<IExtensionDependencies>;
J
Joao Moreno 已提交
134

J
Joao Moreno 已提交
135
	private layoutParticipants: ILayoutParticipant[] = [];
J
Joao Moreno 已提交
136 137
	private contentDisposables: IDisposable[] = [];
	private transientDisposables: IDisposable[] = [];
J
Joao Moreno 已提交
138
	private disposables: IDisposable[];
J
Joao Moreno 已提交
139 140 141

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
142 143
		@IExtensionGalleryService private galleryService: IExtensionGalleryService,
		@IConfigurationService private configurationService: IConfigurationService,
J
Joao Moreno 已提交
144
		@IInstantiationService private instantiationService: IInstantiationService,
145
		@IViewletService private viewletService: IViewletService,
J
Joao Moreno 已提交
146
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
147
		@IThemeService private themeService: IThemeService,
148
		@IKeybindingService private keybindingService: IKeybindingService,
149 150
		@IMessageService private messageService: IMessageService,
		@IOpenerService private openerService: IOpenerService
J
Joao Moreno 已提交
151 152 153 154
	) {
		super(ExtensionEditor.ID, telemetryService);
		this._highlight = null;
		this.highlightDisposable = empty;
J
Joao Moreno 已提交
155
		this.disposables = [];
J
Joao Moreno 已提交
156
		this.extensionReadme = null;
X
XVincentX 已提交
157
		this.extensionChangelog = null;
J
Joao Moreno 已提交
158
		this.extensionManifest = null;
S
Sandeep Somavarapu 已提交
159
		this.extensionDependencies = null;
J
Joao Moreno 已提交
160 161 162 163
	}

	createEditor(parent: Builder): void {
		const container = parent.getHTMLElement();
J
Joao Moreno 已提交
164 165 166

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

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

J
Joao Moreno 已提交
170
		const details = append(header, $('.details'));
J
Joao Moreno 已提交
171
		const title = append(details, $('.title'));
J
Joao Moreno 已提交
172
		this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
J
Joao Moreno 已提交
173
		this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
J
Joao Moreno 已提交
174 175

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

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

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

J
Joao Moreno 已提交
182
		this.license = append(subtitle, $('span.license.clickable'));
J
Joao Moreno 已提交
183
		this.license.textContent = localize('license', 'License');
184
		this.license.style.display = 'none';
J
Joao Moreno 已提交
185

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

J
Joao Moreno 已提交
188 189 190 191
		const extensionActions = append(details, $('.actions'));
		this.extensionActionBar = new ActionBar(extensionActions, { animated: false });
		this.disposables.push(this.extensionActionBar);

J
Joao Moreno 已提交
192 193 194 195
		chain(fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run'))
			.map(({ error }) => error)
			.filter(error => !!error)
			.on(this.onError, this, this.disposables);
196

J
Joao Moreno 已提交
197 198
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
199

J
Joao Moreno 已提交
200
		this.content = append(body, $('.content'));
J
Joao Moreno 已提交
201 202 203
	}

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

J
Joao Moreno 已提交
206 207
		this.transientDisposables = dispose(this.transientDisposables);

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

J
Joao Moreno 已提交
210
		this.extensionReadme = new Cache(() => extension.getReadme());
X
XVincentX 已提交
211
		this.extensionChangelog = new Cache(() => extension.getChangelog());
J
Joao Moreno 已提交
212
		this.extensionManifest = new Cache(() => extension.getManifest());
S
Sandeep Somavarapu 已提交
213
		this.extensionDependencies = new Cache(() => this.extensionsWorkbenchService.loadDependencies(extension));
J
Joao Moreno 已提交
214

215 216 217 218
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

J
Joao Moreno 已提交
219
		this.name.textContent = extension.displayName;
S
Sandeep Somavarapu 已提交
220
		this.identifier.textContent = `${extension.publisher}.${extension.name}`;
J
Joao Moreno 已提交
221 222
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
223

J
Joao Moreno 已提交
224 225 226
		if (product.extensionsGallery) {
			const extensionUrl = `${ product.extensionsGallery.itemUrl }?itemName=${ extension.publisher }.${ extension.name }`;

J
Joao Moreno 已提交
227 228 229
			this.name.onclick = finalHandler(() => shell.openExternal(extensionUrl));
			this.rating.onclick = finalHandler(() => shell.openExternal(`${ extensionUrl }#review-details`));
			this.publisher.onclick = finalHandler(() => {
230
				this.viewletService.openViewlet(VIEWLET_ID, true)
J
Joao Moreno 已提交
231
					.then(viewlet => viewlet as IExtensionsViewlet)
232
					.done(viewlet => viewlet.search(`publisher:"${ extension.publisherDisplayName }"`));
J
Joao Moreno 已提交
233
			});
234 235 236 237 238 239 240 241

			if (extension.licenseUrl) {
				this.license.onclick = finalHandler(() => shell.openExternal(extension.licenseUrl));
				this.license.style.display = 'initial';
			} else {
				this.license.onclick = null;
				this.license.style.display = 'none';
			}
J
Joao Moreno 已提交
242 243
		}

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

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

J
Joao Moreno 已提交
250
		const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
J
Joao Moreno 已提交
251 252 253 254 255
		const installAction = this.instantiationService.createInstance(CombinedInstallAction);
		const updateAction = this.instantiationService.createInstance(UpdateAction);
		const enableAction = this.instantiationService.createInstance(EnableAction);

		installAction.extension = extension;
J
Joao Moreno 已提交
256
		builtinStatusAction.extension = extension;
J
Joao Moreno 已提交
257 258 259
		updateAction.extension = extension;
		enableAction.extension = extension;

J
Joao Moreno 已提交
260
		this.extensionActionBar.clear();
J
Joao Moreno 已提交
261 262
		this.extensionActionBar.push([enableAction, updateAction, installAction, builtinStatusAction], { icon: true, label: true });
		this.transientDisposables.push(enableAction, updateAction, installAction, builtinStatusAction);
J
Joao Moreno 已提交
263

J
Joao Moreno 已提交
264 265
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
266 267
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"));
J
Joao Moreno 已提交
268

269 270 271
		if (extension.hasChangelog) {
			this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"));
		}
S
Sandeep Somavarapu 已提交
272 273 274
		if (extension.hasDependencies) {
			this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"));
		}
J
Joao Moreno 已提交
275 276 277 278 279 280 281

		this.content.innerHTML = '';

		return super.setInput(input, options);
	}

	private onNavbarChange(extension: IExtension, id: string): void {
S
Sandeep Somavarapu 已提交
282 283
		this.contentDisposables = dispose(this.contentDisposables);
		this.content.innerHTML = '';
J
Joao Moreno 已提交
284
		switch (id) {
285 286 287
			case NavbarSection.Readme: return this.openReadme();
			case NavbarSection.Contributions: return this.openContributions();
			case NavbarSection.Changelog: return this.openChangelog();
S
Sandeep Somavarapu 已提交
288
			case NavbarSection.Dependencies: return this.openDependencies();
J
Joao Moreno 已提交
289
		}
J
Joao Moreno 已提交
290
	}
J
Joao Moreno 已提交
291

292
	private openMarkdown(content: TPromise<string>, noContentCopy: string) {
293
		return this.loadContents(() => content
J
Joao Moreno 已提交
294
			.then(marked.parse)
J
Joao Moreno 已提交
295
			.then(renderBody)
J
Joao Moreno 已提交
296 297 298 299 300 301 302
			.then<void>(body => {
				const webview = new WebView(
					this.content,
					document.querySelector('.monaco-editor-background')
				);

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

305 306 307
				webview.onDidClickLink(link => this.openerService.open(link), null, this.contentDisposables);
				this.themeService.onDidColorThemeChange(themeId => webview.style(themeId), null, this.contentDisposables);
				this.contentDisposables.push(webview);
J
Joao Moreno 已提交
308
			})
J
Joao Moreno 已提交
309 310
			.then(null, () => {
				const p = append(this.content, $('p'));
X
XVincentX 已提交
311
				p.textContent = noContentCopy;
J
Joao Moreno 已提交
312
			}));
J
Joao Moreno 已提交
313
	}
J
Joao Moreno 已提交
314

315 316
	private openReadme() {
		return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available."));
317 318
	}

319 320
	private openChangelog() {
		return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No CHANGELOG available."));
321 322
	}

323
	private openContributions() {
J
Joao Moreno 已提交
324
		return this.loadContents(() => this.extensionManifest.get()
325
			.then(manifest => {
J
Joao Moreno 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
				const content = $('div', { class: 'subcontent' });
				const scrollableContent = new DomScrollableElement(content, { canUseTranslate3d: false });
				append(this.content, scrollableContent.getDomNode());
				this.contentDisposables.push(scrollableContent);

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

				ExtensionEditor.renderSettings(content, manifest, layout);
				this.renderCommands(content, manifest, layout);
				ExtensionEditor.renderLanguages(content, manifest, layout);
				ExtensionEditor.renderThemes(content, manifest, layout);
				ExtensionEditor.renderJSONValidation(content, manifest, layout);
				ExtensionEditor.renderDebuggers(content, manifest, layout);

				scrollableContent.scanDomNode();
343
			}));
J
Joao Moreno 已提交
344 345
	}

S
Sandeep Somavarapu 已提交
346 347 348 349 350 351 352 353 354 355
	private openDependencies() {
		addClass(this.content, 'loading');
		this.extensionDependencies.get().then(extensionDependencies => {
			removeClass(this.content, 'loading');

			const content = $('div', { class: 'subcontent' });
			const scrollableContent = new DomScrollableElement(content, { canUseTranslate3d: false });
			append(this.content, scrollableContent.getDomNode());
			this.contentDisposables.push(scrollableContent);

356
			const tree = ExtensionEditor.renderDependencies(content, extensionDependencies, this.instantiationService);
357 358 359 360
			const layout = () => {
				scrollableContent.scanDomNode();
				tree.layout(scrollableContent.getHeight());
			};
S
Sandeep Somavarapu 已提交
361 362 363
			const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
			this.contentDisposables.push(toDisposable(removeLayoutParticipant));

364
			this.contentDisposables.push(tree);
S
Sandeep Somavarapu 已提交
365 366 367 368
			scrollableContent.scanDomNode();
		});
	}

369
	private static renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies, instantiationService: IInstantiationService): Tree {
370
		const renderer = new Renderer();
371
		const controller = instantiationService.createInstance(Controller);
S
Sandeep Somavarapu 已提交
372 373
		const tree = new Tree(container, {
			dataSource: new DataSource(),
374 375
			renderer,
			controller
S
Sandeep Somavarapu 已提交
376 377 378 379 380 381 382 383
		}, {
				indentPixels: 40,
				twistiePixels: 20
			});
		tree.setInput(extensionDependencies);
		return tree;
	}

J
Joao Moreno 已提交
384
	private static renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
385 386
		const contributes = manifest.contributes;
		const configuration = contributes && contributes.configuration;
J
Joao Moreno 已提交
387
		const properties = configuration && configuration.properties;
J
Joao Moreno 已提交
388
		const contrib = properties ? Object.keys(properties) : [];
J
Joao Moreno 已提交
389

J
Joao Moreno 已提交
390
		if (!contrib.length) {
J
Joao Moreno 已提交
391 392 393
			return;
		}

J
Joao Moreno 已提交
394
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
395
			$('summary', null, localize('settings', "Settings ({0})", contrib.length)),
J
Joao Moreno 已提交
396
			$('table', null,
J
Joao Moreno 已提交
397 398 399
				$('tr', null,
					$('th', null, localize('setting name', "Name")),
					$('th', null, localize('description', "Description")),
J
nls  
Joao Moreno 已提交
400
					$('th', null, localize('default', "Default"))
J
Joao Moreno 已提交
401 402 403 404 405 406
				),
				...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 已提交
407
			)
J
Joao Moreno 已提交
408 409 410
		);

		append(container, details);
J
Joao Moreno 已提交
411 412
	}

J
Joao Moreno 已提交
413
	private static renderDebuggers(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
414 415
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.debuggers || [];
J
Joao Moreno 已提交
416

J
Joao Moreno 已提交
417
		if (!contrib.length) {
J
Joao Moreno 已提交
418 419 420
			return;
		}

J
Joao Moreno 已提交
421
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
422
			$('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)),
J
Joao Moreno 已提交
423
			$('table', null,
J
Joao Moreno 已提交
424 425
				$('tr', null, $('th', null, localize('debugger name', "Name"))),
				...contrib.map(d => $('tr', null, $('td', null, d.label || d.type)))
J
Joao Moreno 已提交
426
			)
J
Joao Moreno 已提交
427 428 429
		);

		append(container, details);
J
Joao Moreno 已提交
430 431
	}

J
Joao Moreno 已提交
432
	private static renderThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
433 434
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.themes || [];
J
Joao Moreno 已提交
435

J
Joao Moreno 已提交
436
		if (!contrib.length) {
J
Joao Moreno 已提交
437 438 439
			return;
		}

J
Joao Moreno 已提交
440
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
441
			$('summary', null, localize('themes', "Themes ({0})", contrib.length)),
J
Joao Moreno 已提交
442 443 444 445
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
J
Joao Moreno 已提交
446 447
	}

J
Joao Moreno 已提交
448
	private static renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
449 450
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
451 452 453 454 455

		if (!contrib.length) {
			return;
		}

J
Joao Moreno 已提交
456
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
457
			$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
J
Joao Moreno 已提交
458 459 460 461
			$('ul', null, ...contrib.map(v => $('li', null, v.fileMatch)))
		);

		append(container, details);
J
Joao Moreno 已提交
462 463
	}

J
Joao Moreno 已提交
464
	private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
465 466
		const contributes = manifest.contributes;
		const rawCommands = contributes && contributes.commands || [];
J
Joao Moreno 已提交
467
		const commands = rawCommands.map(c => ({
468 469
			id: c.command,
			title: c.title,
J
Joao Moreno 已提交
470
			keybindings: [],
471 472 473
			menus: []
		}));

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

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

478 479
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
480
				let command = byId[menu.command];
481 482

				if (!command) {
J
Joao Moreno 已提交
483
					command = { id: menu.command, title: '', keybindings: [], menus: [context] };
J
Joao Moreno 已提交
484
					byId[command.id] = command;
485 486 487 488 489 490 491
					commands.push(command);
				} else {
					command.menus.push(context);
				}
			});
		});

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

494 495
		rawKeybindings.forEach(rawKeybinding => {
			const keyLabel = this.keybindingToLabel(rawKeybinding);
J
Joao Moreno 已提交
496
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
497 498

			if (!command) {
499
				command = { id: rawKeybinding.command, title: '', keybindings: [keyLabel], menus: [] };
J
Joao Moreno 已提交
500
				byId[command.id] = command;
J
Joao Moreno 已提交
501 502
				commands.push(command);
			} else {
503
				command.keybindings.push(keyLabel);
J
Joao Moreno 已提交
504 505 506
			}
		});

507 508 509 510
		if (!commands.length) {
			return;
		}

J
Joao Moreno 已提交
511
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
512 513 514 515 516
			$('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 已提交
517
					$('th', null, localize('keyboard shortcuts', "Keyboard Shortcuts")),
518 519 520
					$('th', null, localize('menuContexts', "Menu Contexts"))
				),
				...commands.map(c => $('tr', null,
521
					$('td', null, $('code', null, c.id)),
522
					$('td', null, c.title),
523
					$('td', null, ...join(c.keybindings.map(keybinding => $('code', null, keybinding)), ' ')),
524 525 526
					$('td', null, ...c.menus.map(context => $('code', null, context)))
				))
			)
J
Joao Moreno 已提交
527 528 529
		);

		append(container, details);
530 531
	}

J
Joao Moreno 已提交
532
	private static renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
533 534
		const contributes = manifest.contributes;
		const rawLanguages = contributes && contributes.languages || [];
J
Joao Moreno 已提交
535 536
		const languages = rawLanguages.map(l => ({
			id: l.id,
J
Joao Moreno 已提交
537
			name: (l.aliases || [])[0] || l.id,
J
Joao Moreno 已提交
538 539 540
			extensions: l.extensions || [],
			hasGrammar: false,
			hasSnippets: false
J
Joao Moreno 已提交
541 542
		}));

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

J
Joao Moreno 已提交
545
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
546 547 548 549 550

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

			if (!language) {
J
Joao Moreno 已提交
551
				language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false };
J
Joao Moreno 已提交
552 553 554 555 556 557 558
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasGrammar = true;
			}
		});

J
Joao Moreno 已提交
559 560 561 562 563 564 565 566 567 568 569 570 571 572
		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 已提交
573 574 575 576
		if (!languages.length) {
			return;
		}

J
Joao Moreno 已提交
577
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
578 579 580
			$('summary', null, localize('languages', "Languages ({0})", languages.length)),
			$('table', null,
				$('tr', null,
J
Joao Moreno 已提交
581 582
					$('th', null, localize('language id', "ID")),
					$('th', null, localize('language name', "Name")),
J
Joao Moreno 已提交
583
					$('th', null, localize('file extensions', "File Extensions")),
J
Joao Moreno 已提交
584 585
					$('th', null, localize('grammar', "Grammar")),
					$('th', null, localize('snippets', "Snippets"))
J
Joao Moreno 已提交
586 587
				),
				...languages.map(l => $('tr', null,
J
Joao Moreno 已提交
588
					$('td', null, l.id),
J
Joao Moreno 已提交
589
					$('td', null, l.name),
J
Joao Moreno 已提交
590
					$('td', null, ...join(l.extensions.map(ext => $('code', null, ext)), ' ')),
J
Joao Moreno 已提交
591 592
					$('td', null, document.createTextNode(l.hasGrammar ? '✔︎' : '')),
					$('td', null, document.createTextNode(l.hasSnippets ? '✔︎' : ''))
J
Joao Moreno 已提交
593 594
				))
			)
J
Joao Moreno 已提交
595 596 597
		);

		append(container, details);
J
Joao Moreno 已提交
598 599
	}

600 601 602 603 604 605 606 607 608 609 610 611 612
	private keybindingToLabel(rawKeyBinding: IKeyBinding): string {
		let key: string;

		switch(process.platform) {
			case 'win32': key = rawKeyBinding.win; break;
			case 'linux': key = rawKeyBinding.linux; break;
			case 'darwin': key = rawKeyBinding.mac; break;
		}

		const keyBinding = new Keybinding(Keybinding.fromUserSettingsLabel(key || rawKeyBinding.key));
		return this.keybindingService.getLabelFor(keyBinding);
	}

J
Joao Moreno 已提交
613 614
	private loadContents(loadingTask: ()=>TPromise<any>): void {
		addClass(this.content, 'loading');
J
Joao Moreno 已提交
615

J
Joao Moreno 已提交
616 617
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
618

J
Joao Moreno 已提交
619
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
620 621 622
	}

	layout(): void {
J
Joao Moreno 已提交
623
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
624 625
	}

626 627 628 629 630 631 632 633
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

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

J
Joao Moreno 已提交
634 635
	dispose(): void {
		this._highlight = null;
J
Joao Moreno 已提交
636 637
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
638 639 640
		super.dispose();
	}
}