extensionsViewlet.ts 21.0 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/extensionsViewlet';
J
Joao Moreno 已提交
9 10
import { localize } from 'vs/nls';
import { ThrottledDelayer, always } from 'vs/base/common/async';
J
Joao Moreno 已提交
11
import { TPromise } from 'vs/base/common/winjs.base';
12
import { isPromiseCanceledError, onUnexpectedError, create as createError } from 'vs/base/common/errors';
J
Joao Moreno 已提交
13
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
J
Joao Moreno 已提交
14 15
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Builder, Dimension } from 'vs/base/browser/builder';
16
import { assign } from 'vs/base/common/objects';
J
Joao Moreno 已提交
17
import EventOf, { mapEvent, chain } from 'vs/base/common/event';
18
import { IAction } from 'vs/base/common/actions';
J
Joao Moreno 已提交
19
import { domEvent } from 'vs/base/browser/event';
J
Joao Moreno 已提交
20
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
J
Joao Moreno 已提交
21 22
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
J
Joao Moreno 已提交
23
import { Viewlet } from 'vs/workbench/browser/viewlet';
24
import { IViewlet } from 'vs/workbench/common/viewlet';
B
Benjamin Pasero 已提交
25
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Joao Moreno 已提交
26
import { append, $, addStandardDisposableListener, EventType, addClass, removeClass, toggleClass } from 'vs/base/browser/dom';
27
import { PagedModel, IPagedModel, mergePagers, IPager } from 'vs/base/common/paging';
J
Joao Moreno 已提交
28
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
J
Joao Moreno 已提交
29 30
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
31
import { Delegate, Renderer } from 'vs/workbench/parts/extensions/browser/extensionsList';
32
import { IExtensionsWorkbenchService, IExtension, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from '../common/extensions';
33
import {
34
	ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction,
J
Joao Moreno 已提交
35
	ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction
36 37
} from 'vs/workbench/parts/extensions/browser/extensionsActions';
import { InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
J
Joao Moreno 已提交
38
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, SortBy, SortOrder, IQueryOptions, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
39
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
J
Joao Moreno 已提交
40
import { Query } from '../common/extensionQuery';
41
import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
J
Joao Moreno 已提交
42
import { IProgressService } from 'vs/platform/progress/common/progress';
43
import { IListService } from 'vs/platform/list/browser/listService';
J
Joao Moreno 已提交
44
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
45
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
J
Joao Moreno 已提交
46 47
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
import Severity from 'vs/base/common/severity';
48
import { IActivityBarService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService';
S
Sandeep Somavarapu 已提交
49
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
50
import { IModeService } from 'vs/editor/common/services/modeService';
J
Joao Moreno 已提交
51

52 53 54 55
interface SearchInputEvent extends Event {
	target: HTMLInputElement;
	immediate?: boolean;
}
J
Joao Moreno 已提交
56

57
export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
J
Joao Moreno 已提交
58

59
	private onSearchChange: EventOf<string>;
J
Joao Moreno 已提交
60
	private searchDelayer: ThrottledDelayer<any>;
J
Joao Moreno 已提交
61
	private root: HTMLElement;
J
Joao Moreno 已提交
62 63
	private searchBox: HTMLInputElement;
	private extensionsBox: HTMLElement;
J
Joao Moreno 已提交
64
	private messageBox: HTMLElement;
J
Joao Moreno 已提交
65
	private list: PagedList<IExtension>;
J
Joao Moreno 已提交
66 67
	private primaryActions: IAction[];
	private secondaryActions: IAction[];
J
Joao Moreno 已提交
68
	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
69

J
Joao Moreno 已提交
70 71
	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
72
		@IExtensionGalleryService private galleryService: IExtensionGalleryService,
S
Sandeep Somavarapu 已提交
73
		@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
J
Joao Moreno 已提交
74
		@IProgressService private progressService: IProgressService,
J
Joao Moreno 已提交
75
		@IInstantiationService private instantiationService: IInstantiationService,
76
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
77
		@IEditorGroupService private editorInputService: IEditorGroupService,
78
		@IListService private listService: IListService,
J
Joao Moreno 已提交
79
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
J
Joao Moreno 已提交
80
		@IExtensionTipsService private tipsService: IExtensionTipsService,
81
		@IMessageService private messageService: IMessageService,
S
Sandeep Somavarapu 已提交
82
		@IViewletService private viewletService: IViewletService,
83 84
		@IExtensionService private extensionService: IExtensionService,
		@IModeService private modeService: IModeService
J
Joao Moreno 已提交
85
	) {
J
Joao Moreno 已提交
86
		super(VIEWLET_ID, telemetryService);
J
Joao Moreno 已提交
87
		this.searchDelayer = new ThrottledDelayer(500);
J
Joao Moreno 已提交
88

89
		this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
J
Joao Moreno 已提交
90 91 92 93
	}

	create(parent: Builder): TPromise<void> {
		super.create(parent);
J
Joao Moreno 已提交
94 95 96
		parent.addClass('extensions-viewlet');
		this.root = parent.getHTMLElement();

J
Joao Moreno 已提交
97 98 99 100
		const header = append(this.root, $('.header'));

		this.searchBox = append(header, $<HTMLInputElement>('input.search-box'));
		this.searchBox.placeholder = localize('searchExtensions', "Search Extensions in Marketplace");
I
isidor 已提交
101 102
		this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.FOCUS, () => addClass(this.searchBox, 'synthetic-focus')));
		this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.BLUR, () => removeClass(this.searchBox, 'synthetic-focus')));
