diff options
Diffstat (limited to 'src/vs')
9 files changed, 128 insertions, 43 deletions
diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 064b2124435..c7cfa2a4619 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { getFallbackTargetPlarforms, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getFallbackTargetPlarforms, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, GallerySortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; @@ -216,7 +216,7 @@ const DefaultPageSize = 10; interface IQueryState { readonly pageNumber: number; readonly pageSize: number; - readonly sortBy: SortBy; + readonly sortBy: GallerySortBy; readonly sortOrder: SortOrder; readonly flags: Flags; readonly criteria: ICriterium[]; @@ -227,7 +227,7 @@ interface IQueryState { const DefaultQueryState: IQueryState = { pageNumber: 1, pageSize: DefaultPageSize, - sortBy: SortBy.NoneOrRelevance, + sortBy: GallerySortBy.NoneOrRelevance, sortOrder: SortOrder.Default, flags: Flags.None, criteria: [], @@ -315,8 +315,8 @@ class Query { return new Query({ ...this.state, criteria }); } - withSortBy(sortBy: SortBy): Query { - return sortBy !== SortBy.UpdateDate ? new Query({ ...this.state, sortBy }) : new Query({ ...this.state }); + withSortBy(sortBy: GallerySortBy): Query { + return new Query({ ...this.state, sortBy }); } withSortOrder(sortOrder: SortOrder): Query { @@ -746,13 +746,13 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi query = query.withFilter(FilterType.SearchText, text); } - query = query.withSortBy(SortBy.NoneOrRelevance); + query = query.withSortBy(GallerySortBy.NoneOrRelevance); } else if (options.ids) { query = query.withFilter(FilterType.ExtensionId, ...options.ids); } else if (options.names) { query = query.withFilter(FilterType.ExtensionName, ...options.names); } else { - query = query.withSortBy(SortBy.InstallCount); + query = query.withSortBy(GallerySortBy.InstallCount); } if (typeof options.sortBy === 'number') { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 73ef9335d17..9d7154fa478 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -251,7 +251,7 @@ export interface ILocalExtension extends IExtension { updated: boolean; } -export const enum SortBy { +export const enum GallerySortBy { NoneOrRelevance = 0, LastUpdatedDate = 1, Title = 2, @@ -260,7 +260,16 @@ export const enum SortBy { PublishedDate = 10, AverageRating = 6, WeightedRating = 12, - UpdateDate = 13, +} + +export enum LocalSortBy { + UpdateDate = 'UpdateDate', +} + +export type SortBy = GallerySortBy | LocalSortBy; + +export function isLocalSort(sortBy: SortBy): sortBy is LocalSortBy { + return (Object.values(LocalSortBy) as SortBy[]).includes(sortBy); } export const enum SortOrder { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 09d0ff85a9c..6f0c73a98c1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -930,11 +930,11 @@ export class ExtensionEditor extends EditorPane { if (gallery) { append(moreInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('published date', "Published on")), + $('div', undefined, localize('published', "Published")), $('div', undefined, new Date(gallery.releaseDate).toLocaleString(locale, { hourCycle: 'h23' })) ), $('.more-info-entry', undefined, - $('div', undefined, localize('latest release', "Latest release")), + $('div', undefined, localize('last released', "Last released")), $('div', undefined, new Date(gallery.lastUpdated).toLocaleString(locale, { hourCycle: 'h23' })) ) ); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 646f17e477d..0352dcda3f8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -14,11 +14,11 @@ import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsServi import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, DefaultViewsContext, ExtensionsSortByContext, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions'; import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; -import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; +import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext, DefaultViewsContext, ExtensionsSortByContext, SearchHasTextContext } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; @@ -941,7 +941,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menuTitles: { [extensionsFilterSubMenu.id]: localize('recently published filter', "Recently Published") }, - run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@sort:publishedDate ')) + run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recentlyPublished ')) }); const extensionsCategoryFilterSubMenu = new MenuId('extensionsCategoryFilterSubMenu'); @@ -985,6 +985,23 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); this.registerExtensionAction({ + id: 'workbench.extensions.action.recentlyUpdatedExtensions', + title: { value: localize('recentlyUpdatedExtensions', "Show Recently Updated Extensions"), original: 'Show Recently Updated Extensions' }, + category: ExtensionsLocalizedLabel, + menu: [{ + id: MenuId.CommandPalette, + }, { + id: extensionsFilterSubMenu, + group: '3_installed', + order: 7, + }], + menuTitles: { + [extensionsFilterSubMenu.id]: localize('recently updated filter', "Recently Updated") + }, + run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recentlyUpdated')) + }); + + this.registerExtensionAction({ id: LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, title: { value: localize('showWorkspaceUnsupportedExtensions', "Show Extensions Unsupported By Workspace"), original: 'Show Extensions Unsupported By Workspace' }, category: ExtensionsLocalizedLabel, @@ -1080,17 +1097,17 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi MenuRegistry.appendMenuItem(extensionsFilterSubMenu, <ISubmenuItem>{ submenu: extensionsSortSubMenu, title: localize('sorty by', "Sort By"), - when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), BuiltInExtensionsContext.negate()), + when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext)), group: '4_sort', order: 1, }); [ - { id: 'installs', title: localize('sort by installs', "Install Count") }, - { id: 'rating', title: localize('sort by rating', "Rating") }, - { id: 'name', title: localize('sort by name', "Name"), }, - { id: 'publishedDate', title: localize('sort by published date', "Published Date") }, - { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate()) }, + { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate() }, + { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate() }, + { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate() }, + { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate() }, + { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()) }, ].map(({ id, title, precondition }, index) => { this.registerExtensionAction({ id: `extensions.sort.${id}`, @@ -1118,7 +1135,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi category: ExtensionsLocalizedLabel, icon: clearSearchResultsIcon, f1: true, - precondition: DefaultViewsContext.toNegated(), + precondition: SearchHasTextContext, menu: { id: MenuId.ViewContainerTitle, when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index b956aafd4d2..50c81efc666 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -16,7 +16,7 @@ import { append, $, Dimension, hide, show, DragAndDropObserver } from 'vs/base/b import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, DefaultViewsContext, ExtensionsSortByContext, WORKSPACE_RECOMMENDATIONS_VIEW_ID, AutoCheckUpdatesConfigurationKey } from '../common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, AutoCheckUpdatesConfigurationKey } from '../common/extensions'; import { InstallLocalExtensionsInRemoteAction, InstallRemoteExtensionsInLocalAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -61,8 +61,12 @@ import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { extname } from 'vs/base/common/resources'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +export const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true); +export const ExtensionsSortByContext = new RawContextKey<string>('extensionsSortByValue', ''); export const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false); +export const SearchHasTextContext = new RawContextKey<boolean>('extensionSearchHasText', false); const SearchInstalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false); +const SearchRecentlyUpdatedExtensionsContext = new RawContextKey<boolean>('searchRecentlyUpdatedExtensions', false); const SearchOutdatedExtensionsContext = new RawContextKey<boolean>('searchOutdatedExtensions', false); const SearchEnabledExtensionsContext = new RawContextKey<boolean>('searchEnabledExtensions', false); const SearchDisabledExtensionsContext = new RawContextKey<boolean>('searchDisabledExtensions', false); @@ -289,6 +293,16 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }); /* + * View used for searching recently updated extensions + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.searchRecentlyUpdated', + name: localize('recently updated', "Recently Updated"), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), + when: ContextKeyExpr.and(ContextKeyExpr.has('searchRecentlyUpdatedExtensions')), + }); + + /* * View used for searching enabled extensions */ viewDescriptors.push({ @@ -444,8 +458,10 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private defaultViewsContextKey: IContextKey<boolean>; private sortByContextKey: IContextKey<string>; private searchMarketplaceExtensionsContextKey: IContextKey<boolean>; + private searchHasTextContextKey: IContextKey<boolean>; private sortByUpdateDateContextKey: IContextKey<boolean>; private searchInstalledExtensionsContextKey: IContextKey<boolean>; + private searchRecentlyUpdatedExtensionsContextKey: IContextKey<boolean>; private searchOutdatedExtensionsContextKey: IContextKey<boolean>; private searchEnabledExtensionsContextKey: IContextKey<boolean>; private searchDisabledExtensionsContextKey: IContextKey<boolean>; @@ -488,8 +504,10 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.defaultViewsContextKey = DefaultViewsContext.bindTo(contextKeyService); this.sortByContextKey = ExtensionsSortByContext.bindTo(contextKeyService); this.searchMarketplaceExtensionsContextKey = SearchMarketplaceExtensionsContext.bindTo(contextKeyService); + this.searchHasTextContextKey = SearchHasTextContext.bindTo(contextKeyService); this.sortByUpdateDateContextKey = SortByUpdateDateContext.bindTo(contextKeyService); this.searchInstalledExtensionsContextKey = SearchInstalledExtensionsContext.bindTo(contextKeyService); + this.searchRecentlyUpdatedExtensionsContextKey = SearchRecentlyUpdatedExtensionsContext.bindTo(contextKeyService); this.searchWorkspaceUnsupportedExtensionsContextKey = SearchUnsupportedWorkspaceExtensionsContext.bindTo(contextKeyService); this.searchDeprecatedExtensionsContextKey = SearchDeprecatedExtensionsContext.bindTo(contextKeyService); this.searchOutdatedExtensionsContextKey = SearchOutdatedExtensionsContext.bindTo(contextKeyService); @@ -654,7 +672,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE const value = this.normalizedQuery(); this.contextKeyService.bufferChangeEvents(() => { const isRecommendedExtensionsQuery = ExtensionsListView.isRecommendedExtensionsQuery(value); + this.searchHasTextContextKey.set(value.trim() !== ''); this.searchInstalledExtensionsContextKey.set(ExtensionsListView.isInstalledExtensionsQuery(value)); + this.searchRecentlyUpdatedExtensionsContextKey.set(ExtensionsListView.isSearchRecentlyUpdatedQuery(value)); this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value)); this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value)); this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value)); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index cacf2cf72cb..88f6b03d252 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -9,7 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { isCancellationError, getErrorMessage } from 'vs/base/common/errors'; import { createErrorWithActions } from 'vs/base/common/errorMessage'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; -import { SortBy, SortOrder, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { SortOrder, IQueryOptions, GallerySortBy, LocalSortBy, isLocalSort } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService, EnablementState, IWorkbenchExtensionManagementService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions, getExtensionDependencies } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -238,11 +238,11 @@ export class ExtensionsListView extends ViewPane { }; switch (parsedQuery.sortBy) { - case 'installs': options.sortBy = SortBy.InstallCount; break; - case 'rating': options.sortBy = SortBy.WeightedRating; break; - case 'name': options.sortBy = SortBy.Title; break; - case 'publishedDate': options.sortBy = SortBy.PublishedDate; break; - case 'updateDate': options.sortBy = SortBy.UpdateDate; break; + case 'installs': options.sortBy = GallerySortBy.InstallCount; break; + case 'rating': options.sortBy = GallerySortBy.WeightedRating; break; + case 'name': options.sortBy = GallerySortBy.Title; break; + case 'publishedDate': options.sortBy = GallerySortBy.PublishedDate; break; + case 'updateDate': options.sortBy = LocalSortBy.UpdateDate; break; } const request = createCancelablePromise(async token => { @@ -329,9 +329,13 @@ export class ExtensionsListView extends ViewPane { return this.queryLocal(query, options); } - if (/@popular/i.test(query.value)) { + if (ExtensionsListView.isSearchPopularQuery(query.value)) { query.value = query.value.replace('@popular', ''); - options.sortBy = !options.sortBy ? SortBy.InstallCount : options.sortBy; + options.sortBy = !options.sortBy ? GallerySortBy.InstallCount : options.sortBy; + } + else if (ExtensionsListView.isSearchRecentlyPublishedQuery(query.value)) { + query.value = query.value.replace('@recentlyPublished', ''); + options.sortBy = !options.sortBy ? GallerySortBy.PublishedDate : options.sortBy; } const model = await this.queryGallery(query, options, token); @@ -421,6 +425,10 @@ export class ExtensionsListView extends ViewPane { extensions = await this.filterDeprecatedExtensions(local, query, options); } + else if (/@recentlyUpdated/i.test(query.value)) { + extensions = this.filterRecentlyUpdatedExtensions(local, query, options); + } + return { extensions, canIncludeInstalledExtensions }; } @@ -639,6 +647,23 @@ export class ExtensionsListView extends ViewPane { return this.sortExtensions(local, options); } + private filterRecentlyUpdatedExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] { + let { value, categories } = this.parseCategories(query.value); + const currentTime = Date.now(); + // Only show extensions which have been updated during the last 7 days + local = local.filter(e => !e.isBuiltin && e.local?.installedTimestamp !== undefined && currentTime - e.local.installedTimestamp < 604800000); + + value = value.replace(/@recentlyUpdated/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); + + const result = local.filter(e => + (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && + (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); + + options.sortBy = options.sortBy ?? LocalSortBy.UpdateDate; + + return this.sortExtensions(result, options); + } + private mergeAddedExtensions(extensions: IExtension[], newExtensions: IExtension[]): IExtension[] | undefined { const oldExtensions = [...extensions]; const findPreviousExtensionIndex = (from: number): number => { @@ -666,9 +691,13 @@ export class ExtensionsListView extends ViewPane { } private async queryGallery(query: Query, options: IQueryOptions, token: CancellationToken): Promise<IPagedModel<IExtension>> { + if (options.sortBy && isLocalSort(options.sortBy)) { + options.sortBy = undefined; + } + const hasUserDefinedSortOrder = options.sortBy !== undefined; if (!hasUserDefinedSortOrder && !query.value.trim()) { - options.sortBy = SortBy.InstallCount; + options.sortBy = GallerySortBy.InstallCount; } if (this.isRecommendationsQuery(query)) { @@ -739,17 +768,17 @@ export class ExtensionsListView extends ViewPane { private sortExtensions(extensions: IExtension[], options: IQueryOptions): IExtension[] { switch (options.sortBy) { - case SortBy.InstallCount: + case GallerySortBy.InstallCount: extensions = extensions.sort((e1, e2) => typeof e2.installCount === 'number' && typeof e1.installCount === 'number' ? e2.installCount - e1.installCount : NaN); break; - case SortBy.UpdateDate: + case LocalSortBy.UpdateDate: extensions = extensions.sort((e1, e2) => typeof e2.local?.installedTimestamp === 'number' && typeof e1.local?.installedTimestamp === 'number' ? e2.local.installedTimestamp - e1.local.installedTimestamp : typeof e2.local?.installedTimestamp === 'number' ? 1 : typeof e1.local?.installedTimestamp === 'number' ? -1 : NaN); break; - case SortBy.AverageRating: - case SortBy.WeightedRating: + case GallerySortBy.AverageRating: + case GallerySortBy.WeightedRating: extensions = extensions.sort((e1, e2) => typeof e2.rating === 'number' && typeof e1.rating === 'number' ? e2.rating - e1.rating : NaN); break; default: @@ -1040,6 +1069,7 @@ export class ExtensionsListView extends ViewPane { || this.isBuiltInGroupExtensionsQuery(query) || this.isSearchDeprecatedExtensionsQuery(query) || this.isSearchWorkspaceUnsupportedExtensionsQuery(query) + || this.isSearchRecentlyUpdatedQuery(query) || this.isSortInstalledExtensionsQuery(query, sortBy); } @@ -1107,6 +1137,18 @@ export class ExtensionsListView extends ViewPane { return (sortBy !== undefined && sortBy !== '' && query === '') || (!sortBy && /^@sort:\S*$/i.test(query)); } + static isSearchPopularQuery(query: string): boolean { + return /@popular/i.test(query); + } + + static isSearchRecentlyPublishedQuery(query: string): boolean { + return /@recentlyPublished/i.test(query); + } + + static isSearchRecentlyUpdatedQuery(query: string): boolean { + return /@recentlyUpdated/i.test(query); + } + static isSortUpdateDateQuery(query: string): boolean { return /@sort:updateDate/i.test(query); } @@ -1285,8 +1327,7 @@ export class RecommendedExtensionsView extends ExtensionsListView { } override async show(query: string): Promise<IPagedModel<IExtension>> { - return (query && query.trim() !== this.recommendedExtensionsQuery && !ExtensionsListView.isSortInstalledExtensionsQuery(query)) ? - this.showEmptyModel() : super.show(query.trim()); + return (query && query.trim() !== this.recommendedExtensionsQuery) ? this.showEmptyModel() : super.show(this.recommendedExtensionsQuery); } } diff --git a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts index 749025920f9..d921466976b 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts @@ -13,7 +13,7 @@ export class Query { } static suggestions(query: string): string[] { - const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'featured', 'popular', 'recommended', 'workspaceUnsupported', 'deprecated', 'sort', 'category', 'tag', 'ext', 'id'] as const; + const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'featured', 'popular', 'recommended', 'recentlyUpdated', 'recentlyPublished', 'workspaceUnsupported', 'deprecated', 'sort', 'category', 'tag', 'ext', 'id'] as const; const subcommands = { 'sort': ['installs', 'rating', 'name', 'publishedDate', 'updateDate'], 'category': EXTENSION_CATEGORIES.map(c => `"${c.toLowerCase()}"`), diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index ca1a3a5ff3f..32c2becd92f 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -187,8 +187,6 @@ export const INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID = 'workbench.extensions.comm export const LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID = 'workbench.extensions.action.listWorkspaceUnsupportedExtensions'; // Context Keys -export const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true); -export const ExtensionsSortByContext = new RawContextKey<string>('extensionsSortByValue', ''); export const HasOutdatedExtensionsContext = new RawContextKey<boolean>('hasOutdatedExtensions', false); // Context Menu Groups diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 9d665e5b934..263b6070363 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -11,7 +11,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, - DidUninstallExtensionEvent, InstallExtensionEvent, SortBy, InstallExtensionResult, getTargetPlatform, IExtensionInfo, UninstallExtensionEvent + DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, getTargetPlatform, IExtensionInfo, UninstallExtensionEvent, GallerySortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; @@ -217,7 +217,7 @@ suite('ExtensionsListView Tests', () => { return testableView.show('').then(() => { assert.ok(target.calledOnce); const options: IQueryOptions = target.args[0][0]; - assert.strictEqual(options.sortBy, SortBy.InstallCount); + assert.strictEqual(options.sortBy, GallerySortBy.InstallCount); }); }); @@ -235,7 +235,7 @@ suite('ExtensionsListView Tests', () => { return testableView.show('some extension @sort:rating').then(() => { assert.ok(target.calledOnce); const options: IQueryOptions = target.args[0][0]; - assert.strictEqual(options.sortBy, SortBy.WeightedRating); + assert.strictEqual(options.sortBy, GallerySortBy.WeightedRating); }); }); |