extensionEditor.ts 39.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';
S
Sandeep Somavarapu 已提交
14
import { OS } from 'vs/base/common/platform';
M
Matt Bierner 已提交
15
import { Event, Emitter, once, chain } from 'vs/base/common/event';
J
Joao Moreno 已提交
16
import Cache from 'vs/base/common/cache';
J
Joao Moreno 已提交
17
import { Action } from 'vs/base/common/actions';
18
import { isPromiseCanceledError } from 'vs/base/common/errors';
19
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
20
import { domEvent } from 'vs/base/browser/event';
S
Sandeep Somavarapu 已提交
21
import { append, $, addClass, removeClass, finalHandler, join, toggleClass } from 'vs/base/browser/dom';
J
Joao Moreno 已提交
22
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
B
Benjamin Pasero 已提交
23
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Joao Moreno 已提交
24
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
25
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
S
Sandeep Somavarapu 已提交
26
import { IExtensionManifest, IKeyBinding, IView, IExtensionTipsService, LocalExtensionType, IViewContainer } from 'vs/platform/extensionManagement/common/extensionManagement';
27
import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
28
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
29
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions';
30
import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer';
31
import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
J
Joao Moreno 已提交
32
import { EditorOptions } from 'vs/workbench/common/editor';
J
Joao Moreno 已提交
33
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
S
Sandeep Somavarapu 已提交
34
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, MaliciousStatusLabelAction, DisabledStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
M
Matt Bierner 已提交
35
import { WebviewElement } from 'vs/workbench/parts/webview/electron-browser/webviewElement';
A
Alex Dima 已提交
36
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
J
Johannes Rieken 已提交
37
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
J
Joao Moreno 已提交
38
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
39
import { IOpenerService } from 'vs/platform/opener/common/opener';
S
Sandeep Somavarapu 已提交
40
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
41
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
42
import { IThemeService } from 'vs/platform/theme/common/themeService';
S
Sandeep Somavarapu 已提交
43
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
M
Matt Bierner 已提交
44
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
45
import { Command, ICommandOptions } from 'vs/editor/browser/editorExtensions';
46
import { INextEditorService } from 'vs/workbench/services/editor/common/editorService';
47
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
48
import { Color } from 'vs/base/common/color';
49
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
50
import { assign } from 'vs/base/common/objects';
51
import { INotificationService } from 'vs/platform/notification/common/notification';
52
import { CancellationToken } from 'vs/base/common/cancellation';
53 54 55

/**  A context key that is set when an extension editor webview has focus. */
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey<boolean>('extensionEditorWebviewFocus', undefined);
56 57
/**  A context key that is set when the find widget find input in extension editor webview is focused. */
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED = new RawContextKey<boolean>('extensionEditorFindWidgetInputFocused', false);
J
Joao Moreno 已提交
58

59 60
function renderBody(body: string): string {
	const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-core-resource://');
J
Joao Moreno 已提交
61 62 63 64
	return `<!DOCTYPE html>
		<html>
			<head>
				<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
65 66
				<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-core-resource:; child-src 'none'; frame-src 'none';">
				<link rel="stylesheet" type="text/css" href="${styleSheetPath}">
J
Joao Moreno 已提交
67
			</head>
68 69 70 71
			<body>
				<a id="scroll-to-top" role="button" aria-label="scroll to top" href="#"><span class="icon"></span></a>
				${body}
			</body>
J
Joao Moreno 已提交
72 73
		</html>`;
}
J
Joao Moreno 已提交
74

75 76 77 78 79 80 81 82 83
function removeEmbeddedSVGs(documentContent: string): string {
	const newDocument = new DOMParser().parseFromString(documentContent, 'text/html');

	// remove all inline svgs
	const allSVGs = newDocument.documentElement.querySelectorAll('svg');
	for (let i = 0; i < allSVGs.length; i++) {
		allSVGs[i].parentNode.removeChild(allSVGs[i]);
	}

K
kieferrm 已提交
84
	return newDocument.documentElement.outerHTML;
85 86
}

