extensionEditor.ts 19.9 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
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';
J
Joao Moreno 已提交
30
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension } from './extensions';
31
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
J
Joao Moreno 已提交
32
import { ITemplateData } from './extensionsList';
33
import { RatingsWidget, InstallWidget } from './extensionsWidgets';
J
Joao Moreno 已提交
34
import { EditorOptions } from 'vs/workbench/common/editor';
J
Joao Moreno 已提交
35 36
import { shell } from 'electron';
import product from 'vs/platform/product';
J
Joao Moreno 已提交
37
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
38
import { CombinedInstallAction, UpdateAction, EnableAction } from './extensionsActions';
J
Joao Moreno 已提交
39
import WebView from 'vs/workbench/parts/html/browser/webview';
40
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
J
Joao Moreno 已提交
41 42
import { Keybinding } from 'vs/base/common/keyCodes';
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';
J
Joao Moreno 已提交
45 46 47 48 49 50 51 52 53 54 55

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

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

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

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

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

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

	private _highlight: ITemplateData;
	private highlightDisposable: IDisposable;

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

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

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

	createEditor(parent: Builder): void {
		const container = parent.getHTMLElement();
J
Joao Moreno 已提交
155 156 157

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

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

J
Joao Moreno 已提交
161
		const details = append(header, $('.details'));
J
Joao Moreno 已提交
162
		const title = append(details, $('.title'));
J
Joao Moreno 已提交
163
		this.name = append(title, $('span.name.clickable'));
J
Joao Moreno 已提交
164 165

		const subtitle = append(details, $('.subtitle'));
J
Joao Moreno 已提交
166
		this.publisher = append(subtitle, $('span.publisher.clickable'));
J
Joao Moreno 已提交
167

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

J
Joao Moreno 已提交
170
		this.rating = append(subtitle, $('span.rating.clickable'));
J
Joao Moreno 已提交
171

J
Joao Moreno 已提交
172
		this.license = append(subtitle, $('span.license.clickable'));
J
Joao Moreno 已提交
173
		this.license.textContent = localize('license', 'License');
174
		this.license.style.display = 'none';
J
Joao Moreno 已提交
175

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

J
Joao Moreno 已提交
178 179 180 181
		const extensionActions = append(details, $('.actions'));
		this.extensionActionBar = new ActionBar(extensionActions, { animated: false });
		this.disposables.push(this.extensionActionBar);

182 183 184 185 186
		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 已提交
187 188
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
189

J
Joao Moreno 已提交
190
		this.content = append(body, $('.content'));
J
Joao Moreno 已提交
191 192 193
	}

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

J
Joao Moreno 已提交
196 197
		this.transientDisposables = dispose(this.transientDisposables);

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

J
Joao Moreno 已提交
200 201 202
		this.extensionReadme = new Cache(() => extension.getReadme());
		this.extensionManifest = new Cache(() => extension.getManifest());

203 204 205 206
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

J
Joao Moreno 已提交
207 208 209
		this.name.textContent = extension.displayName;
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
210

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

J
Joao Moreno 已提交
214 215 216
			this.name.onclick = finalHandler(() => shell.openExternal(extensionUrl));
			this.rating.onclick = finalHandler(() => shell.openExternal(`${ extensionUrl }#review-details`));
			this.publisher.onclick = finalHandler(() => {
217
				this.viewletService.openViewlet(VIEWLET_ID, true)
J
Joao Moreno 已提交
218
					.then(viewlet => viewlet as IExtensionsViewlet)
219
					.done(viewlet => viewlet.search(`publisher:"${ extension.publisherDisplayName }"`));
J
Joao Moreno 已提交
220
			});
221 222 223 224 225 226 227 228

			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 已提交
229 230
		}

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

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

J
Joao Moreno 已提交
237 238 239 240 241 242 243 244
		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 已提交
245 246
		this.extensionActionBar.clear();
		this.extensionActionBar.push([enableAction, updateAction, installAction], { icon: true, label: true });
247
		this.transientDisposables.push(enableAction, updateAction, installAction);
J
Joao Moreno 已提交
248