J
Joao Moreno 已提交
103

J
Joao Moreno 已提交
104
		this.extensionsBox = append(this.root, $('.extensions'));
J
Joao Moreno 已提交
105
		this.messageBox = append(this.root, $('.message'));
J
Joao Moreno 已提交
106 107

		const delegate = new Delegate();
108
		const renderer = this.instantiationService.createInstance(Renderer);
J
João Moreno 已提交
109
		this.list = new PagedList(this.extensionsBox, delegate, [renderer], {
110 111
			ariaLabel: localize('extensions', "Extensions"),
			keyboardSupport: false
J
João Moreno 已提交
112
		});
J
Joao Moreno 已提交
113

114 115
		this.disposables.push(this.listService.register(this.list.widget));

J
Joao Moreno 已提交
116
		const onKeyDown = chain(domEvent(this.searchBox, 'keydown'))
J
Joao Moreno 已提交
117
			.filter(() => this.list.length > 0)
J
Joao Moreno 已提交
118 119 120 121 122 123 124 125
			.map(e => new StandardKeyboardEvent(e));

		onKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables);
		onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables);
		onKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.disposables);
		onKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables);
		onKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables);
		onKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables);
J
Joao Moreno 已提交
126

127 128 129 130
		const onSearchInput = domEvent(this.searchBox, 'input') as EventOf<SearchInputEvent>;
		onSearchInput(e => this.triggerSearch(e.target.value, e.immediate), null, this.disposables);

		this.onSearchChange = mapEvent(onSearchInput, e => e.target.value);
J
Joao Moreno 已提交
131

J
Joao Moreno 已提交
132 133 134
		chain(this.list.onSelectionChange)
			.map(e => e.elements[0])
			.filter(e => !!e)
135
			.on(this.openExtension, this, this.disposables);
J
Joao Moreno 已提交
136

J
Joao Moreno 已提交
137 138 139
		return TPromise.as(null);
	}

