mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Handle delayed Notebook provider registration (#3526)
* Handle delayed Notebook provider registration - Fixes #3197 Notebooks: builtin provider always used on reopen with notebook file visible - Fixes #3414 Can't refresh kernel after connect to big data cluster There are 3 parts to this fix: - If no notebook provider other than the default is installed, we warn users and prompt to install the SQL2019 extension - We wait on the extension host registration to complete before determining which provider to use - We know that the extension registration of the provider instance will be after package.json is read, so if we wait after registration for 10 seconds to give this a chance to happen before returning a provider to the front end * Remove launch.json change that was added accidentally * Fix timeout not being the expected value * Removed console log left in during debugging * Remove unnecessary whitespace * Fix unit test failure * Name the registration better, and remove outdated comments
This commit is contained in:
@@ -5,9 +5,8 @@
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { EditorInput, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
|
||||
import URI from 'vs/base/common/uri';
|
||||
@@ -17,8 +16,7 @@ import { QueryInput } from 'sql/parts/query/common/queryInput';
|
||||
import { IQueryEditorOptions } from 'sql/parts/query/common/queryEditorService';
|
||||
import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput';
|
||||
import { NotebookInput, NotebookInputModel, NotebookInputValidator } from 'sql/parts/notebook/notebookInput';
|
||||
import { Extensions, INotebookProviderRegistry } from 'sql/services/notebook/notebookRegistry';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER } from 'sql/services/notebook/notebookService';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, INotebookService } from 'sql/services/notebook/notebookService';
|
||||
import { getProviderForFileName } from 'sql/parts/notebook/notebookUtils';
|
||||
|
||||
const fs = require('fs');
|
||||
@@ -59,20 +57,20 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
|
||||
|
||||
//Notebook
|
||||
let notebookValidator = instantiationService.createInstance(NotebookInputValidator);
|
||||
uri = getNotebookEditorUri(input);
|
||||
uri = getNotebookEditorUri(input, instantiationService);
|
||||
if(uri && notebookValidator.isNotebookEnabled()){
|
||||
//TODO: We need to pass in notebook data either through notebook input or notebook service
|
||||
let fileName: string = 'untitled';
|
||||
let providerId: string = DEFAULT_NOTEBOOK_PROVIDER;
|
||||
if (input) {
|
||||
fileName = input.getName();
|
||||
providerId = getProviderForFileName(fileName);
|
||||
}
|
||||
let notebookInputModel = new NotebookInputModel(uri, undefined, false, undefined);
|
||||
notebookInputModel.providerId = providerId;
|
||||
//TO DO: Second parameter has to be the content.
|
||||
let notebookInput: NotebookInput = instantiationService.createInstance(NotebookInput, fileName, notebookInputModel);
|
||||
return notebookInput;
|
||||
return withService<INotebookService, NotebookInput>(instantiationService, INotebookService, notebookService => {
|
||||
let fileName: string = 'untitled';
|
||||
let providerId: string = DEFAULT_NOTEBOOK_PROVIDER;
|
||||
if (input) {
|
||||
fileName = input.getName();
|
||||
providerId = getProviderForFileName(fileName, notebookService);
|
||||
}
|
||||
let notebookInputModel = new NotebookInputModel(uri, undefined, false, undefined);
|
||||
notebookInputModel.providerId = providerId;
|
||||
let notebookInput: NotebookInput = instantiationService.createInstance(NotebookInput, fileName, notebookInputModel);
|
||||
return notebookInput;
|
||||
});
|
||||
}
|
||||
}
|
||||
return input;
|
||||
@@ -159,7 +157,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI {
|
||||
* If input is a supported notebook editor file (.ipynb), return it's URI. Otherwise return undefined.
|
||||
* @param input The EditorInput to get the URI of.
|
||||
*/
|
||||
function getNotebookEditorUri(input: EditorInput): URI {
|
||||
function getNotebookEditorUri(input: EditorInput, instantiationService: IInstantiationService): URI {
|
||||
if (!input || !input.getName()) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -170,7 +168,7 @@ function getNotebookEditorUri(input: EditorInput): URI {
|
||||
if (!(input instanceof NotebookInput)) {
|
||||
let uri: URI = getSupportedInputResource(input);
|
||||
if (uri) {
|
||||
if (hasFileExtension(getNotebookFileExtensions(), input, false)) {
|
||||
if (hasFileExtension(getNotebookFileExtensions(instantiationService), input, false)) {
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
@@ -179,9 +177,17 @@ function getNotebookEditorUri(input: EditorInput): URI {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getNotebookFileExtensions() {
|
||||
let notebookRegistry = Registry.as<INotebookProviderRegistry>(Extensions.NotebookProviderContribution);
|
||||
return notebookRegistry.getSupportedFileExtensions();
|
||||
function getNotebookFileExtensions(instantiationService: IInstantiationService): string[] {
|
||||
return withService<INotebookService, string[]>(instantiationService, INotebookService, notebookService => {
|
||||
return notebookService.getSupportedFileExtensions();
|
||||
});
|
||||
}
|
||||
|
||||
function withService<TService, TResult>(instantiationService: IInstantiationService, serviceId: ServiceIdentifier<TService>, action: (service: TService) => TResult, ): TResult {
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
let service = accessor.get(serviceId);
|
||||
return action(service);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,41 +9,42 @@ import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, V
|
||||
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { INotificationService, INotification } from 'vs/platform/notification/common/notification';
|
||||
import { INotificationService, INotification, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
|
||||
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
|
||||
import { ICellModel, IModelFactory, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_FILETYPE } from 'sql/services/notebook/notebookService';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { NotebookModel, NotebookContentChange } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
|
||||
import * as notebookUtils from './notebookUtils';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction, SaveNotebookAction } from 'sql/parts/notebook/notebookActions';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IAction, Action, IActionItem } from 'vs/base/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { fillInActions, LabeledMenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts';
|
||||
import { ICellModel, IModelFactory, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_FILETYPE, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/services/notebook/notebookService';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { NotebookModel, NotebookContentChange } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
|
||||
import * as notebookUtils from 'sql/parts/notebook/notebookUtils';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction, SaveNotebookAction } from 'sql/parts/notebook/notebookActions';
|
||||
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||
@@ -88,6 +89,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
@Inject(IKeybindingService) private keybindingService: IKeybindingService,
|
||||
@Inject(IHistoryService) private historyService: IHistoryService,
|
||||
@Inject(IWindowService) private windowService: IWindowService,
|
||||
@Inject(IViewletService) private viewletService: IViewletService
|
||||
) {
|
||||
super();
|
||||
this.updateProfile();
|
||||
@@ -228,6 +230,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
}
|
||||
|
||||
private async loadModel(): Promise<void> {
|
||||
await this.awaitNonDefaultProvider();
|
||||
this.notebookManager = await this.notebookService.getOrCreateNotebookManager(this._notebookParams.providerId, this._notebookParams.notebookUri);
|
||||
let model = new NotebookModel({
|
||||
factory: this.modelFactory,
|
||||
@@ -247,6 +250,34 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
private async awaitNonDefaultProvider(): Promise<void> {
|
||||
// Wait on registration for now. Long-term would be good to cache and refresh
|
||||
await this.notebookService.registrationComplete;
|
||||
// Refresh the provider if we had been using default
|
||||
if (DEFAULT_NOTEBOOK_PROVIDER === this._notebookParams.providerId) {
|
||||
this._notebookParams.providerId = notebookUtils.getProviderForFileName(this._notebookParams.notebookUri.fsPath, this.notebookService);
|
||||
}
|
||||
if (DEFAULT_NOTEBOOK_PROVIDER === this._notebookParams.providerId) {
|
||||
// If it's still the default, warn them they should install an extension
|
||||
this.notificationService.prompt(Severity.Warning,
|
||||
localize('noKernelInstalled', 'Please install the SQL Server 2019 extension to run cells'),
|
||||
[{
|
||||
label: localize('installSql2019Extension', 'Install Extension'),
|
||||
run: () => this.openExtensionGallery()
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
private async openExtensionGallery(): Promise<void> {
|
||||
try {
|
||||
let viewlet = await this.viewletService.openViewlet(VIEWLET_ID, true) as IExtensionsViewlet;
|
||||
viewlet.search('sql-vnext');
|
||||
viewlet.focus();
|
||||
} catch (error) {
|
||||
this.notificationService.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Updates toolbar components
|
||||
private updateToolbarComponents(isTrusted: boolean)
|
||||
{
|
||||
|
||||
@@ -11,9 +11,7 @@ import * as os from 'os';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IOutputChannel } from 'vs/workbench/parts/output/common/output';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { INotebookProviderRegistry, Extensions } from 'sql/services/notebook/notebookRegistry';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE } from 'sql/services/notebook/notebookService';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE, INotebookService } from 'sql/services/notebook/notebookService';
|
||||
|
||||
|
||||
/**
|
||||
@@ -41,18 +39,17 @@ export async function mkDir(dirPath: string, outputChannel?: IOutputChannel): Pr
|
||||
}
|
||||
}
|
||||
|
||||
export function getProviderForFileName(fileName: string): string {
|
||||
export function getProviderForFileName(fileName: string, notebookService: INotebookService): string {
|
||||
let fileExt = path.extname(fileName);
|
||||
let provider: string;
|
||||
let notebookRegistry = Registry.as<INotebookProviderRegistry>(Extensions.NotebookProviderContribution);
|
||||
// First try to get provider for actual file type
|
||||
if (fileExt && fileExt.startsWith('.')) {
|
||||
fileExt = fileExt.slice(1,fileExt.length);
|
||||
provider = notebookRegistry.getProviderForFileType(fileExt);
|
||||
provider = notebookService.getProviderForFileType(fileExt);
|
||||
}
|
||||
// Fallback to provider for default file type (assume this is a global handler)
|
||||
if (!provider) {
|
||||
provider = notebookRegistry.getProviderForFileType(DEFAULT_NOTEBOOK_FILETYPE);
|
||||
provider = notebookService.getProviderForFileType(DEFAULT_NOTEBOOK_FILETYPE);
|
||||
}
|
||||
// Finally if all else fails, use the built-in handler
|
||||
if (!provider) {
|
||||
|
||||
Reference in New Issue
Block a user