J
Joao Moreno 已提交
87 88 89 90 91
class NavBar {

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

J
Joao Moreno 已提交
92
	private currentId: string = null;
J
Joao Moreno 已提交
93 94 95 96 97 98 99 100 101
	private actions: Action[];
	private actionbar: ActionBar;

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

I
InspectorDeno 已提交
102
	push(id: string, label: string, tooltip: string): void {
J
Joao Moreno 已提交
103
		const run = () => this._update(id);
J
Joao Moreno 已提交
104 105
		const action = new Action(id, label, null, true, run);

106
		action.tooltip = tooltip;
I
InspectorDeno 已提交
107

J
Joao Moreno 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120
		this.actions.push(action);
		this.actionbar.push(action);

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

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

J
Joao Moreno 已提交
121 122 123 124 125 126 127 128 129 130 131
	update(): void {
		this._update(this.currentId);
	}

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

J
Joao Moreno 已提交
132 133 134 135 136 137 138
	dispose(): void {
		this.actionbar = dispose(this.actionbar);
	}
}

const NavbarSection = {
	Readme: 'readme',
X
XVincentX 已提交
139
	Contributions: 'contributions',
S
Sandeep Somavarapu 已提交
140 141
	Changelog: 'changelog',
	Dependencies: 'dependencies'
J
Joao Moreno 已提交
142 143
};

J
Joao Moreno 已提交
144 145 146 147
interface ILayoutParticipant {
	layout(): void;
}

J
Joao Moreno 已提交
148 149
export class ExtensionEditor extends BaseEditor {

M
Matt Bierner 已提交
150
	static readonly ID: string = 'workbench.editor.extension';
J
Joao Moreno 已提交
151

152
	private icon: HTMLImageElement;
J
Joao Moreno 已提交
153
	private name: HTMLElement;
S
Sandeep Somavarapu 已提交
154
	private identifier: HTMLElement;
155
	private preview: HTMLElement;
S
Sandeep Somavarapu 已提交
156
	private builtin: HTMLElement;
J
Joao Moreno 已提交
157 158
	private license: HTMLElement;
	private publisher: HTMLElement;
J
Joao Moreno 已提交
159
	private installCount: HTMLElement;
J
Joao Moreno 已提交
160
	private rating: HTMLElement;
161
	private repository: HTMLElement;
J
Joao Moreno 已提交
162
	private description: HTMLElement;
J
Joao Moreno 已提交
163 164 165
	private extensionActionBar: ActionBar;
	private navbar: NavBar;
	private content: HTMLElement;
166 167
	private recommendation: HTMLElement;
	private header: HTMLElement;
J
Joao Moreno 已提交
168

J
Joao Moreno 已提交
169
	private extensionReadme: Cache<string>;
X
XVincentX 已提交
170
	private extensionChangelog: Cache<string>;
J
Joao Moreno 已提交
171
	private extensionManifest: Cache<IExtensionManifest>;
S
Sandeep Somavarapu 已提交
172
	private extensionDependencies: Cache<IExtensionDependencies>;
J
Joao Moreno 已提交
173

174
	private contextKey: IContextKey<boolean>;
175
	private findInputFocusContextKey: IContextKey<boolean>;
J
Joao Moreno 已提交
176
	private layoutParticipants: ILayoutParticipant[] = [];
J
Joao Moreno 已提交
177 178
	private contentDisposables: IDisposable[] = [];
	private transientDisposables: IDisposable[] = [];
J
Joao Moreno 已提交
179
	private disposables: IDisposable[];
M
Matt Bierner 已提交
180
	private activeWebview: WebviewElement;
181
	private editorLoadComplete: boolean = false;
J
Joao Moreno 已提交
182 183 184

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
M
Matt Bierner 已提交
185 186 187
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IViewletService private readonly viewletService: IViewletService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
188
		@IThemeService protected themeService: IThemeService,
M
Matt Bierner 已提交
189 190 191 192 193 194
		@IKeybindingService private readonly keybindingService: IKeybindingService,
		@INotificationService private readonly notificationService: INotificationService,
		@IOpenerService private readonly openerService: IOpenerService,
		@IPartService private readonly partService: IPartService,
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
		@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
J
Joao Moreno 已提交
195
	) {
B
Benjamin Pasero 已提交
196
		super(ExtensionEditor.ID, telemetryService, themeService);
J
Joao Moreno 已提交
197
		this.disposables = [];
J
Joao Moreno 已提交
198
		this.extensionReadme = null;
X
XVincentX 已提交
199
		this.extensionChangelog = null;
J
Joao Moreno 已提交
200
		this.extensionManifest = null;
S
Sandeep Somavarapu 已提交
201
		this.extensionDependencies = null;
202 203
		this.contextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS.bindTo(this.contextKeyService);
		this.findInputFocusContextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(this.contextKeyService);
J
Joao Moreno 已提交
204 205
	}

206 207
	createEditor(parent: HTMLElement): void {
		const root = append(parent, $('.extension-editor'));
208
		this.header = append(root, $('.header'));
J
Joao Moreno 已提交
209

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

212
		const details = append(this.header, $('.details'));
J
Joao Moreno 已提交
213
		const title = append(details, $('.title'));
J
Joao Moreno 已提交
214
		this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
J
Joao Moreno 已提交
215
		this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
S
Sandeep Somavarapu 已提交
216

217
		this.preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
S
Sandeep Somavarapu 已提交
218 219 220 221
		this.preview.textContent = localize('preview', "Preview");

		this.builtin = append(title, $('span.builtin'));
		this.builtin.textContent = localize('builtin', "Built-in");
J
Joao Moreno 已提交
222 223

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

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

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

230 231 232 233
		this.repository = append(subtitle, $('span.repository.clickable'));
		this.repository.textContent = localize('repository', 'Repository');
		this.repository.style.display = 'none';

J
Joao Moreno 已提交
234
		this.license = append(subtitle, $('span.license.clickable'));
J
Joao Moreno 已提交
235
		this.license.textContent = localize('license', 'License');
236
		this.license.style.display = 'none';
J
Joao Moreno 已提交
237

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

J
Joao Moreno 已提交
240
		const extensionActions = append(details, $('.actions'));
241 242 243 244 245 246 247 248 249 250 251 252
		this.extensionActionBar = new ActionBar(extensionActions, {
			animated: false,
			actionItemProvider: (action: Action) => {
				if (action.id === EnableAction.ID) {
					return (<EnableAction>action).actionItem;
				}
				if (action.id === DisableAction.ID) {
					return (<DisableAction>action).actionItem;
				}
				return null;
			}
		});
J
Joao Moreno 已提交
253 254
		this.disposables.push(this.extensionActionBar);

255 256
		this.recommendation = append(details, $('.recommendation'));

I
isidor 已提交
257
		chain(this.extensionActionBar.onDidRun)
J
Joao Moreno 已提交
258 259 260
			.map(({ error }) => error)
			.filter(error => !!error)
			.on(this.onError, this, this.disposables);
261

J
Joao Moreno 已提交
262 263
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
264

J
Joao Moreno 已提交
265
		this.content = append(body, $('.content'));
J
Joao Moreno 已提交
266 267
	}

268
	setInput(input: ExtensionsInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
269
		this.editorLoadComplete = false;
J
Joao Moreno 已提交
270 271
		const extension = input.extension;

J
Joao Moreno 已提交
272 273
		this.transientDisposables = dispose(this.transientDisposables);

J
Joao Moreno 已提交
274
		this.extensionReadme = new Cache(() => extension.getReadme());
X
XVincentX 已提交
275
		this.extensionChangelog = new Cache(() => extension.getChangelog());
J
Joao Moreno 已提交
276
		this.extensionManifest = new Cache(() => extension.getManifest());
S
Sandeep Somavarapu 已提交
277
		this.extensionDependencies = new Cache(() => this.extensionsWorkbenchService.loadDependencies(extension));
J
Joao Moreno 已提交
278

279 280 281 282
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

J
Joao Moreno 已提交
283
		this.name.textContent = extension.displayName;
S
Sandeep Somavarapu 已提交
284
		this.identifier.textContent = extension.id;
S
Sandeep Somavarapu 已提交
285 286
		this.preview.style.display = extension.preview ? 'inherit' : 'none';
		this.builtin.style.display = extension.type === LocalExtensionType.System ? 'inherit' : 'none';
287

J
Joao Moreno 已提交
288 289
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
290

291
		const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
292
		let recommendationsData = {};
293 294
		if (extRecommendations[extension.id.toLowerCase()]) {
			addClass(this.header, 'recommended');
295 296
			this.recommendation.textContent = extRecommendations[extension.id.toLowerCase()].reasonText;
			recommendationsData = { recommendationReason: extRecommendations[extension.id.toLowerCase()].reasonId };
297 298
		} else {
			removeClass(this.header, 'recommended');
299
			this.recommendation.textContent = '';
300 301
		}

302 303
		/* __GDPR__
			"extensionGallery:openExtension" : {
R
Ramya Achutha Rao 已提交
304
				"recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
305 306 307 308 309 310 311
				"${include}": [
					"${GalleryExtensionTelemetryData}"
				]
			}
		*/
		this.telemetryService.publicLog('extensionGallery:openExtension', assign(extension.telemetryData, recommendationsData));

S
Sandeep Somavarapu 已提交
312 313 314
		toggleClass(this.name, 'clickable', !!extension.url);
		toggleClass(this.publisher, 'clickable', !!extension.url);
		toggleClass(this.rating, 'clickable', !!extension.url);
315 316 317
		if (extension.url) {
			this.name.onclick = finalHandler(() => window.open(extension.url));
			this.rating.onclick = finalHandler(() => window.open(`${extension.url}#review-details`));
J
Joao Moreno 已提交
318
			this.publisher.onclick = finalHandler(() => {
319
				this.viewletService.openViewlet(VIEWLET_ID, true)
J
Joao Moreno 已提交
320
					.then(viewlet => viewlet as IExtensionsViewlet)
J
Johannes Rieken 已提交
321
					.done(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`));
J
Joao Moreno 已提交
322
			});
323 324

			if (extension.licenseUrl) {
B
Benjamin Pasero 已提交
325
				this.license.onclick = finalHandler(() => window.open(extension.licenseUrl));
326 327 328 329 330
				this.license.style.display = 'initial';
			} else {
				this.license.onclick = null;
				this.license.style.display = 'none';
			}
S
Sandeep Somavarapu 已提交
331 332 333 334
		} else {
			this.name.onclick = null;
			this.rating.onclick = null;
			this.publisher.onclick = null;
J
Joao Moreno 已提交
335 336
		}

337 338 339 340 341 342 343 344 345
		if (extension.repository) {
			this.repository.onclick = finalHandler(() => window.open(extension.repository));
			this.repository.style.display = 'initial';
		}
		else {
			this.repository.onclick = null;
			this.repository.style.display = 'none';
		}

346
		const install = this.instantiationService.createInstance(InstallCountWidget, this.installCount, { extension });
J
Joao Moreno 已提交
347 348
		this.transientDisposables.push(install);

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

J
Joao Moreno 已提交
352
		const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, true);
S
Sandeep Somavarapu 已提交
353
		const disabledStatusAction = this.instantiationService.createInstance(DisabledStatusLabelAction);
J
Joao Moreno 已提交
354 355 356
		const installAction = this.instantiationService.createInstance(CombinedInstallAction);
		const updateAction = this.instantiationService.createInstance(UpdateAction);
		const enableAction = this.instantiationService.createInstance(EnableAction);
357
		const disableAction = this.instantiationService.createInstance(DisableAction);
358
		const reloadAction = this.instantiationService.createInstance(ReloadAction);
J
Joao Moreno 已提交
359 360

		installAction.extension = extension;
J
Joao Moreno 已提交
361
		maliciousStatusAction.extension = extension;
S
Sandeep Somavarapu 已提交
362
		disabledStatusAction.extension = extension;
J
Joao Moreno 已提交
363 364
		updateAction.extension = extension;
		enableAction.extension = extension;
365
		disableAction.extension = extension;
366
		reloadAction.extension = extension;
J
Joao Moreno 已提交
367

J
Joao Moreno 已提交
368
		this.extensionActionBar.clear();
S
Sandeep Somavarapu 已提交
369 370
		this.extensionActionBar.push([disabledStatusAction, reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction], { icon: true, label: true });
		this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, maliciousStatusAction, disabledStatusAction);
J
Joao Moreno 已提交
371

S
Sandeep Somavarapu 已提交
372
		this.content.innerHTML = ''; // Clear content before setting navbar actions.
S
Sandeep Somavarapu 已提交
373

J
Joao Moreno 已提交
374 375
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
I
InspectorDeno 已提交
376 377 378 379
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"), localize('detailstooltip', "Extension details, rendered from the extension's 'README.md' file"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"), localize('contributionstooltip', "Extension contributions to the VS Code editor"));
		this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"), localize('changelogtooltip', "Extension update history, rendered from the extension's 'CHANGELOG.md' file"));
		this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"), localize('dependenciestooltip', "Extension dependencies, lists other extensions this extension depends on"));
J
Joao Moreno 已提交
380

381
		this.editorLoadComplete = true;
382
		return super.setInput(input, options, token);
J
Joao Moreno 已提交
383
	}
384

385 386 387 388 389 390
	showFind(): void {
		if (this.activeWebview) {
			this.activeWebview.showFind();
		}
	}

391 392 393 394 395 396 397 398 399 400 401 402
	public showNextFindTerm() {
		if (this.activeWebview) {
			this.activeWebview.showNextFindTerm();
		}
	}

	public showPreviousFindTerm() {
		if (this.activeWebview) {
			this.activeWebview.showPreviousFindTerm();
		}
	}

J
Joao Moreno 已提交
403
	private onNavbarChange(extension: IExtension, id: string): void {
404 405 406
		if (this.editorLoadComplete) {
			/* __GDPR__
				"extensionEditor:navbarChange" : {
407
					"navItem": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
408 409 410 411 412 413 414 415
					"${include}": [
						"${GalleryExtensionTelemetryData}"
					]
				}
			*/
			this.telemetryService.publicLog('extensionEditor:navbarChange', assign(extension.telemetryData, { navItem: id }));
		}

S
Sandeep Somavarapu 已提交
416 417
		this.contentDisposables = dispose(this.contentDisposables);
		this.content.innerHTML = '';
418
		this.activeWebview = null;
J
Joao Moreno 已提交
419
		switch (id) {
420 421 422
			case NavbarSection.Readme: return this.openReadme();
			case NavbarSection.Contributions: return this.openContributions();
			case NavbarSection.Changelog: return this.openChangelog();
423
			case NavbarSection.Dependencies: return this.openDependencies(extension);
J
Joao Moreno 已提交
424
		}
J
Joao Moreno 已提交
425
	}
J
Joao Moreno 已提交
426

427
	private openMarkdown(content: TPromise<string>, noContentCopy: string) {
428
		return this.loadContents(() => content
J
Joao Moreno 已提交
429
			.then(marked.parse)
430
			.then(renderBody)
431
			.then(removeEmbeddedSVGs)
J
Joao Moreno 已提交
432
			.then<void>(body => {
433
				const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders;
434
				const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {};
435
				this.activeWebview = this.instantiationService.createInstance(WebviewElement, this.partService.getContainer(Parts.EDITOR_PART), this.contextKey, this.findInputFocusContextKey, webViewOptions);
M
Matt Bierner 已提交
436
				this.activeWebview.mountTo(this.content);
437
				const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview);
438
				this.contentDisposables.push(toDisposable(removeLayoutParticipant));
439
				this.activeWebview.contents = body;
J
Joao Moreno 已提交
440

441
				this.activeWebview.onDidClickLink(link => {
M
Matt Bierner 已提交
442 443 444 445 446
					// Whitelist supported schemes for links
					if (link && ['http', 'https', 'mailto'].indexOf(link.scheme) >= 0) {
						this.openerService.open(link);
					}
				}, null, this.contentDisposables);
447
				this.contentDisposables.push(this.activeWebview);
J
Joao Moreno 已提交
448
			})
J
Joao Moreno 已提交
449
			.then(null, () => {
450
				const p = append(this.content, $('p.nocontent'));
X
XVincentX 已提交
451
				p.textContent = noContentCopy;
J
Joao Moreno 已提交
452
			}));
J
Joao Moreno 已提交
453
	}
J
Joao Moreno 已提交
454

455 456
	private openReadme() {
		return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available."));
457 458
	}

459
	private openChangelog() {
460
		return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available."));
461 462
	}

463
	private openContributions() {
J
Joao Moreno 已提交
464
		return this.loadContents(() => this.extensionManifest.get()
465
			.then(manifest => {
J
Joao Moreno 已提交
466
				const content = $('div', { class: 'subcontent' });
467
				const scrollableContent = new DomScrollableElement(content, {});
J
Joao Moreno 已提交
468 469 470 471 472

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

J
Joao Moreno 已提交
473
				const renders = [
B
Benjamin Pasero 已提交
474
					this.renderSettings(content, manifest, layout),
J
Joao Moreno 已提交
475
					this.renderCommands(content, manifest, layout),
B
Benjamin Pasero 已提交
476
					this.renderLanguages(content, manifest, layout),
477 478
					this.renderColorThemes(content, manifest, layout),
					this.renderIconThemes(content, manifest, layout),
479
					this.renderColors(content, manifest, layout),
B
Benjamin Pasero 已提交
480
					this.renderJSONValidation(content, manifest, layout),
481
					this.renderDebuggers(content, manifest, layout),
S
Sandeep Somavarapu 已提交
482
					this.renderViewContainers(content, manifest, layout),
483 484
					this.renderViews(content, manifest, layout),
					this.renderLocalizations(content, manifest, layout)
J
Joao Moreno 已提交
485 486 487
				];

				const isEmpty = !renders.reduce((v, r) => r || v, false);
J
Joao Moreno 已提交
488
				scrollableContent.scanDomNode();
489 490 491 492 493 494 495 496

				if (isEmpty) {
					append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
					return;
				} else {
					append(this.content, scrollableContent.getDomNode());
					this.contentDisposables.push(scrollableContent);
				}
S
Sandeep Somavarapu 已提交
497 498
			}, () => {
				append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
499
			}));
J
Joao Moreno 已提交
500 501
	}

502 503 504 505 506
	private openDependencies(extension: IExtension) {
		if (extension.dependencies.length === 0) {
			append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
			return;
		}
S
Sandeep Somavarapu 已提交
507

508 509 510
		return this.loadContents(() => {
			return this.extensionDependencies.get().then(extensionDependencies => {
				const content = $('div', { class: 'subcontent' });
511
				const scrollableContent = new DomScrollableElement(content, {});
512 513 514 515 516 517
				append(this.content, scrollableContent.getDomNode());
				this.contentDisposables.push(scrollableContent);

				const tree = this.renderDependencies(content, extensionDependencies);
				const layout = () => {
					scrollableContent.scanDomNode();
518 519
					const scrollDimensions = scrollableContent.getScrollDimensions();
					tree.layout(scrollDimensions.height);
520 521 522
				};
				const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
				this.contentDisposables.push(toDisposable(removeLayoutParticipant));
S
Sandeep Somavarapu 已提交
523

524
				this.contentDisposables.push(tree);
525
				scrollableContent.scanDomNode();
526 527
			}, error => {
				append(this.content, $('p.nocontent')).textContent = error;
528
				this.notificationService.error(error);
529
			});
S
Sandeep Somavarapu 已提交
530 531 532
		});
	}

533 534 535
	private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree {
		const renderer = this.instantiationService.createInstance(Renderer);
		const controller = this.instantiationService.createInstance(Controller);
536
		const tree = this.instantiationService.createInstance(WorkbenchTree, container, {
S
Sandeep Somavarapu 已提交
537
			dataSource: new DataSource(),
538 539
			renderer,
			controller
S
Sandeep Somavarapu 已提交
540 541
		}, {
				indentPixels: 40,
542
				twistiePixels: 20
543
			});
B
Benjamin Pasero 已提交
544

S
Sandeep Somavarapu 已提交
545
		tree.setInput(extensionDependencies);
546

547
		this.contentDisposables.push(tree.onDidChangeSelection(event => {
548 549 550 551 552
			if (event && event.payload && event.payload.origin === 'keyboard') {
				controller.openExtension(tree, false);
			}
		}));

S
Sandeep Somavarapu 已提交
553 554 555
		return tree;
	}

B
Benjamin Pasero 已提交
556
	private renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
557 558
		const contributes = manifest.contributes;
		const configuration = contributes && contributes.configuration;
J
Joao Moreno 已提交
559
		const properties = configuration && configuration.properties;
J
Joao Moreno 已提交
560
		const contrib = properties ? Object.keys(properties) : [];
J
Joao Moreno 已提交
561

J
Joao Moreno 已提交
562
		if (!contrib.length) {
563
			return false;
J
Joao Moreno 已提交
564 565
		}

J
Joao Moreno 已提交
566
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
567
			$('summary', null, localize('settings', "Settings ({0})", contrib.length)),
J
Joao Moreno 已提交
568
			$('table', null,
J
Joao Moreno 已提交
569 570 571
				$('tr', null,
					$('th', null, localize('setting name', "Name")),
					$('th', null, localize('description', "Description")),
J
nls  
Joao Moreno 已提交
572
					$('th', null, localize('default', "Default"))
J
Joao Moreno 已提交
573 574 575 576 577 578
				),
				...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 已提交
579
			)
J
Joao Moreno 已提交
580 581 582
		);

		append(container, details);
583
		return true;
J
Joao Moreno 已提交
584 585
	}

586 587


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

J
Joao Moreno 已提交
592
		if (!contrib.length) {
593
			return false;
J
Joao Moreno 已提交
594 595
		}

J
Joao Moreno 已提交
596
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
597
			$('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)),
J
Joao Moreno 已提交
598
			$('table', null,
599 600 601 602 603 604 605
				$('tr', null,
					$('th', null, localize('debugger name', "Name")),
					$('th', null, localize('debugger type', "Type")),
				),
				...contrib.map(d => $('tr', null,
					$('td', null, d.label),
					$('td', null, d.type)))
J
Joao Moreno 已提交
606
			)
J
Joao Moreno 已提交
607 608 609
		);

		append(container, details);
610
		return true;
J
Joao Moreno 已提交
611 612
	}

S
Sandeep Somavarapu 已提交
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
	private renderViewContainers(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.viewsContainers || {};

		let viewContainers = <{ id: string, title: string, location: string }[]>Object.keys(contrib).reduce((result, location) => {
			let viewContainersForLocation: IViewContainer[] = contrib[location];
			result.push(...viewContainersForLocation.map(viewContainer => ({ ...viewContainer, location })));
			return result;
		}, []);

		if (!viewContainers.length) {
			return false;
		}

		const details = $('details', { open: true, ontoggle: onDetailsToggle },
			$('summary', null, localize('viewContainers', "View Containers ({0})", viewContainers.length)),
			$('table', null,
				$('tr', null, $('th', null, localize('view container id', "ID")), $('th', null, localize('view container title', "Title")), $('th', null, localize('view container location', "Where"))),
				...viewContainers.map(viewContainer => $('tr', null, $('td', null, viewContainer.id), $('td', null, viewContainer.title), $('td', null, viewContainer.location)))
			)
		);

		append(container, details);
		return true;
	}

639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
	private renderViews(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.views || {};

		let views = <{ id: string, name: string, location: string }[]>Object.keys(contrib).reduce((result, location) => {
			let viewsForLocation: IView[] = contrib[location];
			result.push(...viewsForLocation.map(view => ({ ...view, location })));
			return result;
		}, []);

		if (!views.length) {
			return false;
		}

		const details = $('details', { open: true, ontoggle: onDetailsToggle },
			$('summary', null, localize('views', "Views ({0})", views.length)),
			$('table', null,
				$('tr', null, $('th', null, localize('view id', "ID")), $('th', null, localize('view name', "Name")), $('th', null, localize('view location', "Where"))),
				...views.map(view => $('tr', null, $('td', null, view.id), $('td', null, view.name), $('td', null, view.location)))
			)
		);

		append(container, details);
		return true;
	}

665 666 667 668 669 670 671 672 673 674 675
	private renderLocalizations(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
		const contributes = manifest.contributes;
		const localizations = contributes && contributes.localizations || [];

		if (!localizations.length) {
			return false;
		}

		const details = $('details', { open: true, ontoggle: onDetailsToggle },
			$('summary', null, localize('localizations', "Localizations ({0})", localizations.length)),
			$('table', null,
A
Alex Dima 已提交
676
				$('tr', null, $('th', null, localize('localizations language id', "Language Id")), $('th', null, localize('localizations language name', "Language Name")), $('th', null, localize('localizations localized language name', "Language Name (Localized)"))),
677
				...localizations.map(localization => $('tr', null, $('td', null, localization.languageId), $('td', null, localization.languageName), $('td', null, localization.languageNameLocalized)))
678 679 680 681 682 683 684
			)
		);

		append(container, details);
		return true;
	}

685
	private renderColorThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
686 687
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.themes || [];
J
Joao Moreno 已提交
688

J
Joao Moreno 已提交
689
		if (!contrib.length) {
690
			return false;
J
Joao Moreno 已提交
691 692
		}

J
Joao Moreno 已提交
693
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
			$('summary', null, localize('colorThemes', "Color Themes ({0})", contrib.length)),
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
		return true;
	}

	private renderIconThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.iconThemes || [];

		if (!contrib.length) {
			return false;
		}

		const details = $('details', { open: true, ontoggle: onDetailsToggle },
			$('summary', null, localize('iconThemes', "Icon Themes ({0})", contrib.length)),
J
Joao Moreno 已提交
712 713 714 715
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
716
		return true;
J
Joao Moreno 已提交
717 718
	}

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
	private renderColors(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
		const contributes = manifest.contributes;
		const colors = contributes && contributes.colors;

		if (!colors || !colors.length) {
			return false;
		}

		function colorPreview(colorReference: string): Node[] {
			let result: Node[] = [];
			if (colorReference && colorReference[0] === '#') {
				let color = Color.fromHex(colorReference);
				if (color) {
					result.push($('span', { class: 'colorBox', style: 'background-color: ' + Color.Format.CSS.format(color) }, ''));
				}
			}
			result.push($('code', null, colorReference));
			return result;
		}

		const details = $('details', { open: true, ontoggle: onDetailsToggle },
			$('summary', null, localize('colors', "Colors ({0})", colors.length)),
			$('table', null,
				$('tr', null,
					$('th', null, localize('colorId', "Id")),
					$('th', null, localize('description', "Description")),
					$('th', null, localize('defaultDark', "Dark Default")),
					$('th', null, localize('defaultLight', "Light Default")),
					$('th', null, localize('defaultHC', "High Contrast Default"))
				),
				...colors.map(color => $('tr', null,
					$('td', null, $('code', null, color.id)),
					$('td', null, color.description),
					$('td', null, ...colorPreview(color.defaults.dark)),
					$('td', null, ...colorPreview(color.defaults.light)),
					$('td', null, ...colorPreview(color.defaults.highContrast))
				))
			)
		);

		append(container, details);
		return true;
	}


B
Benjamin Pasero 已提交
764
	private renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
765 766
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
767 768

		if (!contrib.length) {
769
			return false;
J
Joao Moreno 已提交
770 771
		}

J
Joao Moreno 已提交
772
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
773
			$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
S
Sandeep Somavarapu 已提交
774 775 776 777 778 779
			$('table', null,
				$('tr', null,
					$('th', null, localize('fileMatch', "File Match")),
					$('th', null, localize('schema', "Schema"))
				),
				...contrib.map(v => $('tr', null,
S
Sandeep Somavarapu 已提交
780
					$('td', null, $('code', null, v.fileMatch)),
S
Sandeep Somavarapu 已提交
781 782
					$('td', null, v.url)
				))));
J
Joao Moreno 已提交
783 784

		append(container, details);
785
		return true;
J
Joao Moreno 已提交
786 787
	}

788
	private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
789 790
		const contributes = manifest.contributes;
		const rawCommands = contributes && contributes.commands || [];
J
Joao Moreno 已提交
791
		const commands = rawCommands.map(c => ({
792 793
			id: c.command,
			title: c.title,
J
Joao Moreno 已提交
794
			keybindings: [],
795 796 797
			menus: []
		}));

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

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

802 803
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
804
				let command = byId[menu.command];
805 806

				if (!command) {
J
Joao Moreno 已提交
807
					command = { id: menu.command, title: '', keybindings: [], menus: [context] };
J
Joao Moreno 已提交
808
					byId[command.id] = command;
809 810 811 812 813 814 815
					commands.push(command);
				} else {
					command.menus.push(context);
				}
			});
		});

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

818
		rawKeybindings.forEach(rawKeybinding => {
S
Sandeep Somavarapu 已提交
819
			const keybinding = this.resolveKeybinding(rawKeybinding);
J
Joao Moreno 已提交
820

S
Sandeep Somavarapu 已提交
821
			if (!keybinding) {
J
Joao Moreno 已提交
822 823 824
				return;
			}

J
Joao Moreno 已提交
825
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
826 827

			if (!command) {
S
Sandeep Somavarapu 已提交
828
				command = { id: rawKeybinding.command, title: '', keybindings: [keybinding], menus: [] };
J
Joao Moreno 已提交
829
				byId[command.id] = command;
J
Joao Moreno 已提交
830 831
				commands.push(command);
			} else {
S
Sandeep Somavarapu 已提交
832
				command.keybindings.push(keybinding);
J
Joao Moreno 已提交
833 834 835
			}
		});

836
		if (!commands.length) {
837
			return false;
838 839
		}

S
Sandeep Somavarapu 已提交
840 841 842 843 844 845
		const renderKeybinding = (keybinding: ResolvedKeybinding): HTMLElement => {
			const element = $('');
			new KeybindingLabel(element, OS).set(keybinding, null);
			return element;
		};

J
Joao Moreno 已提交
846
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
847 848 849 850 851
			$('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 已提交
852
					$('th', null, localize('keyboard shortcuts', "Keyboard Shortcuts")),
853 854 855
					$('th', null, localize('menuContexts', "Menu Contexts"))
				),
				...commands.map(c => $('tr', null,
856
					$('td', null, $('code', null, c.id)),
857
					$('td', null, c.title),
S
Sandeep Somavarapu 已提交
858
					$('td', null, ...c.keybindings.map(keybinding => renderKeybinding(keybinding))),
859 860 861
					$('td', null, ...c.menus.map(context => $('code', null, context)))
				))
			)
J
Joao Moreno 已提交
862 863 864
		);

		append(container, details);
865
		return true;
866 867
	}

B
Benjamin Pasero 已提交
868
	private renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
869 870
		const contributes = manifest.contributes;
		const rawLanguages = contributes && contributes.languages || [];
J
Joao Moreno 已提交
871 872
		const languages = rawLanguages.map(l => ({
			id: l.id,
J
Joao Moreno 已提交
873
			name: (l.aliases || [])[0] || l.id,
J
Joao Moreno 已提交
874 875 876
			extensions: l.extensions || [],
			hasGrammar: false,
			hasSnippets: false
J
Joao Moreno 已提交
877 878
		}));

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

J
Joao Moreno 已提交
881
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
882 883 884 885 886

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

			if (!language) {
J
Joao Moreno 已提交
887
				language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false };
J
Joao Moreno 已提交
888 889 890 891 892 893 894
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasGrammar = true;
			}
		});

J
Joao Moreno 已提交
895 896 897 898 899 900 901 902 903 904 905 906 907 908
		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 已提交
909
		if (!languages.length) {
910
			return false;
J
Joao Moreno 已提交
911 912
		}

J
Joao Moreno 已提交
913
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
914 915 916
			$('summary', null, localize('languages', "Languages ({0})", languages.length)),
			$('table', null,
				$('tr', null,
J
Joao Moreno 已提交
917 918
					$('th', null, localize('language id', "ID")),
					$('th', null, localize('language name', "Name")),
J
Joao Moreno 已提交
919
					$('th', null, localize('file extensions', "File Extensions")),
J
Joao Moreno 已提交
920 921
					$('th', null, localize('grammar', "Grammar")),
					$('th', null, localize('snippets', "Snippets"))
J
Joao Moreno 已提交
922 923
				),
				...languages.map(l => $('tr', null,
J
Joao Moreno 已提交
924
					$('td', null, l.id),
J
Joao Moreno 已提交
925
					$('td', null, l.name),
J
Joao Moreno 已提交
926
					$('td', null, ...join(l.extensions.map(ext => $('code', null, ext)), ' ')),
J
Joao Moreno 已提交
927 928
					$('td', null, document.createTextNode(l.hasGrammar ? '✔︎' : '')),
					$('td', null, document.createTextNode(l.hasSnippets ? '✔︎' : ''))
J
Joao Moreno 已提交
929 930
				))
			)
J
Joao Moreno 已提交
931 932 933
		);

		append(container, details);
934
		return true;
J
Joao Moreno 已提交
935 936
	}

S
Sandeep Somavarapu 已提交
937
	private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding {
938 939
		let key: string;

J
Johannes Rieken 已提交
940
		switch (process.platform) {
941 942 943 944 945
			case 'win32': key = rawKeyBinding.win; break;
			case 'linux': key = rawKeyBinding.linux; break;
			case 'darwin': key = rawKeyBinding.mac; break;
		}

946
		const keyBinding = KeybindingIO.readKeybinding(key || rawKeyBinding.key, OS);
A
Alex Dima 已提交
947 948 949 950
		if (!keyBinding) {
			return null;
		}

S
Sandeep Somavarapu 已提交
951
		return this.keybindingService.resolveKeybinding(keyBinding)[0];
952 953
	}

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

J
Joao Moreno 已提交
957 958
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
959

J
Joao Moreno 已提交
960
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
961 962 963
	}

	layout(): void {
J
Joao Moreno 已提交
964
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
965 966
	}

967 968 969 970 971
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

972
		this.notificationService.error(err);
973 974
	}

J
Joao Moreno 已提交
975
	dispose(): void {
J
Joao Moreno 已提交
976 977
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
978 979 980
		super.dispose();
	}
}
981

982 983 984 985 986 987 988 989 990
class ShowExtensionEditorFindCommand extends Command {
	public runCommand(accessor: ServicesAccessor, args: any): void {
		const extensionEditor = this.getExtensionEditor(accessor);
		if (extensionEditor) {
			extensionEditor.showFind();
		}
	}

	private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor {
B
Benjamin Pasero 已提交
991 992 993
		const activeControl = accessor.get(INextEditorService).activeControl as ExtensionEditor;
		if (activeControl instanceof ExtensionEditor) {
			return activeControl;
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
		}
		return null;
	}
}
const showCommand = new ShowExtensionEditorFindCommand({
	id: 'editor.action.extensioneditor.showfind',
	precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS,
	kbOpts: {
		primary: KeyMod.CtrlCmd | KeyCode.KEY_F
	}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(showCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
class ShowExtensionEditorFindTermCommand extends Command {
	constructor(opts: ICommandOptions, private _next: boolean) {
		super(opts);
	}

	public runCommand(accessor: ServicesAccessor, args: any): void {
		const extensionEditor = this.getExtensionEditor(accessor);
		if (extensionEditor) {
			if (this._next) {
				extensionEditor.showNextFindTerm();
			} else {
				extensionEditor.showPreviousFindTerm();
			}
		}
	}

	private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor {
B
Benjamin Pasero 已提交
1024 1025 1026
		const activeControl = accessor.get(INextEditorService).activeControl as ExtensionEditor;
		if (activeControl instanceof ExtensionEditor) {
			return activeControl;
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
		}
		return null;
	}
}

const showNextFindTermCommand = new ShowExtensionEditorFindTermCommand({
	id: 'editor.action.extensioneditor.showNextFindTerm',
	precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED,
	kbOpts: {
		primary: KeyMod.Alt | KeyCode.DownArrow
	}
}, true);
KeybindingsRegistry.registerCommandAndKeybindingRule(showNextFindTermCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

const showPreviousFindTermCommand = new ShowExtensionEditorFindTermCommand({
	id: 'editor.action.extensioneditor.showPreviousFindTerm',
	precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED,
	kbOpts: {
		primary: KeyMod.Alt | KeyCode.UpArrow
	}
}, false);
KeybindingsRegistry.registerCommandAndKeybindingRule(showPreviousFindTermCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));