runtimeExtensionsEditor.ts 10.7 KB
Newer Older
A
Alex Dima 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import 'vs/css!./media/runtimeExtensionsEditor';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { EditorInput } from 'vs/workbench/common/editor';
import { TPromise } from 'vs/base/common/winjs.base';
13
import { Action, IAction } from 'vs/base/common/actions';
A
Alex Dima 已提交
14 15 16 17 18 19 20 21 22 23 24
import { Builder, Dimension } from 'vs/base/browser/builder';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService, IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
A
Alex Dima 已提交
25
import { append, $, addDisposableListener, addClass, toggleClass } from 'vs/base/browser/dom';
A
Alex Dima 已提交
26 27 28
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
I
isidor 已提交
29
import { ExtensionHostProfileAction, ReportExtensionIssueAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
A
Alex Dima 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

interface IRuntimeExtension {

	description: IExtensionDescription;
	marketplaceInfo: IExtension;
	status: IExtensionsStatus;

}

export class RuntimeExtensionsEditor extends BaseEditor {

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

	private _list: WorkbenchList<IRuntimeExtension>;
	private _elements: IRuntimeExtension[];
	private _extensionsDescriptions: IExtensionDescription[];

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IThemeService themeService: IThemeService,
		@IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionService private readonly _extensionService: IExtensionService,
		@IListService private readonly _listService: IListService,
		@IContextKeyService private readonly _contextKeyService: IContextKeyService,
		@IMessageService private readonly _messageService: IMessageService,

	) {
		super(RuntimeExtensionsEditor.ID, telemetryService, themeService);

		this._list = null;
		this._elements = null;

		this._extensionsDescriptions = [];
		this._updateExtensions();

		this._extensionService.getExtensions().then((extensions) => {
A
Alex Dima 已提交
66 67 68 69
			// We only deal with extensions with source code!
			this._extensionsDescriptions = extensions.filter((extension) => {
				return !!extension.main;
			});
A
Alex Dima 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
			this._updateExtensions();
		});
		this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateExtensions()));

		// TODO@Alex TODO@Isi ????
		// this._extensionsWorkbenchService.onChange(() => this._updateExtensions());
	}

	private _updateExtensions(): void {
		this._elements = this._resolveExtensions();
		if (this._list) {
			this._list.splice(0, this._list.length, this._elements);
		}
	}

	private _resolveExtensions(): IRuntimeExtension[] {
		let marketplaceMap: { [id: string]: IExtension; } = Object.create(null);
		for (let extension of this._extensionsWorkbenchService.local) {
			marketplaceMap[extension.id] = extension;
		}

		let statusMap = this._extensionService.getExtensionsStatus();

		let result: IRuntimeExtension[] = [];
		for (let i = 0, len = this._extensionsDescriptions.length; i < len; i++) {
			const extensionDescription = this._extensionsDescriptions[i];

			result[i] = {
				description: extensionDescription,
				marketplaceInfo: marketplaceMap[extensionDescription.id],
				status: statusMap[extensionDescription.id]
			};
		}

A
Alex Dima 已提交
104
		return result.filter((element) => element.status.activationTimes);
A
Alex Dima 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	}

	protected createEditor(parent: Builder): void {
		const container = parent.getHTMLElement();

		addClass(container, 'runtime-extensions-editor');

		const TEMPLATE_ID = 'runtimeExtensionElementTemplate';

		const delegate = new class implements IDelegate<IRuntimeExtension>{
			getHeight(element: IRuntimeExtension): number {
				return 62;
			}
			getTemplateId(element: IRuntimeExtension): string {
				return TEMPLATE_ID;
			}
		};

		interface IRuntimeExtensionTemplateData {
			root: HTMLElement;
			element: HTMLElement;
			icon: HTMLImageElement;
			name: HTMLElement;
A
Alex Dima 已提交
128

A
Alex Dima 已提交
129 130 131
			timeContainer: HTMLElement;
			timeIcon: HTMLElement;
			timeLabel: HTMLElement;
A
Alex Dima 已提交
132 133 134 135

			msgIcon: HTMLElement;
			msgLabel: HTMLElement;

I
isidor 已提交
136
			actionbar: ActionBar;
A
Alex Dima 已提交
137 138 139 140 141 142 143 144 145
			disposables: IDisposable[];
			elementDisposables: IDisposable[];
		}

		const renderer: IRenderer<IRuntimeExtension, IRuntimeExtensionTemplateData> = {
			templateId: TEMPLATE_ID,
			renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => {
				const element = append(root, $('.extension'));
				const icon = append(element, $<HTMLImageElement>('img.icon'));
A
Alex Dima 已提交
146 147 148

				const desc = append(element, $('div.desc'));
				const name = append(desc, $('div.name'));
A
Alex Dima 已提交
149

A
Alex Dima 已提交
150
				const timeContainer = append(desc, $('div.time'));
A
Alex Dima 已提交
151 152
				const timeIcon = append(timeContainer, $('span.octicon.octicon-clock'));
				const timeLabel = append(timeContainer, $('span.time-label'));
A
Alex Dima 已提交
153 154 155

				const msgContainer = append(desc, $('div.msg'));
				const msgIcon = append(msgContainer, $('.'));
A
Alex Dima 已提交
156
				const msgLabel = append(msgContainer, $('span.msg-label'));
A
Alex Dima 已提交
157

A
Alex Dima 已提交
158
				const actionbar = new ActionBar(element, {
I
isidor 已提交
159
					animated: false
A
Alex Dima 已提交
160 161
				});
				actionbar.onDidRun(({ error }) => error && this._messageService.show(Severity.Error, error));
I
isidor 已提交
162
				actionbar.push(new ReportExtensionIssueAction(ReportExtensionIssueAction.ID, ReportExtensionIssueAction.LABEL, this._extensionsWorkbenchService), { icon: false });
A
Alex Dima 已提交
163 164 165 166 167 168 169 170

				const disposables = [actionbar];

				return {
					root,
					element,
					icon,
					name,
I
isidor 已提交
171
					actionbar,
A
Alex Dima 已提交
172 173 174
					timeContainer,
					timeIcon,
					timeLabel,
A
Alex Dima 已提交
175 176
					msgIcon,
					msgLabel,
A
Alex Dima 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
					disposables,
					elementDisposables: []
				};
			},

			renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => {

				data.elementDisposables = dispose(data.elementDisposables);

				data.elementDisposables.push(
					addDisposableListener(data.icon, 'error', () => {
						data.icon.src = element.marketplaceInfo.iconUrlFallback;
					})
				);
				data.icon.src = element.marketplaceInfo.iconUrl;

				data.name.textContent = element.marketplaceInfo.displayName;
A
Alex Dima 已提交
194 195 196

				const activationTimes = element.status.activationTimes;
				let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime;
A
Alex Dima 已提交
197
				data.timeLabel.textContent = `${syncTime}ms`;
I
isidor 已提交
198
				data.actionbar.context = element.marketplaceInfo;
A
Alex Dima 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

				let title: string;
				if (activationTimes.activationEvent === '*') {
					title = nls.localize('starActivation', "Activated on start-up");
				} else if (/^workspaceContains:/.test(activationTimes.activationEvent)) {
					let fileNameOrGlob = activationTimes.activationEvent.substr('workspaceContains:'.length);
					if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
						title = nls.localize('workspaceContainsGlobActivation', "Activated because a file matching {0} exists in your workspace", fileNameOrGlob);
					} else {
						title = nls.localize('workspaceContainsFileActivation', "Activated because file {0} exists in your workspace", fileNameOrGlob);
					}
				} else if (/^onLanguage:/.test(activationTimes.activationEvent)) {
					let language = activationTimes.activationEvent.substr('onLanguage:'.length);
					title = nls.localize('languageActivation', "Activated because you opened a {0} file", language);
				} else {
					title = nls.localize('workspaceGenericActivation', "Activated on {0}", activationTimes.activationEvent);
				}
A
Alex Dima 已提交
216 217 218 219 220 221
				data.timeContainer.title = title;

				toggleClass(data.timeContainer, 'on-startup', activationTimes.startup);
				if (activationTimes.startup) {
					data.timeIcon.className = 'octicon octicon-clock';
				} else {
A
Alex Dima 已提交
222 223 224 225 226 227 228 229 230
					data.timeIcon.className = 'octicon octicon-dashboard';
				}

				if (element.status.messages && element.status.messages.length > 0) {
					data.msgIcon.className = 'octicon octicon-alert';
					data.msgLabel.textContent = element.status.messages[0].message;
				} else {
					data.msgIcon.className = '';
					data.msgLabel.textContent = '';
A
Alex Dima 已提交
231
				}
A
Alex Dima 已提交
232 233 234 235 236 237 238 239 240 241 242 243 244 245
			},

			disposeTemplate: (data: IRuntimeExtensionTemplateData): void => {
				data.disposables = dispose(data.disposables);
			}
		};

		this._list = new WorkbenchList<IRuntimeExtension>(container, delegate, [renderer], {
			multipleSelectionSupport: false
		}, this._contextKeyService, this._listService, this.themeService);

		this._list.splice(0, this._list.length, this._elements);
	}

