mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-25 06:10:30 -04:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -7,14 +7,12 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { chain } from 'vs/base/common/event';
|
||||
import { isPromiseCanceledError, create as createError } from 'vs/base/common/errors';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { PagedModel, IPagedModel, mergePagers, IPager } from 'vs/base/common/paging';
|
||||
import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
@@ -32,7 +30,7 @@ import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachListStyler, attachBadgeStyler } from 'vs/platform/theme/common/styler';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
@@ -40,23 +38,24 @@ import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { EventType } from 'vs/base/common/events';
|
||||
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
|
||||
|
||||
export class ExtensionsListView extends CollapsibleView {
|
||||
export class ExtensionsListView extends ViewsViewletPanel {
|
||||
|
||||
private messageBox: HTMLElement;
|
||||
private extensionsList: HTMLElement;
|
||||
private badge: CountBadge;
|
||||
|
||||
protected badgeContainer: HTMLElement;
|
||||
private list: PagedList<IExtension>;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IMessageService protected messageService: IMessageService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@@ -70,13 +69,15 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IProgressService private progressService: IProgressService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: options.name, sizing: ViewSizing.Flexible, collapsed: !!options.collapsed, initialBodySize: 1 * 62 }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService);
|
||||
}
|
||||
|
||||
renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = append(container, $('div.title'));
|
||||
append(titleDiv, $('span')).textContent = this.options.name;
|
||||
this.badge = new CountBadge(append(container, $('.count-badge-wrapper')));
|
||||
|
||||
this.badgeContainer = append(container, $('.count-badge-wrapper'));
|
||||
this.badge = new CountBadge(this.badgeContainer);
|
||||
this.disposables.push(attachBadgeStyler(this.badge, this.themeService));
|
||||
}
|
||||
|
||||
@@ -104,14 +105,6 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
.on(this.pin, this, this.disposables);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): TPromise<void> {
|
||||
return super.setVisible(visible).then(() => {
|
||||
if (!visible) {
|
||||
this.setModel(new PagedModel([]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
layoutBody(size: number): void {
|
||||
this.extensionsList.style.height = size + 'px';
|
||||
this.list.layout(size);
|
||||
@@ -193,6 +186,15 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
return new PagedModel(result);
|
||||
}
|
||||
|
||||
const idMatch = /@id:([a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*)/.exec(value);
|
||||
|
||||
if (idMatch) {
|
||||
const name = idMatch[1];
|
||||
|
||||
return this.extensionsWorkbenchService.queryGallery({ names: [name], source: 'queryById' })
|
||||
.then(pager => new PagedModel(pager));
|
||||
}
|
||||
|
||||
if (/@outdated/i.test(value)) {
|
||||
value = value.replace(/@outdated/g, '').trim().toLowerCase();
|
||||
|
||||
@@ -236,7 +238,7 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
return this.getWorkspaceRecommendationsModel(query, options);
|
||||
} else if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) {
|
||||
return this.getKeymapRecommendationsModel(query, options);
|
||||
} else if (/@recommended:all/i.test(query.value)) {
|
||||
} else if (/@recommended:all/i.test(query.value) || ExtensionsListView.isSearchRecommendedExtensionsQuery(query.value)) {
|
||||
return this.getAllRecommendationsModel(query, options);
|
||||
} else if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) {
|
||||
return this.getRecommendationsModel(query, options);
|
||||
@@ -265,13 +267,15 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
});
|
||||
|
||||
if (names.length) {
|
||||
const namesOptions = assign({}, options, { names });
|
||||
const namesOptions = assign({}, options, { names, source: 'extRegex' });
|
||||
pagerPromises.push(this.extensionsWorkbenchService.queryGallery(namesOptions));
|
||||
}
|
||||
}
|
||||
|
||||
if (text) {
|
||||
options = assign(options, { text: text.substr(0, 350) });
|
||||
options = assign(options, { text: text.substr(0, 350), source: 'searchText' });
|
||||
} else {
|
||||
options.source = 'viewlet';
|
||||
}
|
||||
|
||||
pagerPromises.push(this.extensionsWorkbenchService.queryGallery(options));
|
||||
@@ -283,23 +287,34 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
}
|
||||
|
||||
private getAllRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
|
||||
const value = query.value.replace(/@recommended:all/g, '').trim().toLowerCase();
|
||||
const value = query.value.replace(/@recommended:all/g, '').replace(/@recommended/g, '').trim().toLowerCase();
|
||||
|
||||
return this.extensionsWorkbenchService.queryLocal()
|
||||
.then(result => result.filter(e => e.type === LocalExtensionType.User))
|
||||
.then(local => {
|
||||
return TPromise.join([TPromise.as(this.tipsService.getRecommendations()), this.tipsService.getWorkspaceRecommendations()])
|
||||
.then(([recommendations, workspaceRecommendations]) => {
|
||||
const names = distinct([...recommendations, ...workspaceRecommendations])
|
||||
.filter(name => local.every(ext => `${ext.publisher}.${ext.name}` !== name))
|
||||
.filter(name => name.toLowerCase().indexOf(value) > -1);
|
||||
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
|
||||
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
|
||||
let others = this.tipsService.getOtherRecommendations();
|
||||
|
||||
return this.tipsService.getWorkspaceRecommendations()
|
||||
.then(workspaceRecommendations => {
|
||||
const names = this.getTrimmedRecommendations(installedExtensions, value, fileBasedRecommendations, others, workspaceRecommendations);
|
||||
|
||||
/* __GDPR__
|
||||
"extensionAllRecommendations:open" : {
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionAllRecommendations:open', { count: names.length });
|
||||
if (!names.length) {
|
||||
return TPromise.as(new PagedModel([]));
|
||||
}
|
||||
options.source = 'recommendations-all';
|
||||
return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }))
|
||||
.then(pager => new PagedModel(pager || []));
|
||||
.then(pager => {
|
||||
this.sortFirstPage(pager, names);
|
||||
return new PagedModel(pager || []);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -310,26 +325,77 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
return this.extensionsWorkbenchService.queryLocal()
|
||||
.then(result => result.filter(e => e.type === LocalExtensionType.User))
|
||||
.then(local => {
|
||||
const names = this.tipsService.getRecommendations()
|
||||
.filter(name => local.every(ext => `${ext.publisher}.${ext.name}` !== name))
|
||||
.filter(name => name.toLowerCase().indexOf(value) > -1);
|
||||
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
|
||||
let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations();
|
||||
let others = this.tipsService.getOtherRecommendations();
|
||||
|
||||
this.telemetryService.publicLog('extensionRecommendations:open', { count: names.length });
|
||||
return this.tipsService.getWorkspaceRecommendations()
|
||||
.then(workspaceRecommendations => {
|
||||
workspaceRecommendations = workspaceRecommendations.map(x => x.toLowerCase());
|
||||
fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.indexOf(x.toLowerCase()) === -1);
|
||||
others = others.filter(x => workspaceRecommendations.indexOf(x.toLowerCase()) === -1);
|
||||
|
||||
if (!names.length) {
|
||||
return TPromise.as(new PagedModel([]));
|
||||
}
|
||||
options.source = 'recommendations';
|
||||
return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }))
|
||||
.then(pager => new PagedModel(pager || []));
|
||||
const names = this.getTrimmedRecommendations(installedExtensions, value, fileBasedRecommendations, others, []);
|
||||
|
||||
/* __GDPR__
|
||||
"extensionRecommendations:open" : {
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionRecommendations:open', { count: names.length });
|
||||
|
||||
if (!names.length) {
|
||||
return TPromise.as(new PagedModel([]));
|
||||
}
|
||||
options.source = 'recommendations';
|
||||
return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length }))
|
||||
.then(pager => {
|
||||
this.sortFirstPage(pager, names);
|
||||
return new PagedModel(pager || []);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions
|
||||
private getTrimmedRecommendations(installedExtensions: string[], value: string, fileBasedRecommendations: string[], otherRecommendations: string[], workpsaceRecommendations: string[], ) {
|
||||
const totalCount = 8;
|
||||
workpsaceRecommendations = workpsaceRecommendations
|
||||
.filter(name => {
|
||||
return installedExtensions.indexOf(name) === -1
|
||||
&& name.toLowerCase().indexOf(value) > -1;
|
||||
});
|
||||
fileBasedRecommendations = fileBasedRecommendations.filter(x => {
|
||||
return installedExtensions.indexOf(x) === -1
|
||||
&& workpsaceRecommendations.indexOf(x) === -1
|
||||
&& x.toLowerCase().indexOf(value) > -1;
|
||||
});
|
||||
otherRecommendations = otherRecommendations.filter(x => {
|
||||
return installedExtensions.indexOf(x) === -1
|
||||
&& fileBasedRecommendations.indexOf(x) === -1
|
||||
&& workpsaceRecommendations.indexOf(x) === -1
|
||||
&& x.toLowerCase().indexOf(value) > -1;
|
||||
});
|
||||
|
||||
let otherCount = Math.min(2, otherRecommendations.length);
|
||||
let fileBasedCount = Math.min(fileBasedRecommendations.length, totalCount - workpsaceRecommendations.length - otherCount);
|
||||
let names = workpsaceRecommendations;
|
||||
names.push(...fileBasedRecommendations.splice(0, fileBasedCount));
|
||||
names.push(...otherRecommendations.splice(0, otherCount));
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
|
||||
const value = query.value.replace(/@recommended:workspace/g, '').trim().toLowerCase();
|
||||
return this.tipsService.getWorkspaceRecommendations()
|
||||
.then(recommendations => {
|
||||
const names = recommendations.filter(name => name.toLowerCase().indexOf(value) > -1);
|
||||
/* __GDPR__
|
||||
"extensionWorkspaceRecommendations:open" : {
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionWorkspaceRecommendations:open', { count: names.length });
|
||||
|
||||
if (!names.length) {
|
||||
@@ -345,6 +411,11 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
const value = query.value.replace(/@recommended:keymaps/g, '').trim().toLowerCase();
|
||||
const names = this.tipsService.getKeymapRecommendations()
|
||||
.filter(name => name.toLowerCase().indexOf(value) > -1);
|
||||
/* __GDPR__
|
||||
"extensionKeymapRecommendations:open" : {
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('extensionKeymapRecommendations:open', { count: names.length });
|
||||
|
||||
if (!names.length) {
|
||||
@@ -355,6 +426,14 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
.then(result => new PagedModel(result));
|
||||
}
|
||||
|
||||
// Sorts the firsPage of the pager in the same order as given array of extension ids
|
||||
private sortFirstPage(pager: IPager<IExtension>, ids: string[]) {
|
||||
ids = ids.map(x => x.toLowerCase());
|
||||
pager.firstPage.sort((a, b) => {
|
||||
return ids.indexOf(a.id.toLowerCase()) < ids.indexOf(b.id.toLowerCase()) ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
private setModel(model: IPagedModel<IExtension>) {
|
||||
this.list.model = model;
|
||||
this.list.scrollTop = 0;
|
||||
@@ -427,7 +506,11 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
}
|
||||
|
||||
static isRecommendedExtensionsQuery(query: string): boolean {
|
||||
return /@recommended/i.test(query);
|
||||
return /^@recommended$/i.test(query.trim());
|
||||
}
|
||||
|
||||
static isSearchRecommendedExtensionsQuery(query: string): boolean {
|
||||
return /@recommended/i.test(query) && !ExtensionsListView.isRecommendedExtensionsQuery(query);
|
||||
}
|
||||
|
||||
static isWorkspaceRecommendedExtensionsQuery(query: string): boolean {
|
||||
@@ -456,23 +539,43 @@ export class InstalledExtensionsView extends ExtensionsListView {
|
||||
searchInstalledQuery = query ? searchInstalledQuery + ' ' + query : searchInstalledQuery;
|
||||
return super.show(searchInstalledQuery);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class RecommendedExtensionsView extends ExtensionsListView {
|
||||
|
||||
public static isRecommendedExtensionsQuery(query: string): boolean {
|
||||
return ExtensionsListView.isRecommendedExtensionsQuery(query)
|
||||
|| ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query);
|
||||
async show(query: string): TPromise<IPagedModel<IExtension>> {
|
||||
return super.show(!query.trim() ? '@recommended:all' : '@recommended');
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
|
||||
|
||||
renderHeader(container: HTMLElement): void {
|
||||
super.renderHeader(container);
|
||||
|
||||
const listActionBar = $('.list-actionbar-container');
|
||||
container.insertBefore(listActionBar, this.badgeContainer);
|
||||
|
||||
const actionbar = new ActionBar(listActionBar, {
|
||||
animated: false
|
||||
});
|
||||
actionbar.addListener(EventType.RUN, ({ error }) => error && this.messageService.show(Severity.Error, error));
|
||||
const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL);
|
||||
const configureWorkspaceFolderAction = this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL);
|
||||
|
||||
installAllAction.class = 'octicon octicon-cloud-download';
|
||||
configureWorkspaceFolderAction.class = 'octicon octicon-pencil';
|
||||
|
||||
actionbar.push([installAllAction], { icon: true, label: false });
|
||||
actionbar.push([configureWorkspaceFolderAction], { icon: true, label: false });
|
||||
|
||||
this.disposables.push(actionbar);
|
||||
}
|
||||
|
||||
async show(query: string): TPromise<IPagedModel<IExtension>> {
|
||||
if (RecommendedExtensionsView.isRecommendedExtensionsQuery(query)) {
|
||||
return super.show(query);
|
||||
}
|
||||
let searchInstalledQuery = '@recommended:all';
|
||||
searchInstalledQuery = query ? searchInstalledQuery + ' ' + query : searchInstalledQuery;
|
||||
return super.show(searchInstalledQuery);
|
||||
let model = await super.show('@recommended:workspace');
|
||||
this.setExpanded(model.length > 0);
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user