J
Joao Moreno 已提交
249 250
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
251 252
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"));
J
Joao Moreno 已提交
253 254 255 256 257 258 259 260 261

		this.content.innerHTML = '';

		return super.setInput(input, options);
	}

	private onNavbarChange(extension: IExtension, id: string): void {
		switch (id) {
			case NavbarSection.Readme: return this.openReadme(extension);
262
			case NavbarSection.Contributions: return this.openContributions(extension);
J
Joao Moreno 已提交
263
		}
J
Joao Moreno 已提交
264
	}
J
Joao Moreno 已提交
265

J
Joao Moreno 已提交
266
	private openReadme(extension: IExtension) {
J
Joao Moreno 已提交
267
		return this.loadContents(() => this.extensionReadme.get()
J
Joao Moreno 已提交
268
			.then(marked.parse)
J
Joao Moreno 已提交
269
			.then(renderBody)
J
Joao Moreno 已提交
270 271 272 273 274 275 276
			.then<void>(body => {
				const webview = new WebView(
					this.content,
					document.querySelector('.monaco-editor-background')
				);

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

J
Joao Moreno 已提交
279
				const linkListener = webview.onDidClickLink(link => shell.openExternal(link.toString(true)));
J
Joao Moreno 已提交
280 281 282
				const themeListener = this.themeService.onDidColorThemeChange(themeId => webview.style(themeId));
				this.contentDisposables.push(webview, linkListener, themeListener);
			})
J
Joao Moreno 已提交
283 284 285
			.then(null, () => {
				const p = append(this.content, $('p'));
				p.textContent = localize('noReadme', "No README available.");
J
Joao Moreno 已提交
286
			}));
J
Joao Moreno 已提交
287
	}
J
Joao Moreno 已提交
288

289
	private openContributions(extension: IExtension) {
J
Joao Moreno 已提交
290
		return this.loadContents(() => this.extensionManifest.get()
291
			.then(manifest => {
J
Joao Moreno 已提交
292
				this.content.innerHTML = '';
J
Joao Moreno 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

				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();
311
			}));
J
Joao Moreno 已提交
312 313
	}

J
Joao Moreno 已提交
314
	private static renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
315 316
		const contributes = manifest.contributes;
		const configuration = contributes && contributes.configuration;
J
Joao Moreno 已提交
317
		const properties = configuration && configuration.properties;
J
Joao Moreno 已提交
318
		const contrib = properties ? Object.keys(properties) : [];
J
Joao Moreno 已提交
319

J
Joao Moreno 已提交
320
		if (!contrib.length) {
J
Joao Moreno 已提交
321 322 323
			return;
		}

J
Joao Moreno 已提交
324
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
325
			$('summary', null, localize('settings', "Settings ({0})", contrib.length)),
J
Joao Moreno 已提交
326
			$('table', null,
J
Joao Moreno 已提交
327
				$('tr', null, $('th', null, localize('setting name', "Name")), $('th', null, localize('description', "Description"))),
J
Joao Moreno 已提交
328
				...contrib.map(key => $('tr', null, $('td', null, $('code', null, key)), $('td', null, properties[key].description)))
J
Joao Moreno 已提交
329
			)
J
Joao Moreno 已提交
330 331 332
		);

		append(container, details);
J
Joao Moreno 已提交
333 334
	}

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

J
Joao Moreno 已提交
339
		if (!contrib.length) {
J
Joao Moreno 已提交
340 341 342
			return;
		}

J
Joao Moreno 已提交
343
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
344
			$('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)),
J
Joao Moreno 已提交
345
			$('table', null,
J
Joao Moreno 已提交
346
				$('tr', null, $('th', null, localize('debugger name', "Name")), $('th', null, localize('runtime', "Runtime"))),
J
Joao Moreno 已提交
347
				...contrib.map(d => $('tr', null, $('td', null, d.label || d.type), $('td', null, d.runtime)))
J
Joao Moreno 已提交
348
			)
J
Joao Moreno 已提交
349 350 351
		);

		append(container, details);
J
Joao Moreno 已提交
352 353
	}

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