246 247 248 249
	public getActions(): IAction[] {
		return [new ExtensionHostProfileAction(ExtensionHostProfileAction.LABEL_START, ExtensionHostProfileAction.ID, this._extensionService)];
	}

A
Alex Dima 已提交
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	public layout(dimension: Dimension): void {
		this._list.layout(dimension.height);
	}
}

export class RuntimeExtensionsInput extends EditorInput {

	static ID = 'workbench.runtimeExtensions.input';

	constructor() {
		super();
	}

	getTypeId(): string {
		return RuntimeExtensionsInput.ID;
	}

	getName(): string {
		return nls.localize('extensionsInputName', "Running Extensions");
	}

	matches(other: any): boolean {
		if (!(other instanceof RuntimeExtensionsInput)) {
			return false;
		}
		return true;
	}

	resolve(refresh?: boolean): TPromise<any> {
		return TPromise.as(null);
	}

	supportsSplitEditor(): boolean {
		return false;
	}

	getResource(): URI {
		return URI.from({
			scheme: 'runtime-extensions',
			path: 'default'
		});
	}
}

export class ShowRuntimeExtensionsAction extends Action {
	static ID = 'workbench.action.showRuntimeExtensions';
	static LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions");

	constructor(
		id: string, label: string,
		@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
		@IInstantiationService private readonly _instantiationService: IInstantiationService
	) {
		super(id, label);
	}

	public run(e?: any): TPromise<any> {
		return this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput));
	}
}