diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionQuery.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionQuery.ts new file mode 100644 index 0000000000000000000000000000000000000000..3516a508a1225de367d820ab53513fe09371a7a4 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionQuery.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/extensionActions'; + +export class Query { + + constructor(public value: string, public sortBy: string, public sortOrder: string) {} + + static parse(value: string): Query { + let sortBy: string = undefined; + let sortOrder: string = undefined; + + value = value.replace(/@sort:(\w+)(-asc|-desc)?\b/g, (match, by: string, order: string) => { + if (order) { + sortOrder = order.substr(1); + } + + sortBy = by; + + return ''; + }); + + return new Query(value.trim(), sortBy, sortOrder); + } + + toString(): string { + let result = this.value; + + if (this.sortBy) { + result = `${ result }${ result ? ' ' : '' }@sort:${ this.sortBy }`; + + if (this.sortOrder) { + result = `${ result }-${ this.sortOrder }`; + } + } + + return result; + } + + isValid(): boolean { + return !!this.sortBy || !this.sortOrder; + } + + equals(other: Query): boolean { + return this.value === other.value && this.sortBy === other.sortBy && this.sortOrder === other.sortOrder; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 8d9cfa7dfd7534f1e3f83c1524dfa76cc69ac4d9..798e7ac632fad198fe4ce4f309105ad2e7156d16 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -17,6 +17,7 @@ import { IMessageService, LaterAction } from 'vs/platform/message/common/message import { ToggleViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Query } from './extensionQuery'; export class InstallAction extends Action { @@ -410,6 +411,50 @@ export class ShowRecommendedExtensionsAction extends Action { }); } + protected isEnabled(): boolean { + return true; + } +} + +export class ChangeSortAction extends Action { + + private query: Query; + private disposables: IDisposable[] = []; + + constructor( + id: string, + label: string, + onSearchChange: Event, + private sortBy: string, + private sortOrder: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + + if (this.sortBy === undefined && this.sortOrder === undefined) { + throw new Error('bad arguments'); + } + + this.query = Query.parse(''); + this.enabled = false; + onSearchChange(this.onSearchChange, this, this.disposables); + } + + private onSearchChange(value: string): void { + const query = Query.parse(value); + this.query = new Query(query.value, this.sortBy || query.sortBy, this.sortOrder || query.sortOrder); + this.enabled = this.query.isValid() && !this.query.equals(query); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search(this.query.toString()); + viewlet.focus(); + }); + } + protected isEnabled(): boolean { return true; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 9778acedac64b4f4f19801a26f41c158393ed466..abcafd338e887796090c12f8ed3bf4e08fcdf030 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -16,6 +16,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import EventOf, { mapEvent, filterEvent } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; import { domEvent } from 'vs/base/browser/event'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Viewlet } from 'vs/workbench/browser/viewlet'; @@ -26,9 +27,10 @@ import { PagedList } from 'vs/base/browser/ui/list/listPaging'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Delegate, Renderer } from './extensionsList'; import { IExtensionsWorkbenchService, IExtension, IExtensionsViewlet, VIEWLET_ID } from './extensions'; -import { ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction } from './extensionsActions'; +import { ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction } from './extensionsActions'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, SortBy, SortOrder, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from './extensionsInput'; +import { Query } from './extensionQuery'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -154,7 +156,12 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL), this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL), - this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL) + this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL), + new Separator(), + this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install.asc', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs', undefined), + new Separator(), + this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.asc', localize('ascending', "Sort Order: ↑"), this.onSearchChange, undefined, 'asc'), + this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.desc', localize('descending', "Sort Order: ↓"), this.onSearchChange, undefined, 'desc'), ]; } @@ -196,10 +203,11 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { return local.then(result => new PagedModel(result)); } + const query = Query.parse(value); let options: IQueryOptions = {}; - if (/@recommended/i.test(value)) { - value = value.replace(/@recommended/g, '').trim(); + if (/@recommended/i.test(query.value)) { + const value = query.value.replace(/@recommended/g, '').trim(); return this.extensionsWorkbenchService.queryLocal().then(local => { const names = this.tipsService.getRecommendations() @@ -215,29 +223,17 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { }); } - value = value.replace(/@sort:(\w+)(-asc|-desc)?\b/g, (match, by, order) => { - let sortOrder = SortOrder.Default; - - switch (order) { - case '-asc': sortOrder = SortOrder.Ascending; break; - case '-desc': sortOrder = SortOrder.Descending; break; - } - - let sortBy = SortBy.NoneOrRelevance; - - switch(by) { - case 'installs': sortBy = SortBy.InstallCount; break; - default: return match; - } - - options = assign(options, { sortBy, sortOrder }); - return ''; - }); + switch(query.sortBy) { + case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break; + } - value = value.trim(); + switch (query.sortOrder) { + case 'asc': options = assign(options, { sortOrder: SortOrder.Ascending }); break; + case 'desc': options = assign(options, { sortOrder: SortOrder.Descending }); break; + } - if (value) { - options = assign(options, { text: value }); + if (query.value) { + options = assign(options, { text: query.value }); } return this.extensionsWorkbenchService.queryGallery(options)