J
Johannes Rieken 已提交
140
	setVisible(visible: boolean): TPromise<void> {
J
Joao Moreno 已提交
141 142
		return super.setVisible(visible).then(() => {
			if (visible) {
J
Joao Moreno 已提交
143
				this.searchBox.focus();
144 145
				this.searchBox.setSelectionRange(0, this.searchBox.value.length);
				this.triggerSearch(this.searchBox.value, true, true);
J
Joao Moreno 已提交
146
			} else {
J
Joao Moreno 已提交
147
				this.setModel(new PagedModel([]));
J
Joao Moreno 已提交
148 149
			}
		});
J
Joao Moreno 已提交
150 151 152
	}

	focus(): void {
J
Joao Moreno 已提交
153
		this.searchBox.focus();
J
Joao Moreno 已提交
154 155
	}

J
Johannes Rieken 已提交
156
	layout({ height, width }: Dimension): void {
J
Joao Moreno 已提交
157
		this.list.layout(height - 38);
J
Joao Moreno 已提交
158 159 160 161 162
		toggleClass(this.root, 'narrow', width <= 300);
	}

	getOptimalWidth(): number {
		return 400;
J
Joao Moreno 已提交
163 164
	}

J
Joao Moreno 已提交
165
	getActions(): IAction[] {
J
Joao Moreno 已提交
166 167 168 169
		if (!this.primaryActions) {
			this.primaryActions = [
				this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange)
			];
170 171
		}

J
Joao Moreno 已提交
172
		return this.primaryActions;
J
Joao Moreno 已提交
173 174
	}

175
	getSecondaryActions(): IAction[] {
J
Joao Moreno 已提交
176 177 178 179
		if (!this.secondaryActions) {
			this.secondaryActions = [
				this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL),
				this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL),
180
				this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL),
J
Joao Moreno 已提交
181
				this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL),
S
Sandeep Somavarapu 已提交
182
				this.instantiationService.createInstance(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL),
183
				this.instantiationService.createInstance(ShowRecommendedKeymapExtensionsAction, ShowRecommendedKeymapExtensionsAction.ID, ShowRecommendedKeymapExtensionsAction.LABEL),
J
Joao Moreno 已提交
184 185
				this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL),
				new Separator(),
J
Joao Moreno 已提交
186 187
				this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs', undefined),
				this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating', undefined),
J
Joao Moreno 已提交
188
				new Separator(),
J
Joao Moreno 已提交
189
				this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort..asc', localize('ascending', "Sort Order: ↑"), this.onSearchChange, undefined, 'asc'),
J
Joao Moreno 已提交
190 191
				this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort..desc', localize('descending', "Sort Order: ↓"), this.onSearchChange, undefined, 'desc'),
				new Separator(),
J
Joao Moreno 已提交
192
				this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL),
J
Joao Moreno 已提交
193 194
				this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL),
				this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)
J
Joao Moreno 已提交
195 196 197 198
			];
		}

		return this.secondaryActions;
199 200
	}

J
Joao Moreno 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214
	private setModel(model: IPagedModel<IExtension>) {
		this.list.model = model;
		this.list.scrollTop = 0;

		toggleClass(this.extensionsBox, 'hidden', model.length === 0);
		toggleClass(this.messageBox, 'hidden', model.length > 0);

		if (model.length === 0 && this.isVisible()) {
			this.messageBox.textContent = localize('no extensions found', "No extensions found.");
		} else {
			this.messageBox.textContent = '';
		}
	}

215
	search(value: string): void {
216
		const event = new Event('input', { bubbles: true }) as SearchInputEvent;
217
		event.immediate = true;
218 219 220

		this.searchBox.value = value;
		this.searchBox.dispatchEvent(event);
J
Joao Moreno 已提交
221 222
	}

223
	private triggerSearch(value: string, immediate = false, suggestPopular = false): void {
J
Joao Moreno 已提交
224 225
		this.searchDelayer.trigger(() => this.doSearch(value, suggestPopular), immediate || !value ? 0 : 500)
			.done(null, err => this.onError(err));
J
Joao Moreno 已提交
226 227
	}

