extensionEditor.ts 23.1 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, StatusWidget } 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
Johannes Rieken 已提交
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

function renderBody(body: string): string {
	return `<!DOCTYPE html>
		<html>
			<head>
				<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
J
Johannes Rieken 已提交
53
				<link rel="stylesheet" type="text/css" href="${ require.toUrl('./media/markdown.css')}" >
J
Joao Moreno 已提交
54
			</head>
J
Johannes Rieken 已提交
55
			<body>${ body}</body>
J
Joao Moreno 已提交
56 57
		</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
	private extensionActionBar: ActionBar;
124
	private status: HTMLElement;
J
Joao Moreno 已提交
125 126
	private navbar: NavBar;
	private content: HTMLElement;
J
Joao Moreno 已提交
127 128 129 130

	private _highlight: ITemplateData;
	private highlightDisposable: IDisposable;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

J
Joao Moreno 已提交
208 209
		this.transientDisposables = dispose(this.transientDisposables);

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

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

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

J
Joao Moreno 已提交
221
		this.name.textContent = extension.displayName;
S
Sandeep Somavarapu 已提交
222
		this.identifier.textContent = `${extension.publisher}.${extension.name}`;
223
		this.transientDisposables.push(this.instantiationService.createInstance(StatusWidget, this.status, extension));
224

J
Joao Moreno 已提交
225 226
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
227

J
Joao Moreno 已提交
228
		if (product.extensionsGallery) {
J
Johannes Rieken 已提交
229
			const extensionUrl = `${product.extensionsGallery.itemUrl}?itemName=${extension.publisher}.${extension.name}`;
J
Joao Moreno 已提交
230

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

			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 已提交
246 247
		}

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

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

J
Joao Moreno 已提交
254
		const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
J
Joao Moreno 已提交
255 256 257 258 259
		const installAction = this.instantiationService.createInstance(CombinedInstallAction);
		const updateAction = this.instantiationService.createInstance(UpdateAction);
		const enableAction = this.instantiationService.createInstance(EnableAction);

		installAction.extension = extension;
J
Joao Moreno 已提交
260
		builtinStatusAction.extension = extension;
J
Joao Moreno 已提交
261 262 263
		updateAction.extension = extension;
		enableAction.extension = extension;

J
Joao Moreno 已提交
264
		this.extensionActionBar.clear();
J
Joao Moreno 已提交
265 266
		this.extensionActionBar.push([enableAction, updateAction, installAction, builtinStatusAction], { icon: true, label: true });
		this.transientDisposables.push(enableAction, updateAction, installAction, builtinStatusAction);
J
Joao Moreno 已提交
267

J
Joao Moreno 已提交
268 269
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
270 271
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"));
J
Joao Moreno 已提交
272

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

		this.content.innerHTML = '';

		return super.setInput(input, options);
	}

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

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

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

309 310 311
				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 已提交
312
			})
J
Joao Moreno 已提交
313 314
			.then(null, () => {
				const p = append(this.content, $('p'));
X
XVincentX 已提交
315
				p.textContent = noContentCopy;
J
Joao Moreno 已提交
316
			}));
J
Joao Moreno 已提交
317
	}
J
Joao Moreno 已提交
318

319 320
	private openReadme() {
		return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available."));
321 322
	}

323 324
	private openChangelog() {
		return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No CHANGELOG available."));
325 326
	}

327
	private openContributions() {
J
Joao Moreno 已提交
328
		return this.loadContents(() => this.extensionManifest.get()
329
			.then(manifest => {
J
Joao Moreno 已提交
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
				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();
347
			}));
J
Joao Moreno 已提交
348 349
	}

S
Sandeep Somavarapu 已提交
350 351 352 353 354 355 356 357 358 359
	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);

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

368
			this.contentDisposables.push(tree);
S
Sandeep Somavarapu 已提交
369 370 371 372
			scrollableContent.scanDomNode();
		});
	}

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

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

J
Joao Moreno 已提交
394
		if (!contrib.length) {
J
Joao Moreno 已提交
395 396 397
			return;
		}

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

		append(container, details);
J
Joao Moreno 已提交
415 416
	}

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

J
Joao Moreno 已提交
421
		if (!contrib.length) {
J
Joao Moreno 已提交
422 423 424
			return;
		}

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

		append(container, details);
J
Joao Moreno 已提交
434 435
	}

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

J
Joao Moreno 已提交
440
		if (!contrib.length) {
J
Joao Moreno 已提交
441 442 443
			return;
		}

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

		append(container, details);
J
Joao Moreno 已提交
450 451
	}

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

		if (!contrib.length) {
			return;
		}

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

		append(container, details);
J
Joao Moreno 已提交
466 467
	}

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

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

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

482 483
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
484
				let command = byId[menu.command];
485 486

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

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

498 499
		rawKeybindings.forEach(rawKeybinding => {
			const keyLabel = this.keybindingToLabel(rawKeybinding);
J
Joao Moreno 已提交
500
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
501 502

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

511 512 513 514
		if (!commands.length) {
			return;
		}

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

		append(container, details);
534 535
	}

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

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

J
Joao Moreno 已提交
549
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
550 551 552 553 554

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

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

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

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

		append(container, details);
J
Joao Moreno 已提交
602 603
	}

604 605 606
	private keybindingToLabel(rawKeyBinding: IKeyBinding): string {
		let key: string;

J
Johannes Rieken 已提交
607
		switch (process.platform) {
608 609 610 611 612 613 614 615 616
			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
Johannes Rieken 已提交
617
	private loadContents(loadingTask: () => TPromise<any>): void {
J
Joao Moreno 已提交
618
		addClass(this.content, 'loading');
J
Joao Moreno 已提交
619

J
Joao Moreno 已提交
620 621
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
622

J
Joao Moreno 已提交
623
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
624 625 626
	}

	layout(): void {
J
Joao Moreno 已提交
627
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
628 629
	}

630 631 632 633 634 635 636 637
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

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

J
Joao Moreno 已提交
638 639
	dispose(): void {
		this._highlight = null;
J
Joao Moreno 已提交
640 641
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
642 643 644
		super.dispose();
	}
}