extensionEditor.ts 37.6 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 } 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 47
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
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 53 54

/**  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);
55 56
/**  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 已提交
57

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

74 75 76 77 78 79 80 81 82
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 已提交
83
	return newDocument.documentElement.outerHTML;
84 85
}

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

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

J
Joao Moreno 已提交
91
	private currentId: string = null;
J
Joao Moreno 已提交
92 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 });
	}

	push(id: string, label: string): void {
J
Joao Moreno 已提交
102
		const run = () => this._update(id);
J
Joao Moreno 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
		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 已提交
118 119 120 121 122 123 124 125 126 127 128
	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 已提交
129 130 131 132 133 134 135
	dispose(): void {
		this.actionbar = dispose(this.actionbar);
	}
}

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

J
Joao Moreno 已提交
141 142 143 144
interface ILayoutParticipant {
	layout(): void;
}

J
Joao Moreno 已提交
145 146
export class ExtensionEditor extends BaseEditor {

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

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

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

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

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
M
Matt Bierner 已提交
182 183 184
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IViewletService private readonly viewletService: IViewletService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
185
		@IThemeService protected themeService: IThemeService,
M
Matt Bierner 已提交
186 187 188 189 190 191
		@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 已提交
192
	) {
B
Benjamin Pasero 已提交
193
		super(ExtensionEditor.ID, telemetryService, themeService);
J
Joao Moreno 已提交
194
		this.disposables = [];
J
Joao Moreno 已提交
195
		this.extensionReadme = null;
X
XVincentX 已提交
196
		this.extensionChangelog = null;
J
Joao Moreno 已提交
197
		this.extensionManifest = null;
S
Sandeep Somavarapu 已提交
198
		this.extensionDependencies = null;
199 200
		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 已提交
201 202
	}

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

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

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

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

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

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

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

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

227 228 229 230
		this.repository = append(subtitle, $('span.repository.clickable'));
		this.repository.textContent = localize('repository', 'Repository');
		this.repository.style.display = 'none';

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

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

J
Joao Moreno 已提交
237
		const extensionActions = append(details, $('.actions'));
238 239 240 241 242 243 244 245 246 247 248 249
		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 已提交
250 251
		this.disposables.push(this.extensionActionBar);

252 253
		this.recommendation = append(details, $('.recommendation'));

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

J
Joao Moreno 已提交
259 260
		const body = append(root, $('.body'));
		this.navbar = new NavBar(body);
J
Joao Moreno 已提交
261

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

	setInput(input: ExtensionsInput, options: EditorOptions): TPromise<void> {
266
		this.editorLoadComplete = false;
J
Joao Moreno 已提交
267 268
		const extension = input.extension;

J
Joao Moreno 已提交
269 270
		this.transientDisposables = dispose(this.transientDisposables);

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

276 277 278 279
		const onError = once(domEvent(this.icon, 'error'));
		onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
		this.icon.src = extension.iconUrl;

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

J
Joao Moreno 已提交
285 286
		this.publisher.textContent = extension.publisherDisplayName;
		this.description.textContent = extension.description;
J
Joao Moreno 已提交
287

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

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

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

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

334 335 336 337 338 339 340 341 342
		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';
		}

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

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

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

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

J
Joao Moreno 已提交
365
		this.extensionActionBar.clear();
S
Sandeep Somavarapu 已提交
366 367
		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 已提交
368

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

J
Joao Moreno 已提交
371 372
		this.navbar.clear();
		this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
373 374
		this.navbar.push(NavbarSection.Readme, localize('details', "Details"));
		this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"));
375 376
		this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"));
		this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"));
J
Joao Moreno 已提交
377

378
		this.editorLoadComplete = true;
J
Joao Moreno 已提交
379
		return super.setInput(input, options);
J
Joao Moreno 已提交
380
	}
381

382 383 384 385 386 387
	showFind(): void {
		if (this.activeWebview) {
			this.activeWebview.showFind();
		}
	}

388 389 390 391 392 393 394 395 396 397 398 399
	public showNextFindTerm() {
		if (this.activeWebview) {
			this.activeWebview.showNextFindTerm();
		}
	}

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

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

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

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

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

452 453
	private openReadme() {
		return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available."));
454 455
	}

456
	private openChangelog() {
457
		return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available."));
458 459
	}

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

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

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

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

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

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

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

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

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

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

S
Sandeep Somavarapu 已提交
541
		tree.setInput(extensionDependencies);
542

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

S
Sandeep Somavarapu 已提交
549 550 551
		return tree;
	}

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

J
Joao Moreno 已提交
558
		if (!contrib.length) {
559
			return false;
J
Joao Moreno 已提交
560 561
		}

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

		append(container, details);
579
		return true;
J
Joao Moreno 已提交
580 581
	}

582 583


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

J
Joao Moreno 已提交
588
		if (!contrib.length) {
589
			return false;
J
Joao Moreno 已提交
590 591
		}

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

		append(container, details);
606
		return true;
J
Joao Moreno 已提交
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 632 633 634
	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;
	}

635 636 637 638 639 640 641 642 643 644 645
	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 已提交
646
				$('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)"))),
647
				...localizations.map(localization => $('tr', null, $('td', null, localization.languageId), $('td', null, localization.languageName), $('td', null, localization.languageNameLocalized)))
648 649 650 651 652 653 654
			)
		);

		append(container, details);
		return true;
	}

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

J
Joao Moreno 已提交
659
		if (!contrib.length) {
660
			return false;
J
Joao Moreno 已提交
661 662
		}

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

		append(container, details);
686
		return true;
J
Joao Moreno 已提交
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 731 732 733
	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 已提交
734
	private renderJSONValidation(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
J
Joao Moreno 已提交
735 736
		const contributes = manifest.contributes;
		const contrib = contributes && contributes.jsonValidation || [];
J
Joao Moreno 已提交
737 738

		if (!contrib.length) {
739
			return false;
J
Joao Moreno 已提交
740 741
		}

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

		append(container, details);
755
		return true;
J
Joao Moreno 已提交
756 757
	}

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

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

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

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

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

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

788
		rawKeybindings.forEach(rawKeybinding => {
S
Sandeep Somavarapu 已提交
789
			const keybinding = this.resolveKeybinding(rawKeybinding);
J
Joao Moreno 已提交
790

S
Sandeep Somavarapu 已提交
791
			if (!keybinding) {
J
Joao Moreno 已提交
792 793 794
				return;
			}

J
Joao Moreno 已提交
795
			let command = byId[rawKeybinding.command];
J
Joao Moreno 已提交
796 797

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

806
		if (!commands.length) {
807
			return false;
808 809
		}

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

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

		append(container, details);
835
		return true;
836 837
	}

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

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

J
Joao Moreno 已提交
851
		const grammars = contributes && contributes.grammars || [];
J
Joao Moreno 已提交
852 853 854 855 856

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

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

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

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

		append(container, details);
904
		return true;
J
Joao Moreno 已提交
905 906
	}

S
Sandeep Somavarapu 已提交
907
	private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding {
908 909
		let key: string;

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

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

S
Sandeep Somavarapu 已提交
921
		return this.keybindingService.resolveKeybinding(keyBinding)[0];
922 923
	}

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

J
Joao Moreno 已提交
927 928
		let promise = loadingTask();
		promise = always(promise, () => removeClass(this.content, 'loading'));
J
Joao Moreno 已提交
929

J
Joao Moreno 已提交
930
		this.contentDisposables.push(toDisposable(() => promise.cancel()));
J
Joao Moreno 已提交
931 932 933
	}

	layout(): void {
J
Joao Moreno 已提交
934
		this.layoutParticipants.forEach(p => p.layout());
J
Joao Moreno 已提交
935 936
	}

937 938 939 940 941
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

942
		this.notificationService.error(err);
943 944
	}

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

952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
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()));

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 1016 1017 1018
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()));