extensionEditor.ts 20.4 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';
14
import Event, { Emitter, once, fromEventEmitter, filterEvent, mapEvent } 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 18
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
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';
24
import { IViewlet } from 'vs/workbench/common/viewlet';
J
Joao Moreno 已提交
25
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
J
Joao Moreno 已提交
26 27
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
28
import { IExtensionGalleryService, IExtensionManifest, IKeyBinding } from 'vs/platform/extensionManagement/common/extensionManagement';
J
Joao Moreno 已提交
29
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
30
import { ExtensionsInput } from './extensionsInput';
J
Joao Moreno 已提交
31
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension } from './extensions';
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';
39
import { CombinedInstallAction, UpdateAction, EnableAction } from './extensionsActions';
J
Joao Moreno 已提交
40
import WebView from 'vs/workbench/parts/html/browser/webview';
41
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
J
Joao Moreno 已提交
42 43
import { Keybinding } from 'vs/base/common/keyCodes';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
J
Joao Moreno 已提交
44
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
45
import { IMessageService } from 'vs/platform/message/common/message';
J
Joao Moreno 已提交
46 47 48 49 50 51 52 53 54 55 56

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 已提交
57

J
Joao Moreno 已提交
58 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
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',
101
	Contributions: 'contributions'
J
Joao Moreno 已提交
102 103
};

J
Joao Moreno 已提交
104 105 106 107
interface ILayoutParticipant {
	layout(): void;
}

J
Joao Moreno 已提交
108 109 110 111
export class ExtensionEditor extends BaseEditor {

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

112
	private icon: HTMLImageElement;
J
Joao Moreno 已提交
113
	private name: HTMLAnchorElement;
J
Joao Moreno 已提交
114
	private license: HTMLAnchorElement;
J
Joao Moreno 已提交
115
	private publisher: HTMLAnchorElement;
J
Joao Moreno 已提交
116
	private installCount: HTMLElement;
J
Joao Moreno 已提交
117
	private rating: HTMLAnchorElement;
J
Joao Moreno 已提交
118
	private description: HTMLElement;
J
Joao Moreno 已提交
119 120 121
	private extensionActionBar: ActionBar;
	private navbar: NavBar;
	private content: HTMLElement;
J
Joao Moreno 已提交
122 123 124 125

	private _highlight: ITemplateData;
	private highlightDisposable: IDisposable;

J
Joao Moreno 已提交
126 127 128
	private extensionReadme: Cache<string>;
	private extensionManifest: Cache<IExtensionManifest>;

J
Joao Moreno 已提交
129
	private layoutParticipants: ILayoutParticipant[] = [];
J
Joao Moreno 已提交
130 131
	private contentDisposables: IDisposable[] = [];
	private transientDisposables: IDisposable[] = [];
J
Joao Moreno 已提交
132
	private disposables: IDisposable[];
J
Joao Moreno 已提交
133 134 135

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
136 137
		@IExtensionGalleryService private galleryService: IExtensionGalleryService,
		@IConfigurationService private configurationService: IConfigurationService,
J
Joao Moreno 已提交
138
		@IInstantiationService private instantiationService: IInstantiationService,
139
		@IViewletService private viewletService: IViewletService,
J
Joao Moreno 已提交
140
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
141
		@IThemeService private themeService: IThemeService,
J
Joao Moreno 已提交
142
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
143 144
		@IKeybindingService private keybindingService: IKeybindingService,
		@IMessageService private messageService: IMessageService
J
Joao Moreno 已提交
145 146 147 148
	) {
		super(ExtensionEditor.ID, telemetryService);
		this._highlight = null;
		this.highlightDisposable = empty;
J
Joao Moreno 已提交
149
		this.disposables = [];
J
Joao Moreno 已提交
150 151
		this.extensionReadme = null;
		this.extensionManifest = null;
152

153
		this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
J
Joao Moreno 已提交
154 155 156 157
	}

	createEditor(parent: Builder): void {
		const container = parent.getHTMLElement();
J
Joao Moreno 已提交
158 159 160

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

162
		this.icon = append(header, $<HTMLImageElement>('img.icon'));
J
Joao Moreno 已提交
163

J
Joao Moreno 已提交
164
		const details = append(header, $('.details'));
J
Joao Moreno 已提交
165 166
		const title = append(details, $('.title'));
		this.name = append(title, $<HTMLAnchorElement>('a.name'));
J
Joao Moreno 已提交
167
		this.name.href = '#';
J
Joao Moreno 已提交
168 169

		const subtitle = append(details, $('.subtitle'));
J
Joao Moreno 已提交
170 171
		this.publisher = append(subtitle, $<HTMLAnchorElement>('a.publisher'));
		this.publisher.href = '#';
J
Joao Moreno 已提交
172

173
		this.installCount = append(subtitle, $('span.install'));
J
Joao Moreno 已提交
174

J
Joao Moreno 已提交
175 176
		this.rating = append(subtitle, $<HTMLAnchorElement>('a.rating'));
		this.rating.href = '#';
J
Joao Moreno 已提交
177

J
Joao Moreno 已提交
178 179 180
		this.license = append(subtitle, $<HTMLAnchorElement>('a.license'));
		this.license.href = '#';
		this.license.textContent = localize('license', 'License');
181
		this.license.style.display = 'none';
J
Joao Moreno 已提交
182

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

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

189 190 191 192 193
		let onActionError = fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run');
		onActionError = mapEvent(onActionError, ({ error }) => error);
		onActionError = filterEvent(onActionError, error => !!error);
		onActionError(this.onError, this, this.disposables);

J
Joao Moreno 已提交
194 195
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
196

J
Joao Moreno 已提交
197
		this.content = append(body, $('.content'));
J
Joao Moreno 已提交
198 199 200
	}

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