J
Joao Moreno 已提交
358
		if (!contrib.length) {
J
Joao Moreno 已提交
359 360 361
			return;
		}

J
Joao Moreno 已提交
362
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
363
			$('summary', null, localize('themes', "Themes ({0})", contrib.length)),
J
Joao Moreno 已提交
364 365 366 367
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
J
Joao Moreno 已提交
368 369
	}

J
Joao Moreno 已提交
370
	private static renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
371 372
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
373 374 375 376 377

		if (!contrib.length) {
			return;
		}

J
Joao Moreno 已提交
378
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
379
			$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
J
Joao Moreno 已提交
380 381 382 383
			$('ul', null, ...contrib.map(v => $('li', null, v.fileMatch)))
		);

		append(container, details);
J
Joao Moreno 已提交
384 385
	}

J
Joao Moreno 已提交
386
	private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
387 388
		const contributes = manifest.contributes;
		const rawCommands = contributes && contributes.commands || [];
J
Joao Moreno 已提交
389
		const commands = rawCommands.map(c => ({
390 391
			id: c.command,
			title: c.title,
J
Joao Moreno 已提交
392
			keybindings: [],
393 394 395
			menus: []
		}));

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

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

400 401
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
402
				let command = byId[menu.command];
403 404

				if (!command) {
J
Joao Moreno 已提交
405
					command = { id: menu.command, title: '', keybindings: [], menus: [context] };
J
Joao Moreno 已提交
406
					byId[command.id] = command;
407 408 409 410 411 412 413
					commands.push(command);
				} else {
					command.menus.push(context);
				}
			});
		});

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

416 417
		rawKeybindings.forEach(rawKeybinding => {
			const keyLabel = this.keybindingToLabel(rawKeybinding);
J
Joao Moreno 已提交
418
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
419 420

			if (!command) {
421
				command = { id: rawKeybinding.command, title: '', keybindings: [keyLabel], menus: [] };
J
Joao Moreno 已提交
422
				byId[command.id] = command;
J
Joao Moreno 已提交
423 424
				commands.push(command);
			} else {
425
				command.keybindings.push(keyLabel);
J
Joao Moreno 已提交
426 427 428
			}
		});

429 430 431 432
		if (!commands.length) {
			return;
		}

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

		append(container, details);
452 453
	}

J
Joao Moreno 已提交
454
	private static renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): void {
J
Joao Moreno 已提交
455 456
		const contributes = manifest.contributes;
		const rawLanguages = contributes && contributes.languages || [];
J
Joao Moreno 已提交
457 458
		const languages = rawLanguages.map(l => ({
			id: l.id,
J
Joao Moreno 已提交
459
			name: (l.aliases || [])[0] || l.id,
J
Joao Moreno 已提交
460 461 462
			extensions: l.extensions || [],
			hasGrammar: false,
			hasSnippets: false
J
Joao Moreno 已提交
463 464
		}));

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

J
Joao Moreno 已提交
467
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
468 469 470 471 472

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

			if (!language) {
J
Joao Moreno 已提交
473
				language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false };
J
Joao Moreno 已提交
474 475 476 477 478 479 480
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasGrammar = true;
			}
		});

J
Joao Moreno 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494
		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 已提交
495 496 497 498
		if (!languages.length) {
			return;
		}

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

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

520 521 522 523 524 525 526 527 528 529 530 531 532
	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 已提交
533 534 535
	private loadContents(loadingTask: ()=>TPromise<any>): void {
		this.contentDisposables = dispose(this.contentDisposables);

J
Joao Moreno 已提交
536
		this.content.innerHTML = '';
J
Joao Moreno 已提交
537
		addClass(this.content, 'loading');
J
Joao Moreno 已提交
538

J
Joao Moreno 已提交
539 540
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
541

J
Joao Moreno 已提交
542
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
543 544 545
	}

	layout(): void {
J
Joao Moreno 已提交
546
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
547 548
	}

549 550 551 552 553 554 555 556
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

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

J
Joao Moreno 已提交
557 558
	dispose(): void {
		this._highlight = null;
J
Joao Moreno 已提交
559 560
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
561 562 563
		super.dispose();
	}
}