228
	private doSearch(value: string = '', suggestPopular = false): TPromise<any> {
229 230 231
		return this.progress(this.query(value))
			.then(model => {
				if (!value && model.length === 0 && suggestPopular) {
J
Joao Moreno 已提交
232
					return this.search('@sort:installs ');
233 234
				}

J
Joao Moreno 已提交
235
				this.setModel(model);
236 237 238
			});
	}

J
Joao Moreno 已提交
239
	private query(value: string): TPromise<IPagedModel<IExtension>> {
S
Sandeep Somavarapu 已提交
240
		if (!value || /@installed/i.test(value)) {
241
			// Show installed extensions
S
Sandeep Somavarapu 已提交
242
			value = value ? value.replace(/@installed/g, '').trim().toLowerCase() : '';
243
			return this.extensionsWorkbenchService.queryLocal()
244
				.then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)))
S
Sandeep Somavarapu 已提交
245
				.then(result => result.filter(e => e.type === LocalExtensionType.User && e.name.toLowerCase().indexOf(value) > -1))
246 247
				.then(result => new PagedModel(result));
		}
248

249
		if (/@outdated/i.test(value)) {
S
Sandeep Somavarapu 已提交
250
			value = value.replace(/@outdated/g, '').trim().toLowerCase();
251
			return this.extensionsWorkbenchService.queryLocal()
J
Joao Moreno 已提交
252
				.then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)))
S
Sandeep Somavarapu 已提交
253
				.then(extensions => extensions.filter(extension => extension.outdated && extension.name.toLowerCase().indexOf(value) > -1))
254
				.then(result => new PagedModel(result));
255
		}
256

257
		if (/@disabled/i.test(value)) {
S
Sandeep Somavarapu 已提交
258
			value = value.replace(/@disabled/g, '').trim().toLowerCase();
259
			return this.extensionsWorkbenchService.queryLocal()
J
Joao Moreno 已提交
260
				.then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)))
S
Sandeep Somavarapu 已提交
261
				.then(result => this.extensionService.getExtensions()
S
Sandeep Somavarapu 已提交
262
					.then(runningExtensions => result.filter(e => runningExtensions.every(r => r.id !== e.identifier) && e.name.toLowerCase().indexOf(value) > -1)))
263 264 265
				.then(result => new PagedModel(result));
		}

J
Joao Moreno 已提交
266
		const query = Query.parse(value);
267 268
		let options: IQueryOptions = {};

J
Johannes Rieken 已提交
269
		switch (query.sortBy) {
J
Joao Moreno 已提交
270 271 272 273 274 275 276 277 278
			case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break;
			case 'rating': options = assign(options, { sortBy: SortBy.AverageRating }); break;
		}

		switch (query.sortOrder) {
			case 'asc': options = assign(options, { sortOrder: SortOrder.Ascending }); break;
			case 'desc': options = assign(options, { sortOrder: SortOrder.Descending }); break;
		}

S
Sandeep Somavarapu 已提交
279
		if (/@recommended:workspace/i.test(query.value)) {
280
			return this.getWorkspaceRecommendationsModel(query, options);
281 282
		} else if (/@recommended:keymaps/i.test(query.value)) {
			return this.getKeymapRecommendationsModel(query, options);
283 284
		} else if (/@recommended/i.test(query.value)) {
			return this.getRecommendationsModel(query, options);
J
Joao Moreno 已提交
285 286
		}