J
Joao Moreno 已提交
203 204
		this.transientDisposables = dispose(this.transientDisposables);

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

J
Joao Moreno 已提交
207 208 209
		this.extensionReadme = new Cache(() => extension.getReadme());
		this.extensionManifest = new Cache(() => extension.getManifest());

210 211 212 213
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

J
Joao Moreno 已提交
214 215 216
		this.name.textContent = extension.displayName;
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
217

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

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

			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 已提交
236 237
		}

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

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

J
Joao Moreno 已提交
244 245 246 247 248 249 250 251
		const installAction = this.instantiationService.createInstance(CombinedInstallAction);
		const updateAction = this.instantiationService.createInstance(UpdateAction);
		const enableAction = this.instantiationService.createInstance(EnableAction);

		installAction.extension = extension;
		updateAction.extension = extension;
		enableAction.extension = extension;

J
Joao Moreno 已提交
252 253
		this.extensionActionBar.clear();
		this.extensionActionBar.push([enableAction, updateAction, installAction], { icon: true, label: true });
254
		this.transientDisposables.push(enableAction, updateAction, installAction);
J
Joao Moreno 已提交
255

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

		this.content.innerHTML = '';

		return super.setInput(input, options);
	}

	private onNavbarChange(extension: IExtension, id: string): void {
		switch (id) {
			case NavbarSection.Readme: return this.openReadme(extension);
269
			case NavbarSection.Contributions: return this.openContributions(extension);
J
Joao Moreno 已提交
270
		}
J
Joao Moreno 已提交
271
	}
J
Joao Moreno 已提交
272

J
Joao Moreno 已提交
273
	private openReadme(extension: IExtension) {
J
Joao Moreno 已提交
274
		return this.loadContents(() => this.extensionReadme.get()
J
Joao Moreno 已提交
275
			.then(marked.parse)
J
Joao Moreno 已提交
276
			.then(renderBody)
J
Joao Moreno 已提交
277 278 279 280 281 282 283
			.then<void>(body => {
				const webview = new WebView(
					this.content,
					document.querySelector('.monaco-editor-background')
				);

				webview.style(this.themeService.getColorTheme());
J
Joao Moreno 已提交
284
				webview.contents = [body];
J
Joao Moreno 已提交
285 286 287 288 289

				const linkListener = webview.onDidClickLink(link => shell.openExternal(link.toString()));
				const themeListener = this.themeService.onDidColorThemeChange(themeId => webview.style(themeId));
				this.contentDisposables.push(webview, linkListener, themeListener);
			})
J
Joao Moreno 已提交
290 291 292
			.then(null, () => {
				const p = append(this.content, $('p'));
				p.textContent = localize('noReadme', "No README available.");
J
Joao Moreno 已提交
293
			}));
J
Joao Moreno 已提交
294
	}
J
Joao Moreno 已提交
295

