mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 01:25:37 -05:00
Introducing Visualizer to SQL Query Editor (#6422)
* extension recommendation on application launch * Introducing Visualizer (SandDance) to the SQL Query Editor. (#6347) * Created Visualizer icon in the results grid. Utilized a context key so that the icon only shows if Visualizer extensions (currently, just SandDance) is installed. Visualizer icon open up SandDance in a top-level document. * When the user clicks on Charts, visualizer recommendation popup appears. User can click on "Install Extensions" to download the visualizer extensions. * Enabled SQL Query Editor to pass query data to SandDance extension. * Introducing Visualizer (SandDance) to the SQL Query Editor. (#6347) * Created Visualizer icon in the results grid. Utilized a context key so that the icon only shows if Visualizer extensions (currently, just SandDance) is installed. Visualizer icon open up SandDance in a top-level document. * When the user clicks on Charts, visualizer recommendation popup appears. User can click on "Install Extensions" to download the visualizer extensions. * Enabled SQL Query Editor to pass query data to SandDance extension. * Cleaned code; made changes according to PR comments * removed the test service for extensions gallary * Cleaned up code according to PR changes * unid changes to build/azure-piplines * Removed all the build/azure-pipelines changes * removed changes on media/language.svg * refactored extension recommendation system to allow it to be generic * updated extensionsViews to support generic extension query search; added localized constants for visualizer extensions * Made syntax and error message changes acccording to PR comments. * Updated recommendation message according to scenario type
This commit is contained in:
committed by
Rachel Kim
parent
720a7fbfa2
commit
2c8a22bb0d
@@ -71,6 +71,16 @@ export class SimpleExtensionTipsService implements IExtensionTipsService {
|
||||
getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } {
|
||||
return { global: [], workspace: [] };
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
promptRecommendedExtensionsByScenario(scenarioType: string): void {
|
||||
return;
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionTipsService, SimpleExtensionTipsService, true);
|
||||
|
||||
@@ -47,6 +47,9 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async
|
||||
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import product from 'vs/platform/product/node/product';
|
||||
|
||||
|
||||
class ExtensionsViewState extends Disposable implements IExtensionsViewState {
|
||||
|
||||
@@ -424,6 +427,19 @@ export class ExtensionsListView extends ViewletPanel {
|
||||
options.sortBy = SortBy.InstallCount;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let promiseRecommendedExtensionsByScenario;
|
||||
Object.keys(product.recommendedExtensionsByScenario).forEach(scenarioType => {
|
||||
let re = new RegExp('@' + scenarioType, 'i');
|
||||
if (re.test(query.value)) {
|
||||
promiseRecommendedExtensionsByScenario = this.getRecommendedExtensionsByScenario(token, scenarioType);
|
||||
}
|
||||
});
|
||||
if (promiseRecommendedExtensionsByScenario) {
|
||||
return promiseRecommendedExtensionsByScenario;
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
if (ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value)) {
|
||||
return this.getWorkspaceRecommendationsModel(query, options, token);
|
||||
} else if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) {
|
||||
@@ -650,6 +666,31 @@ export class ExtensionsListView extends ViewletPanel {
|
||||
});
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
private getRecommendedExtensionsByScenario(token: CancellationToken, scenarioType: string): Promise<IPagedModel<IExtension>> {
|
||||
if (!scenarioType) {
|
||||
return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.')));
|
||||
}
|
||||
return this.extensionsWorkbenchService.queryLocal()
|
||||
.then(result => result.filter(e => e.type === ExtensionType.User))
|
||||
.then(local => {
|
||||
return this.tipsService.getRecommendedExtensionsByScenario(scenarioType).then((recommmended) => {
|
||||
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
|
||||
return this.extensionsWorkbenchService.queryGallery(token).then((pager) => {
|
||||
// filter out installed extensions and the extensions not in the recommended list
|
||||
pager.firstPage = pager.firstPage.filter((p) => {
|
||||
const extensionId = `${p.publisher}.${p.name}`;
|
||||
return installedExtensions.indexOf(extensionId) === -1 && recommmended.findIndex(ext => ext.extensionId === extensionId) !== -1;
|
||||
});
|
||||
pager.total = pager.firstPage.length;
|
||||
pager.pageSize = pager.firstPage.length;
|
||||
return this.getPagedModel(pager);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
// Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions
|
||||
private getTrimmedRecommendations(installedExtensions: IExtension[], value: string, fileBasedRecommendations: IExtensionRecommendation[], otherRecommendations: IExtensionRecommendation[], workpsaceRecommendations: IExtensionRecommendation[]): string[] {
|
||||
const totalCount = 8;
|
||||
|
||||
@@ -16,7 +16,11 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction, InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { ShowRecommendedExtensionsByScenarioAction, InstallRecommendedExtensionsByScenarioAction } from 'sql/workbench/contrib/extensions/extensionsActions';
|
||||
import * as Constants from 'sql/workbench/contrib/extensions/constants';
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
@@ -1148,4 +1152,79 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
private isExtensionAllowedToBeRecommended(id: string): boolean {
|
||||
return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
promptRecommendedExtensionsByScenario(scenarioType: string): void {
|
||||
const storageKey = 'extensionAssistant/RecommendationsIgnore/' + scenarioType;
|
||||
|
||||
if (this.storageService.getBoolean(storageKey, StorageScope.GLOBAL, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let recommendations: IExtensionRecommendation[];
|
||||
let localExtensions: ILocalExtension[];
|
||||
const getRecommendationPromise = this.getRecommendedExtensionsByScenario(scenarioType).then(recs => { recommendations = recs; });
|
||||
const getLocalExtensionPromise = this.extensionsService.getInstalled(ExtensionType.User).then(local => { localExtensions = local; });
|
||||
|
||||
let recommendationMessage = localize('ExtensionsRecommended', "Azure Data Studio has extension recommendations.");
|
||||
if (scenarioType === Constants.visualizerExtensions) {
|
||||
recommendationMessage = localize('VisualizerExtensionsRecommended', "Azure Data Studio has extension recommendations for data visualization.\nOnce installed, you can select the Visualizer icon to visualize your query results.");
|
||||
}
|
||||
Promise.all([getRecommendationPromise, getLocalExtensionPromise]).then(() => {
|
||||
if (!recommendations.every(rec => { return localExtensions.findIndex(local => local.identifier.id.toLocaleLowerCase() === rec.extensionId.toLocaleLowerCase()) !== -1; })) {
|
||||
return new Promise<void>(c => {
|
||||
this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
recommendationMessage,
|
||||
[{
|
||||
label: localize('installAll', "Install All"),
|
||||
run: () => {
|
||||
this.telemetryService.publicLog(scenarioType + 'Recommendations:popup', { userReaction: 'install' });
|
||||
const installAllAction = this.instantiationService.createInstance(InstallRecommendedExtensionsByScenarioAction, scenarioType, recommendations);
|
||||
installAllAction.run();
|
||||
installAllAction.dispose();
|
||||
}
|
||||
}, {
|
||||
label: localize('showRecommendations', "Show Recommendations"),
|
||||
run: () => {
|
||||
this.telemetryService.publicLog(scenarioType + 'Recommendations:popup', { userReaction: 'show' });
|
||||
const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsByScenarioAction, scenarioType);
|
||||
showAction.run();
|
||||
showAction.dispose();
|
||||
c(undefined);
|
||||
}
|
||||
}, {
|
||||
label: choiceNever,
|
||||
isSecondary: true,
|
||||
run: () => {
|
||||
this.telemetryService.publicLog(scenarioType + 'Recommendations:popup', { userReaction: 'neverShowAgain' });
|
||||
this.storageService.store(storageKey, true, StorageScope.GLOBAL);
|
||||
c(undefined);
|
||||
}
|
||||
}],
|
||||
{
|
||||
sticky: true,
|
||||
onCancel: () => {
|
||||
this.telemetryService.publicLog(scenarioType + 'Recommendations:popup', { userReaction: 'cancelled' });
|
||||
c(undefined);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
|
||||
if (!scenarioType) {
|
||||
return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.')));
|
||||
}
|
||||
|
||||
return Promise.resolve((product.recommendedExtensionsByScenario[scenarioType] || [])
|
||||
.filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId))
|
||||
.map(extensionId => (<IExtensionRecommendation>{ extensionId, sources: ['application'] })));
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
}
|
||||
|
||||
@@ -106,6 +106,10 @@ export interface IExtensionTipsService {
|
||||
toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void;
|
||||
getAllIgnoredRecommendations(): { global: string[], workspace: string[] };
|
||||
onRecommendationChange: Event<RecommendationChangeNotification>;
|
||||
// {{SQL CARBON EDIT}}
|
||||
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]>;
|
||||
promptRecommendedExtensionsByScenario(scenarioType: string): void;
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
}
|
||||
|
||||
export const enum ExtensionRecommendationReason {
|
||||
|
||||
Reference in New Issue
Block a user