287 288 289 290 291 292 293 294 295
		const pagers: TPromise<IPager<IExtension>>[] = [];
		let text = query.value;
		const extensionRegex = /\bext:([^\s]+)\b/g;

		if (extensionRegex.test(query.value)) {
			let names: string[] = [];

			text = query.value.replace(extensionRegex, (m, ext) => {
				names.push(...this.tipsService.getRecommendationsForExtension(ext));
296 297 298 299 300 301 302 303 304 305 306

				// Get curated keywords
				const keywords = this.tipsService.getKeywordsForExtension(ext);

				// Get mode name
				const modeId = this.modeService.getModeIdByFilenameOrFirstLine(`.${ext}`);
				const languageName = modeId && this.modeService.getLanguageName(modeId);
				const languageTag = languageName ? ` tag:"${languageName}"` : '';

				// Construct a rich query
				return `tag:"__ext_${ext}"${keywords.map(tag => ` tag:${tag}`)}${languageTag}`;
307
			});
J
Joao Moreno 已提交
308

309 310 311
			console.log(text);
			console.log(names);

312 313 314 315 316
			if (names.length) {
				const namesOptions = assign({}, options, { names });
				pagers.push(this.extensionsWorkbenchService.queryGallery(namesOptions));
			}
		}
317

J
Joao Moreno 已提交
318
		if (text) {
319
			options = assign(options, { text: text.substr(0, 350) });
J
Joao Moreno 已提交
320
		}
321

322 323 324 325 326 327
		pagers.push(this.extensionsWorkbenchService.queryGallery(options));

		return TPromise.join(pagers).then(pagers => {
			const pager = pagers.length === 2 ? mergePagers(pagers[0], pagers[1]) : pagers[0];
			return new PagedModel(pager);
		});
J
Joao Moreno 已提交
328 329
	}

J
Joao Moreno 已提交
330
	private getRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
331 332 333 334 335 336
		const value = query.value.replace(/@recommended/g, '').trim().toLowerCase();

		return this.extensionsWorkbenchService.queryLocal()
			.then(result => result.filter(e => e.type === LocalExtensionType.User))
			.then(local => {
				const names = this.tipsService.getRecommendations()
J
Johannes Rieken 已提交
337
					.filter(name => local.every(ext => `${ext.publisher}.${ext.name}` !== name))
338 339 340 341 342
					.filter(name => name.toLowerCase().indexOf(value) > -1);

				this.telemetryService.publicLog('extensionRecommendations:open', { count: names.length });

				if (!names.length) {
J
Joao Moreno 已提交
343
					return TPromise.as(new PagedModel([]));
344 345 346 347 348 349 350
				}

				return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }))
					.then(result => new PagedModel(result));
			});
	}

J
Joao Moreno 已提交
351
	private getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
352
		const value = query.value.replace(/@recommended:workspace/g, '').trim().toLowerCase();
353 354 355 356
		return this.tipsService.getWorkspaceRecommendations()
			.then(recommendations => {
				const names = recommendations.filter(name => name.toLowerCase().indexOf(value) > -1);
				this.telemetryService.publicLog('extensionWorkspaceRecommendations:open', { count: names.length });
357

358 359 360
				if (!names.length) {
					return TPromise.as(new PagedModel([]));
				}
361

362 363 364
				return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }))
					.then(result => new PagedModel(result));
			});
S
Sandeep Somavarapu 已提交
365 366
	}

367 368 369 370 371 372 373 374 375 376 377 378 379 380
	private getKeymapRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
		const value = query.value.replace(/@recommended:keymaps/g, '').trim().toLowerCase();
		const names = this.tipsService.getKeymapRecommendations()
			.filter(name => name.toLowerCase().indexOf(value) > -1);
		this.telemetryService.publicLog('extensionKeymapRecommendations:open', { count: names.length });

		if (!names.length) {
			return TPromise.as(new PagedModel([]));
		}

		return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }))
			.then(result => new PagedModel(result));
	}

381 382 383 384
	private openExtension(extension: IExtension): void {
		this.extensionsWorkbenchService.open(extension).done(null, err => this.onError(err));
	}