296
	private openContributions(extension: IExtension) {
J
Joao Moreno 已提交
297
		return this.loadContents(() => this.extensionManifest.get()
298
			.then(manifest => {
J
Joao Moreno 已提交
299
				this.content.innerHTML = '';
J
Joao Moreno 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

				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();
318
			}));
J
Joao Moreno 已提交
319 320
	}

J
Joao Moreno 已提交
321
	private static renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
322 323
		const contributes = manifest.contributes;
		const configuration = contributes && contributes.configuration;
J
Joao Moreno 已提交
324
		const properties = configuration && configuration.properties;
J
Joao Moreno 已提交
325
		const contrib = properties ? Object.keys(properties) : [];
J
Joao Moreno 已提交
326

J
Joao Moreno 已提交
327
		if (!contrib.length) {
J
Joao Moreno 已提交
328 329 330
			return;
		}

J
Joao Moreno 已提交
331
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
332
			$('summary', null, localize('settings', "Settings ({0})", contrib.length)),
J
Joao Moreno 已提交
333
			$('table', null,
J
Joao Moreno 已提交
334
				$('tr', null, $('th', null, localize('setting name', "Name")), $('th', null, localize('description', "Description"))),
J
Joao Moreno 已提交
335
				...contrib.map(key => $('tr', null, $('td', null, $('code', null, key)), $('td', null, properties[key].description)))
J
Joao Moreno 已提交
336
			)
J
Joao Moreno 已提交
337 338 339
		);

		append(container, details);
J
Joao Moreno 已提交
340 341
	}

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

J
Joao Moreno 已提交
346
		if (!contrib.length) {
J
Joao Moreno 已提交
347 348 349
			return;
		}

J
Joao Moreno 已提交
350
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
351
			$('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)),
J
Joao Moreno 已提交
352
			$('table', null,
J
Joao Moreno 已提交
353
				$('tr', null, $('th', null, localize('debugger name', "Name")), $('th', null, localize('runtime', "Runtime"))),
J
Joao Moreno 已提交
354
				...contrib.map(d => $('tr', null, $('td', null, d.label || d.type), $('td', null, d.runtime)))
J
Joao Moreno 已提交
355
			)
J
Joao Moreno 已提交
356 357 358
		);

		append(container, details);
J
Joao Moreno 已提交
359 360
	}

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

J
Joao Moreno 已提交
365
		if (!contrib.length) {
J
Joao Moreno 已提交
366 367 368
			return;
		}

J
Joao Moreno 已提交
369
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
370
			$('summary', null, localize('themes', "Themes ({0})", contrib.length)),
J
Joao Moreno 已提交
371 372 373 374
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
J
Joao Moreno 已提交
375 376
	}

J
Joao Moreno 已提交
377
	private static renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
378 379
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
380 381 382 383 384

		if (!contrib.length) {
			return;
		}

J
Joao Moreno 已提交
385
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
386
			$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
J
Joao Moreno 已提交
387 388 389 390
			$('ul', null, ...contrib.map(v => $('li', null, v.fileMatch)))
		);

		append(container, details);
J
Joao Moreno 已提交
391 392
	}

J
Joao Moreno 已提交
393
	private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
394 395
		const contributes = manifest.contributes;
		const rawCommands = contributes && contributes.commands || [];
J
Joao Moreno 已提交
396
		const commands = rawCommands.map(c => ({
397 398
			id: c.command,
			title: c.title,
J
Joao Moreno 已提交
399
			keybindings: [],
400 401 402
			menus: []
		}));

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

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

407 408
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
409
				let command = byId[menu.command];
410 411

				if (!command) {
J
Joao Moreno 已提交
412
					command = { id: menu.command, title: '', keybindings: [], menus: [context] };
J
Joao Moreno 已提交
413
					byId[command.id] = command;
414 415 416 417 418 419 420
					commands.push(command);
				} else {
					command.menus.push(context);
				}
			});
		});

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

423 424
		rawKeybindings.forEach(rawKeybinding => {
			const keyLabel = this.keybindingToLabel(rawKeybinding);
J
Joao Moreno 已提交
425
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
426 427

			if (!command) {
428
				command = { id: rawKeybinding.command, title: '', keybindings: [keyLabel], menus: [] };
J
Joao Moreno 已提交
429
				byId[command.id] = command;
J
Joao Moreno 已提交
430 431
				commands.push(command);
			} else {
432
				command.keybindings.push(keyLabel);
J
Joao Moreno 已提交
433 434 435
			}
		});

