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

/**  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);
59 60
/**  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 已提交
61

62 63
function renderBody(body: string): string {
	const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-core-resource://');
J
Joao Moreno 已提交
64 65 66 67
	return `<!DOCTYPE html>
		<html>
			<head>
				<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
68 69
				<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 已提交
70
			</head>
71 72 73 74
			<body>
				<a id="scroll-to-top" role="button" aria-label="scroll to top" href="#"><span class="icon"></span></a>
				${body}
			</body>
J
Joao Moreno 已提交
75 76
		</html>`;
}
J
Joao Moreno 已提交
77

78 79 80 81 82 83 84 85 86
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 已提交
87
	return newDocument.documentElement.outerHTML;
88 89
}

J
Joao Moreno 已提交
90 91 92 93 94
class NavBar {

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

J
Joao Moreno 已提交
95
	private currentId: string = null;
J
Joao Moreno 已提交
96 97 98 99 100 101 102 103 104 105
	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 {
J
Joao Moreno 已提交
106
		const run = () => this._update(id);
J
Joao Moreno 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
		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();
	}

J
Joao Moreno 已提交
122 123 124 125 126 127 128 129 130 131 132
	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 已提交
133 134 135 136 137 138 139
	dispose(): void {
		this.actionbar = dispose(this.actionbar);
	}
}

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

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

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

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

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

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

175
	private contextKey: IContextKey<boolean>;
176
	private findInputFocusContextKey: IContextKey<boolean>;
J
Joao Moreno 已提交
177
	private layoutParticipants: ILayoutParticipant[] = [];
J
Joao Moreno 已提交
178 179
	private contentDisposables: IDisposable[] = [];
	private transientDisposables: IDisposable[] = [];
J
Joao Moreno 已提交
180
	private disposables: IDisposable[];
181
	private activeWebview: Webview;
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 195
		@IKeybindingService private readonly keybindingService: IKeybindingService,
		@INotificationService private readonly notificationService: INotificationService,
		@IOpenerService private readonly openerService: IOpenerService,
		@IPartService private readonly partService: IPartService,
		@IContextViewService private readonly contextViewService: IContextViewService,
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
		@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
196
		@IEnvironmentService private readonly environmentService: IEnvironmentService
197

J
Joao Moreno 已提交
198
	) {
B
Benjamin Pasero 已提交
199
		super(ExtensionEditor.ID, telemetryService, themeService);
J
Joao Moreno 已提交
200
		this.disposables = [];
J
Joao Moreno 已提交
201
		this.extensionReadme = null;
X
XVincentX 已提交
202
		this.extensionChangelog = null;
J
Joao Moreno 已提交
203
		this.extensionManifest = null;
S
Sandeep Somavarapu 已提交
204
		this.extensionDependencies = null;
205 206
		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 已提交
207 208 209 210
	}

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

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

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

217
		const details = append(this.header, $('.details'));
J
Joao Moreno 已提交
218
		const title = append(details, $('.title'));
J
Joao Moreno 已提交
219
		this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
J
Joao Moreno 已提交
220
		this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
S
Sandeep Somavarapu 已提交
221

222
		this.preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
S
Sandeep Somavarapu 已提交
223 224 225 226
		this.preview.textContent = localize('preview', "Preview");

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

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

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

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

235 236 237 238
		this.repository = append(subtitle, $('span.repository.clickable'));
		this.repository.textContent = localize('repository', 'Repository');
		this.repository.style.display = 'none';

J
Joao Moreno 已提交
239
		this.license = append(subtitle, $('span.license.clickable'));
J
Joao Moreno 已提交
240
		this.license.textContent = localize('license', 'License');
241
		this.license.style.display = 'none';
J
Joao Moreno 已提交
242

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

J
Joao Moreno 已提交
245
		const extensionActions = append(details, $('.actions'));
246 247 248 249 250 251 252 253 254 255 256 257
		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 已提交
258 259
		this.disposables.push(this.extensionActionBar);

260 261
		this.recommendation = append(details, $('.recommendation'));

I
isidor 已提交
262
		chain(this.extensionActionBar.onDidRun)
J
Joao Moreno 已提交
263 264 265
			.map(({ error }) => error)
			.filter(error => !!error)
			.on(this.onError, this, this.disposables);
266

J
Joao Moreno 已提交
267 268
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
269

J
Joao Moreno 已提交
270
		this.content = append(body, $('.content'));
J
Joao Moreno 已提交
271 272 273
	}

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

J
Joao Moreno 已提交
276 277
		this.transientDisposables = dispose(this.transientDisposables);

J
Joao Moreno 已提交
278
		this.extensionReadme = new Cache(() => extension.getReadme());
X
XVincentX 已提交
279
		this.extensionChangelog = new Cache(() => extension.getChangelog());
J
Joao Moreno 已提交
280
		this.extensionManifest = new Cache(() => extension.getManifest());
S
Sandeep Somavarapu 已提交
281
		this.extensionDependencies = new Cache(() => this.extensionsWorkbenchService.loadDependencies(extension));
J
Joao Moreno 已提交
282

283 284 285 286
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

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

J
Joao Moreno 已提交
292 293
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
294

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

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

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

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

341 342 343 344 345 346 347 348 349
		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';
		}

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

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

J
Joao Moreno 已提交
356
		const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, true);
J
Joao Moreno 已提交
357 358 359
		const installAction = this.instantiationService.createInstance(CombinedInstallAction);
		const updateAction = this.instantiationService.createInstance(UpdateAction);
		const enableAction = this.instantiationService.createInstance(EnableAction);
360
		const disableAction = this.instantiationService.createInstance(DisableAction);
361
		const reloadAction = this.instantiationService.createInstance(ReloadAction);
J
Joao Moreno 已提交
362 363

		installAction.extension = extension;
J
Joao Moreno 已提交
364
		maliciousStatusAction.extension = extension;
J
Joao Moreno 已提交
365 366
		updateAction.extension = extension;
		enableAction.extension = extension;
367
		disableAction.extension = extension;
368
		reloadAction.extension = extension;
J
Joao Moreno 已提交
369

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

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

J
Joao Moreno 已提交
376 377
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
378 379
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"));
380 381
		this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"));
		this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"));
J
Joao Moreno 已提交
382 383 384 385

		return super.setInput(input, options);
	}

J
Joao Moreno 已提交
386 387 388 389
	changePosition(position: Position): void {
		this.navbar.update();
		super.changePosition(position);
	}
390

391 392 393 394 395 396
	showFind(): void {
		if (this.activeWebview) {
			this.activeWebview.showFind();
		}
	}

397 398 399 400 401 402 403 404 405 406 407 408
	public showNextFindTerm() {
		if (this.activeWebview) {
			this.activeWebview.showNextFindTerm();
		}
	}

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

J
Joao Moreno 已提交
409
	private onNavbarChange(extension: IExtension, id: string): void {
S
Sandeep Somavarapu 已提交
410 411
		this.contentDisposables = dispose(this.contentDisposables);
		this.content.innerHTML = '';
412
		this.activeWebview = null;
J
Joao Moreno 已提交
413
		switch (id) {
414 415 416
			case NavbarSection.Readme: return this.openReadme();
			case NavbarSection.Contributions: return this.openContributions();
			case NavbarSection.Changelog: return this.openChangelog();
417
			case NavbarSection.Dependencies: return this.openDependencies(extension);
J
Joao Moreno 已提交
418
		}
J
Joao Moreno 已提交
419
	}
J
Joao Moreno 已提交
420

421
	private openMarkdown(content: TPromise<string>, noContentCopy: string) {
422
		return this.loadContents(() => content
J
Joao Moreno 已提交
423
			.then(marked.parse)
424
			.then(renderBody)
425
			.then(removeEmbeddedSVGs)
J
Joao Moreno 已提交
426
			.then<void>(body => {
427
				const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders;
428
				const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {};
M
Matt Bierner 已提交
429 430
				this.activeWebview = new Webview(this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions);
				this.activeWebview.mountTo(this.content);
431
				const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview);
432
				this.contentDisposables.push(toDisposable(removeLayoutParticipant));
433
				this.activeWebview.contents = body;
J
Joao Moreno 已提交
434

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

449 450
	private openReadme() {
		return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available."));
451 452
	}

453
	private openChangelog() {
454
		return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available."));
455 456
	}

457
	private openContributions() {
J
Joao Moreno 已提交
458
		return this.loadContents(() => this.extensionManifest.get()
459
			.then(manifest => {
J
Joao Moreno 已提交
460
				const content = $('div', { class: 'subcontent' });
461
				const scrollableContent = new DomScrollableElement(content, {});
J
Joao Moreno 已提交
462 463 464 465 466

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

J
Joao Moreno 已提交
467
				const renders = [
B
Benjamin Pasero 已提交
468
					this.renderSettings(content, manifest, layout),
J
Joao Moreno 已提交
469
					this.renderCommands(content, manifest, layout),
B
Benjamin Pasero 已提交
470
					this.renderLanguages(content, manifest, layout),
471 472
					this.renderColorThemes(content, manifest, layout),
					this.renderIconThemes(content, manifest, layout),
473
					this.renderColors(content, manifest, layout),
B
Benjamin Pasero 已提交
474
					this.renderJSONValidation(content, manifest, layout),
475
					this.renderDebuggers(content, manifest, layout),
476 477
					this.renderViews(content, manifest, layout),
					this.renderLocalizations(content, manifest, layout)
J
Joao Moreno 已提交
478 479 480
				];

				const isEmpty = !renders.reduce((v, r) => r || v, false);
J
Joao Moreno 已提交
481
				scrollableContent.scanDomNode();
482 483 484 485 486 487 488 489

				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 已提交
490 491
			}, () => {
				append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
492
			}));
J
Joao Moreno 已提交
493 494
	}

495 496 497 498 499
	private openDependencies(extension: IExtension) {
		if (extension.dependencies.length === 0) {
			append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
			return;
		}
S
Sandeep Somavarapu 已提交
500

501 502 503
		return this.loadContents(() => {
			return this.extensionDependencies.get().then(extensionDependencies => {
				const content = $('div', { class: 'subcontent' });
504
				const scrollableContent = new DomScrollableElement(content, {});
505 506 507 508 509 510
				append(this.content, scrollableContent.getDomNode());
				this.contentDisposables.push(scrollableContent);

				const tree = this.renderDependencies(content, extensionDependencies);
				const layout = () => {
					scrollableContent.scanDomNode();
511 512
					const scrollDimensions = scrollableContent.getScrollDimensions();
					tree.layout(scrollDimensions.height);
513 514 515
				};
				const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
				this.contentDisposables.push(toDisposable(removeLayoutParticipant));
S
Sandeep Somavarapu 已提交
516

517
				this.contentDisposables.push(tree);
518
				scrollableContent.scanDomNode();
519 520
			}, error => {
				append(this.content, $('p.nocontent')).textContent = error;
521
				this.notificationService.error(error);
522
			});
S
Sandeep Somavarapu 已提交
523 524 525
		});
	}

526 527 528
	private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree {
		const renderer = this.instantiationService.createInstance(Renderer);
		const controller = this.instantiationService.createInstance(Controller);
529
		const tree = this.instantiationService.createInstance(WorkbenchTree, container, {
S
Sandeep Somavarapu 已提交
530
			dataSource: new DataSource(),
531 532
			renderer,
			controller
S
Sandeep Somavarapu 已提交
533 534
		}, {
				indentPixels: 40,
535
				twistiePixels: 20
536
			});
B
Benjamin Pasero 已提交
537

S
Sandeep Somavarapu 已提交
538
		tree.setInput(extensionDependencies);
539

540
		this.contentDisposables.push(tree.onDidChangeSelection(event => {
541 542 543 544 545
			if (event && event.payload && event.payload.origin === 'keyboard') {
				controller.openExtension(tree, false);
			}
		}));

S
Sandeep Somavarapu 已提交
546 547 548
		return tree;
	}

B
Benjamin Pasero 已提交
549
	private renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
550 551
		const contributes = manifest.contributes;
		const configuration = contributes && contributes.configuration;
J
Joao Moreno 已提交
552
		const properties = configuration && configuration.properties;
J
Joao Moreno 已提交
553
		const contrib = properties ? Object.keys(properties) : [];
J
Joao Moreno 已提交
554

J
Joao Moreno 已提交
555
		if (!contrib.length) {
556
			return false;
J
Joao Moreno 已提交
557 558
		}

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

		append(container, details);
576
		return true;
J
Joao Moreno 已提交
577 578
	}

579 580


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

J
Joao Moreno 已提交
585
		if (!contrib.length) {
586
			return false;
J
Joao Moreno 已提交
587 588
		}

J
Joao Moreno 已提交
589
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
590
			$('summary', null, localize('debuggers', "Debuggers ({0})", contrib.length)),
J
Joao Moreno 已提交
591
			$('table', null,
592 593 594 595 596 597 598
				$('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 已提交
599
			)
J
Joao Moreno 已提交
600 601 602
		);

		append(container, details);
603
		return true;
J
Joao Moreno 已提交
604 605
	}

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
	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;
	}

632 633 634 635 636 637 638 639 640 641 642
	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,
643 644
				$('tr', null, $('th', null, localize('localizations language id', "Language Id")), $('th', null, localize('localizations language name', "Langauge Name")), $('th', null, localize('localizations localized language name', "Langauge Name (Localized)"))),
				...localizations.map(localization => $('tr', null, $('td', null, localization.languageId), $('td', null, localization.languageName), $('td', null, localization.languageNameLocalized)))
645 646 647 648 649 650 651
			)
		);

		append(container, details);
		return true;
	}

652
	private renderColorThemes(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
653 654
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.themes || [];
J
Joao Moreno 已提交
655

J
Joao Moreno 已提交
656
		if (!contrib.length) {
657
			return false;
J
Joao Moreno 已提交
658 659
		}

J
Joao Moreno 已提交
660
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
			$('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 已提交
679 680 681 682
			$('ul', null, ...contrib.map(theme => $('li', null, theme.label)))
		);

		append(container, details);
683
		return true;
J
Joao Moreno 已提交
684 685
	}

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
	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 已提交
731
	private renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
732 733
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
734 735

		if (!contrib.length) {
736
			return false;
J
Joao Moreno 已提交
737 738
		}

J
Joao Moreno 已提交
739
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
740
			$('summary', null, localize('JSON Validation', "JSON Validation ({0})", contrib.length)),
S
Sandeep Somavarapu 已提交
741 742 743 744 745 746
			$('table', null,
				$('tr', null,
					$('th', null, localize('fileMatch', "File Match")),
					$('th', null, localize('schema', "Schema"))
				),
				...contrib.map(v => $('tr', null,
S
Sandeep Somavarapu 已提交
747
					$('td', null, $('code', null, v.fileMatch)),
S
Sandeep Somavarapu 已提交
748 749
					$('td', null, v.url)
				))));
J
Joao Moreno 已提交
750 751

		append(container, details);
752
		return true;
J
Joao Moreno 已提交
753 754
	}

755
	private renderCommands(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
756 757
		const contributes = manifest.contributes;
		const rawCommands = contributes && contributes.commands || [];
J
Joao Moreno 已提交
758
		const commands = rawCommands.map(c => ({
759 760
			id: c.command,
			title: c.title,
J
Joao Moreno 已提交
761
			keybindings: [],
762 763 764
			menus: []
		}));

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

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

769 770
		Object.keys(menus).forEach(context => {
			menus[context].forEach(menu => {
J
Joao Moreno 已提交
771
				let command = byId[menu.command];
772 773

				if (!command) {
J
Joao Moreno 已提交
774
					command = { id: menu.command, title: '', keybindings: [], menus: [context] };
J
Joao Moreno 已提交
775
					byId[command.id] = command;
776 777 778 779 780 781 782
					commands.push(command);
				} else {
					command.menus.push(context);
				}
			});
		});

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

785
		rawKeybindings.forEach(rawKeybinding => {
S
Sandeep Somavarapu 已提交
786
			const keybinding = this.resolveKeybinding(rawKeybinding);
J
Joao Moreno 已提交
787

S
Sandeep Somavarapu 已提交
788
			if (!keybinding) {
J
Joao Moreno 已提交
789 790 791
				return;
			}

J
Joao Moreno 已提交
792
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
793 794

			if (!command) {
S
Sandeep Somavarapu 已提交
795
				command = { id: rawKeybinding.command, title: '', keybindings: [keybinding], menus: [] };
J
Joao Moreno 已提交
796
				byId[command.id] = command;
J
Joao Moreno 已提交
797 798
				commands.push(command);
			} else {
S
Sandeep Somavarapu 已提交
799
				command.keybindings.push(keybinding);
J
Joao Moreno 已提交
800 801 802
			}
		});

803
		if (!commands.length) {
804
			return false;
805 806
		}

S
Sandeep Somavarapu 已提交
807 808 809 810 811 812
		const renderKeybinding = (keybinding: ResolvedKeybinding): HTMLElement => {
			const element = $('');
			new KeybindingLabel(element, OS).set(keybinding, null);
			return element;
		};

J
Joao Moreno 已提交
813
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
814 815 816 817 818
			$('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 已提交
819
					$('th', null, localize('keyboard shortcuts', "Keyboard Shortcuts")),
820 821 822
					$('th', null, localize('menuContexts', "Menu Contexts"))
				),
				...commands.map(c => $('tr', null,
823
					$('td', null, $('code', null, c.id)),
824
					$('td', null, c.title),
S
Sandeep Somavarapu 已提交
825
					$('td', null, ...c.keybindings.map(keybinding => renderKeybinding(keybinding))),
826 827 828
					$('td', null, ...c.menus.map(context => $('code', null, context)))
				))
			)
J
Joao Moreno 已提交
829 830 831
		);

		append(container, details);
832
		return true;
833 834
	}

B
Benjamin Pasero 已提交
835
	private renderLanguages(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
836 837
		const contributes = manifest.contributes;
		const rawLanguages = contributes && contributes.languages || [];
J
Joao Moreno 已提交
838 839
		const languages = rawLanguages.map(l => ({
			id: l.id,
J
Joao Moreno 已提交
840
			name: (l.aliases || [])[0] || l.id,
J
Joao Moreno 已提交
841 842 843
			extensions: l.extensions || [],
			hasGrammar: false,
			hasSnippets: false
J
Joao Moreno 已提交
844 845
		}));

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

J
Joao Moreno 已提交
848
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
849 850 851 852 853

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

			if (!language) {
J
Joao Moreno 已提交
854
				language = { id: grammar.language, name: grammar.language, extensions: [], hasGrammar: true, hasSnippets: false };
J
Joao Moreno 已提交
855 856 857 858 859 860 861
				byId[language.id] = language;
				languages.push(language);
			} else {
				language.hasGrammar = true;
			}
		});

J
Joao Moreno 已提交
862 863 864 865 866 867 868 869 870 871 872 873 874 875
		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 已提交
876
		if (!languages.length) {
877
			return false;
J
Joao Moreno 已提交
878 879
		}

J
Joao Moreno 已提交
880
		const details = $('details', { open: true, ontoggle: onDetailsToggle },
J
Joao Moreno 已提交
881 882 883
			$('summary', null, localize('languages', "Languages ({0})", languages.length)),
			$('table', null,
				$('tr', null,
J
Joao Moreno 已提交
884 885
					$('th', null, localize('language id', "ID")),
					$('th', null, localize('language name', "Name")),
J
Joao Moreno 已提交
886
					$('th', null, localize('file extensions', "File Extensions")),
J
Joao Moreno 已提交
887 888
					$('th', null, localize('grammar', "Grammar")),
					$('th', null, localize('snippets', "Snippets"))
J
Joao Moreno 已提交
889 890
				),
				...languages.map(l => $('tr', null,
J
Joao Moreno 已提交
891
					$('td', null, l.id),
J
Joao Moreno 已提交
892
					$('td', null, l.name),
J
Joao Moreno 已提交
893
					$('td', null, ...join(l.extensions.map(ext => $('code', null, ext)), ' ')),
J
Joao Moreno 已提交
894 895
					$('td', null, document.createTextNode(l.hasGrammar ? '✔︎' : '')),
					$('td', null, document.createTextNode(l.hasSnippets ? '✔︎' : ''))
J
Joao Moreno 已提交
896 897
				))
			)
J
Joao Moreno 已提交
898 899 900
		);

		append(container, details);
901
		return true;
J
Joao Moreno 已提交
902 903
	}

S
Sandeep Somavarapu 已提交
904
	private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding {
905 906
		let key: string;

J
Johannes Rieken 已提交
907
		switch (process.platform) {
908 909 910 911 912
			case 'win32': key = rawKeyBinding.win; break;
			case 'linux': key = rawKeyBinding.linux; break;
			case 'darwin': key = rawKeyBinding.mac; break;
		}

913
		const keyBinding = KeybindingIO.readKeybinding(key || rawKeyBinding.key, OS);
A
Alex Dima 已提交
914 915 916 917
		if (!keyBinding) {
			return null;
		}

S
Sandeep Somavarapu 已提交
918
		return this.keybindingService.resolveKeybinding(keyBinding)[0];
919 920
	}

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

J
Joao Moreno 已提交
924 925
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
926

J
Joao Moreno 已提交
927
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
928 929 930
	}

	layout(): void {
J
Joao Moreno 已提交
931
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
932 933
	}

934 935 936 937 938
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

939
		this.notificationService.error(err);
940 941
	}

J
Joao Moreno 已提交
942
	dispose(): void {
J
Joao Moreno 已提交
943 944
		this.transientDisposables = dispose(this.transientDisposables);
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
945 946 947
		super.dispose();
	}
}
948

949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
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 {
		const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor;
		if (activeEditor instanceof ExtensionEditor) {
			return activeEditor;
		}
		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()));

974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
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 {
		const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor;
		if (activeEditor instanceof ExtensionEditor) {
			return activeEditor;
		}
		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()));