J
Joao Moreno 已提交
385
	private onEnter(): void {
J
Joao Moreno 已提交
386
		this.list.setSelection(this.list.getFocus());
J
Joao Moreno 已提交
387 388 389
	}

	private onEscape(): void {
390
		this.search('');
J
Joao Moreno 已提交
391 392 393 394
	}

	private onUpArrow(): void {
		this.list.focusPrevious();
J
Joao Moreno 已提交
395
		this.list.reveal(this.list.getFocus()[0]);
J
Joao Moreno 已提交
396 397 398 399
	}

	private onDownArrow(): void {
		this.list.focusNext();
J
Joao Moreno 已提交
400
		this.list.reveal(this.list.getFocus()[0]);
J
Joao Moreno 已提交
401 402
	}

J
Joao Moreno 已提交
403 404 405 406 407 408 409 410 411 412
	private onPageUpArrow(): void {
		this.list.focusPreviousPage();
		this.list.reveal(this.list.getFocus()[0]);
	}

	private onPageDownArrow(): void {
		this.list.focusNextPage();
		this.list.reveal(this.list.getFocus()[0]);
	}

413 414 415 416 417
	private progress<T>(promise: TPromise<T>): TPromise<T> {
		const progressRunner = this.progressService.show(true);
		return always(promise, () => progressRunner.done());
	}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
	private onViewletOpen(viewlet: IViewlet): void {
		if (!viewlet || viewlet.getId() === VIEWLET_ID) {
			return;
		}

		const model = this.editorInputService.getStacksModel();

		const promises = model.groups.map(group => {
			const position = model.positionOfGroup(group);
			const inputs = group.getEditors().filter(input => input instanceof ExtensionsInput);
			const promises = inputs.map(input => this.editorService.closeEditor(position, input));

			return TPromise.join(promises);
		});

		TPromise.join(promises).done(null, onUnexpectedError);
	}

J
Joao Moreno 已提交
436 437 438 439 440 441 442
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

		const message = err && err.message || '';

J
Joao Moreno 已提交
443
		if (/ECONNREFUSED/.test(message)) {
J
Joao Moreno 已提交
444 445
			const error = createError(localize('suggestProxyError', "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting."), {
				actions: [
446 447
					this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL),
					CloseAction
J
Joao Moreno 已提交
448 449 450 451 452 453 454 455 456 457
				]
			});

			this.messageService.show(Severity.Error, error);
			return;
		}

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

J
Joao Moreno 已提交
458
	dispose(): void {
J
Joao Moreno 已提交
459
		this.disposables = dispose(this.disposables);
J
Joao Moreno 已提交
460 461 462
		super.dispose();
	}
}
J
Joao Moreno 已提交
463 464 465 466

export class StatusUpdater implements IWorkbenchContribution {

	private disposables: IDisposable[];
467
	private badgeHandle: IDisposable;
J
Joao Moreno 已提交
468 469

	constructor(
470
		@IActivityBarService private activityBarService: IActivityBarService,
J
Joao Moreno 已提交
471 472 473 474 475 476 477 478 479 480
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
		extensionsWorkbenchService.onChange(this.onServiceChange, this, this.disposables);
	}

	getId(): string {
		return 'vs.extensions.statusupdater';
	}

	private onServiceChange(): void {
481 482 483

		dispose(this.badgeHandle);

J
Joao Moreno 已提交
484
		if (this.extensionsWorkbenchService.local.some(e => e.state === ExtensionState.Installing)) {
485
			this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', "Extensions")), 'extensions-badge progress-badge');
J
Joao Moreno 已提交
486 487 488
			return;
		}

489 490 491
		const outdated = this.extensionsWorkbenchService.local.reduce((r, e) => r + (e.outdated ? 1 : 0), 0);
		if (outdated > 0) {
			const badge = new NumberBadge(outdated, n => localize('outdatedExtensions', '{0} Outdated Extensions', n));
492
			this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, badge, 'extensions-badge count-badge');
493
		}
J
Joao Moreno 已提交
494 495 496 497
	}

	dispose(): void {
		this.disposables = dispose(this.disposables);
498
		dispose(this.badgeHandle);
J
Joao Moreno 已提交
499 500
	}
}