436 437 438 439
		if (!commands.length) {
			return;
		}

J
Joao Moreno 已提交
440
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
441 442 443 444 445
			$('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 已提交
446
					$('th', null, localize('keyboard shortcuts', "Keyboard Shortcuts")),
447 448 449
					$('th', null, localize('menuContexts', "Menu Contexts"))
				),
				...commands.map(c => $('tr', null,
450
					$('td', null, $('code', null, c.id)),
451
					$('td', null, c.title),
452
					$('td', null, ...join(c.keybindings.map(keybinding => $('code', null, keybinding)), ' ')),
453 454 455
					$('td', null, ...c.menus.map(context => $('code', null, context)))
				))
			)
J
Joao Moreno 已提交
456 457 458
		);

		append(container, details);
459 460
	}

J
Joao Moreno 已提交
461
	private static renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
462 463
		const contributes = manifest.contributes;
		const rawLanguages = contributes && contributes.languages || [];
J
Joao Moreno 已提交
464 465
		const languages = rawLanguages.map(l => ({
			id: l.id,
J
Joao Moreno 已提交
466
			name: (l.aliases || [])[0] || l.id,
J
Joao Moreno 已提交
467 468 469
			extensions: l.extensions || [],
			hasGrammar: false,
			hasSnippets: false
J
Joao Moreno 已提交
470 471
		}));

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

J
Joao Moreno 已提交
474
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
475 476 477 478 479

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

			if (!language) {
J
Joao Moreno 已提交
480
				language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false };
J
Joao Moreno 已提交
481 482 483 484 485 486 487
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasGrammar = true;
			}
		});

J
Joao Moreno 已提交
488 489 490 491 492 493 494 495 496 497 498 499 500 501
		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 已提交
502 503 504 505
		if (!languages.length) {
			return;
		}

J
Joao Moreno 已提交
506
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
507 508 509 510
			$('summary', null, localize('languages', "Languages ({0})", languages.length)),
			$('table', null,
				$('tr', null,
					$('th', null, localize('command name', "Name")),
J
Joao Moreno 已提交
511
					$('th', null, localize('file extensions', "File Extensions")),
J
Joao Moreno 已提交
512 513
					$('th', null, localize('grammar', "Grammar")),
					$('th', null, localize('snippets', "Snippets"))
J
Joao Moreno 已提交
514 515 516
				),
				...languages.map(l => $('tr', null,
					$('td', null, l.name),
J
Joao Moreno 已提交
517
					$('td', null, ...join(l.extensions.map(ext => $('code', null, ext)), ' ')),
J
Joao Moreno 已提交
518 519
					$('td', null, document.createTextNode(l.hasGrammar ? '✔︎' : '')),
					$('td', null, document.createTextNode(l.hasSnippets ? '✔︎' : ''))
J
Joao Moreno 已提交
520 521
				))
			)
J
Joao Moreno 已提交
522 523 524
		);

		append(container, details);
J
Joao Moreno 已提交
525 526
	}

527 528 529 530 531 532 533 534 535 536 537 538 539
	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 已提交
540 541 542
	private loadContents(loadingTask: ()=>TPromise<any>): void {
		this.contentDisposables = dispose(this.contentDisposables);

J
Joao Moreno 已提交
543
		this.content.innerHTML = '';
J
Joao Moreno 已提交
544
		addClass(this.content, 'loading');
J
Joao Moreno 已提交
545

J
Joao Moreno 已提交
546 547
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
548

J
Joao Moreno 已提交
549
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
550 551 552
	}

	layout(): void {
J
Joao Moreno 已提交
553
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
554 555
	}

556
	private onViewletOpen(viewlet: IViewlet): void {
557 558 559 560 561 562 563
		if (!viewlet || viewlet.getId() === VIEWLET_ID) {
			return;
		}

		this.editorService.closeEditor(this.position, this.input).done(null, onUnexpectedError);
	}

564 565 566 567 568 569 570 571
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

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

J
Joao Moreno 已提交
572 573
	dispose(): void {
		this._highlight = null;
J
Joao Moreno 已提交
574 575
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
576 577 578
		super.dispose();
	}
}