diff --git a/src/sql/workbench/common/constants.ts b/src/sql/workbench/common/constants.ts index 8e5e7d49e5..de138ef854 100644 --- a/src/sql/workbench/common/constants.ts +++ b/src/sql/workbench/common/constants.ts @@ -30,7 +30,7 @@ export const InputBoxFocusedKey = new RawContextKey('inputBoxFocus', fa export const SearchInputBoxFocusedKey = new RawContextKey('searchInputBoxFocus', false); export const enum NotebookLanguage { - Notebook = 'notebook', + Notebook = 'Notebook', Ipynb = 'ipynb' } export interface INotebookSearchConfigurationProperties { diff --git a/src/sql/workbench/contrib/editorReplacement/common/editorReplacer.contribution.ts b/src/sql/workbench/contrib/editorReplacement/common/editorReplacer.contribution.ts deleted file mode 100644 index c92995f0e6..0000000000 --- a/src/sql/workbench/contrib/editorReplacement/common/editorReplacer.contribution.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { EditorReplacementContribution } from 'sql/workbench/contrib/editorReplacement/common/editorReplacerContribution'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; - -const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(EditorReplacementContribution, LifecyclePhase.Starting); diff --git a/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts b/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts deleted file mode 100644 index bf24e9f5c2..0000000000 --- a/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts +++ /dev/null @@ -1,110 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorInput } from 'vs/workbench/common/editor'; -import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { Registry } from 'vs/platform/registry/common/platform'; -import * as path from 'vs/base/common/path'; - -import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation'; -import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -import { isThenable } from 'vs/base/common/async'; -import { withNullAsUndefined } from 'vs/base/common/types'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { mixin } from 'vs/base/common/objects'; - -const languageAssociationRegistry = Registry.as(LanguageAssociationExtensions.LanguageAssociations); - -export class EditorReplacementContribution implements IWorkbenchContribution { - private editorOpeningListener: IDisposable; - - constructor( - @IEditorService private readonly editorService: IEditorService, - @IModeService private readonly modeService: IModeService - ) { - this.editorOpeningListener = this.editorService.overrideOpenEditor({ - open: (editor, options, group) => this.onEditorOpening(editor, options, group) - }); - } - - private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { - // If the resource was already opened before in the group, do not prevent - // the opening of that resource. Otherwise we would have the same settings - // opened twice (https://github.com/Microsoft/vscode/issues/36447) - // if (group.isOpened(editor)) { - // return undefined; - // } - - if (!(editor instanceof FileEditorInput) && !(editor instanceof UntitledTextEditorInput) && !(editor instanceof DiffEditorInput)) { - return undefined; - } - - let language: string | undefined; - if (editor instanceof FileEditorInput) { - language = editor.getPreferredMode(); - } else if (editor instanceof UntitledTextEditorInput) { - language = editor.getMode(); - } - - if (!language) { // in the case the input doesn't have a preferred mode set we will attempt to guess the mode from the file path - language = withNullAsUndefined(this.modeService.getModeIdByFilepathOrFirstLine(editor.resource)); - } - - if (!language) { - // Attempt to use extension or extension of modified input (if in diff editor) - // remove the . - language = editor instanceof DiffEditorInput ? path.extname(editor.modifiedInput.resource.fsPath).slice(1) : path.extname(editor.resource.toString()).slice(1); - } - - if (!language) { - const defaultInputCreator = languageAssociationRegistry.defaultAssociation; - if (defaultInputCreator && !(editor instanceof DiffEditorInput)) { - editor.setMode(defaultInputCreator[0]); - const newInput = defaultInputCreator[1].convertInput(editor); - if (newInput) { - return { - // If the new input is a thenable which resolves to undefined (no input to convert) - // then don't allow further overriding since otherwise we could get in an infinite loop - // (openEditor calls onEditorOpening handler and we keep repeating this process). This - // is replicating the behavior for the non-Thenable returns where we just let the openEditor - // continue on - override: isThenable(newInput) ? - newInput.then(input => this.editorService.openEditor(input ?? editor, mixin(options, { override: input ? options?.override : false }), group)) : - this.editorService.openEditor(newInput, options, group) - }; - } - } - } else { - const inputCreator = languageAssociationRegistry.getAssociationForLanguage(language); - if (inputCreator) { - const newInput = inputCreator.convertInput(editor); - if (newInput) { - return { - // If the new input is a thenable which resolves to undefined (no input to convert) - // then don't allow further overriding since otherwise we could get in an infinite loop - // (openEditor calls onEditorOpening handler and we keep repeating this process). This - // is replicating the behavior for the non-Thenable returns where we just let the openEditor - // continue on - override: isThenable(newInput) ? - newInput.then(input => this.editorService.openEditor(input ?? editor, mixin(options, { override: input ? options?.override : false }), group)) - : this.editorService.openEditor(newInput, options, group) - }; - } - } - } - - return undefined; - } - - dispose(): void { - dispose(this.editorOpeningListener); - } -} diff --git a/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts b/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts deleted file mode 100644 index 63dce62b02..0000000000 --- a/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts +++ /dev/null @@ -1,324 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { EditorReplacementContribution } from 'sql/workbench/contrib/editorReplacement/common/editorReplacerContribution'; -import { TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; -import { Event } from 'vs/base/common/event'; -import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; -import { URI } from 'vs/base/common/uri'; -import { IOpenEditorOverrideHandler, IOpenEditorOverride, IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; -import { isUndefinedOrNull } from 'vs/base/common/types'; -import { IEditorInput, EditorInput, IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor'; -import { ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation'; -import { QueryEditorLanguageAssociation } from 'sql/workbench/contrib/query/browser/queryInputFactory'; -import { workbenchInstantiationService } from 'sql/workbench/test/workbenchTestServices'; -import { NotebookEditorInputAssociation } from 'sql/workbench/contrib/notebook/browser/models/notebookInputFactory'; -import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput'; -import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -import { UntitledQueryEditorInput } from 'sql/workbench/common/editor/query/untitledQueryEditorInput'; -import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; -import { NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; -import { TestQueryEditorService } from 'sql/workbench/services/queryEditor/test/common/testQueryEditorService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { DiffNotebookInput } from 'sql/workbench/contrib/notebook/browser/models/diffNotebookInput'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; - -const languageAssociations = Registry.as(LanguageAssociationExtensions.LanguageAssociations); - - -suite('Editor Replacer Contribution', () => { - let disposables: IDisposable[] = []; - - setup(() => { - disposables.push(languageAssociations.registerLanguageAssociation(QueryEditorLanguageAssociation.languages, QueryEditorLanguageAssociation, QueryEditorLanguageAssociation.isDefault)); - disposables.push(languageAssociations.registerLanguageAssociation(NotebookEditorInputAssociation.languages, NotebookEditorInputAssociation)); - const instantiationService = workbenchInstantiationService(); - instantiationService.stub(INotebookService, new NotebookServiceStub()); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - instantiationService.stub(IQueryEditorService, instantiationService.createInstance(TestQueryEditorService)); - const configService = new TestConfigurationService(); - configService.setUserConfiguration('notebook', { showRenderedNotebookInDiffEditor: true }); - instantiationService.stub(IConfigurationService, configService); - instantiationService.invokeFunction(accessor => { - languageAssociations.start(accessor); - }); - }); - - teardown(() => { - disposables = dispose(disposables); - }); - - test('does proper lifecycle', () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - const modeService = new TestModeService(); - const contrib = new EditorReplacementContribution(editorService, modeService); - assert.equal(editorService.overridenOpens.length, 1); - contrib.dispose(); - assert.equal(editorService.overridenOpens.length, 0); - }); - - test('does replace sql file input from uri (no mode service)', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.sql'), undefined, undefined, undefined, undefined, undefined); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response?.override); - const newinput = (await response.override) as EditorInput; // our test service returns this so we are fine to cast this - - assert(newinput instanceof QueryEditorInput); - - contrib.dispose(); - }); - - test.skip('does replace sql file input using input mode', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.other'), undefined, undefined, 'sql', undefined, undefined); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response?.override); - const newinput = (await response.override) as EditorInput; // our test service returns this so we are fine to cast this - - assert(newinput instanceof QueryEditorInput); - - contrib.dispose(); - }); - - test('does replace notebook file input using input extension notebook', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.notebook'), undefined, undefined, undefined, undefined, undefined); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response?.override); - const newinput = (await response.override) as EditorInput; // our test service returns this so we are fine to cast this - - assert(newinput instanceof NotebookInput); - - contrib.dispose(); - }); - - test.skip('does replace notebook file input using input extension iynb', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.iynb'), undefined, undefined, 'notebook', undefined, undefined); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response?.override); - const newinput = (await response.override) as EditorInput; // our test service returns this so we are fine to cast this - - assert(newinput instanceof NotebookInput); - - contrib.dispose(); - }); - - test('does replace notebook file diff input using input extension ipynb', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.ipynb'), undefined, undefined, undefined, undefined, undefined); - const input2 = instantiationService.createInstance(FileEditorInput, URI.file('/test/file2.ipynb'), undefined, undefined, undefined, undefined, undefined); - const diffInput = instantiationService.createInstance(DiffEditorInput, undefined, undefined, input, input2, undefined); - const response = editorService.fireOpenEditor(diffInput, undefined, undefined as IEditorGroup); - const newinput = await response.override; // our test service returns this so we are fine to cast this - assert(newinput instanceof DiffNotebookInput); - contrib.dispose(); - }); - - test('does not replace sql file diff input using input extension sql', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.sql'), undefined, undefined, undefined, undefined, undefined); - const input2 = instantiationService.createInstance(FileEditorInput, URI.file('/test/file2.sql'), undefined, undefined, undefined, undefined, undefined); - const diffInput = instantiationService.createInstance(DiffEditorInput, undefined, undefined, input, input2, undefined); - const response = editorService.fireOpenEditor(diffInput, undefined, undefined as IEditorGroup); - const newinput = await response.override; - assert(newinput instanceof DiffEditorInput); - contrib.dispose(); - }); - - test('does replace file input using default mode', async function () { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const accessor = instantiationService.createInstance(ServiceAccessor); - const service = accessor.untitledTextEditorService; - - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response?.override); - const newinput = (await response.override) as EditorInput; // our test service returns this so we are fine to cast this - - assert(newinput instanceof QueryEditorInput); - - contrib.dispose(); - }); - - test('does not replace editors that it shouldnt', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const accessor = instantiationService.createInstance(ServiceAccessor); - const service = accessor.untitledTextEditorService; - const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create()); - const input = instantiationService.createInstance(UntitledQueryEditorInput, '', untitled, undefined); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response === undefined); - - contrib.dispose(); - }); - - test('does not replace editors if it doesnt have a replacer', async () => { - const instantiationService = workbenchInstantiationService(); - const editorService = new MockEditorService(instantiationService); - instantiationService.stub(IEditorService, editorService); - const contrib = instantiationService.createInstance(EditorReplacementContribution); - const accessor = instantiationService.createInstance(ServiceAccessor); - const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: URI.file('/test/file.unknown') })); - const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup); - assert(response === undefined); - - contrib.dispose(); - }); -}); - -class MockEditorService extends TestEditorService { - - constructor(private readonly instantiationService: IInstantiationService) { - super(); - } - readonly overridenOpens: IOpenEditorOverrideHandler[] = []; - - override overrideOpenEditor(_handler: IOpenEditorOverrideHandler): IDisposable { - this.overridenOpens.push(_handler); - return toDisposable(() => { - const index = this.overridenOpens.findIndex(v => v === _handler); - if (!isUndefinedOrNull(index)) { - this.overridenOpens.splice(index, 1); - } - }); - } - - fireOpenEditor(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) { - for (const handler of this.overridenOpens) { - let response: IOpenEditorOverride | undefined; - if (response = handler.open(editor, options, group)) { - return response; - } - } - return undefined; - } - - override openEditor(_editor: any, _options?: any, _group?: any): Promise { - return Promise.resolve(_editor); - } - - override createEditorInput(_input: IUntitledTextResourceEditorInput): EditorInput { - const accessor = this.instantiationService.createInstance(ServiceAccessor); - const service = accessor.untitledTextEditorService; - return this.instantiationService.createInstance(UntitledTextEditorInput, service.create()); - } -} - -class TestModeService implements IModeService { - _serviceBrand: undefined; - onDidCreateMode: Event; - onLanguagesMaybeChanged: Event; - - isRegisteredMode(mimetypeOrModeId: string): boolean { - throw new Error('Method not implemented.'); - } - - getRegisteredModes(): string[] { - throw new Error('Method not implemented.'); - } - - getRegisteredLanguageNames(): string[] { - throw new Error('Method not implemented.'); - } - - getExtensions(alias: string): string[] { - throw new Error('Method not implemented.'); - } - - getFilenames(alias: string): string[] { - throw new Error('Method not implemented.'); - } - - getMimeForMode(modeId: string): string { - throw new Error('Method not implemented.'); - } - - getLanguageName(modeId: string): string { - throw new Error('Method not implemented.'); - } - - getModeIdForLanguageName(alias: string): string { - throw new Error('Method not implemented.'); - } - - getModeIdByFilepathOrFirstLine(resource: URI, firstLine?: string): string { - throw new Error('Method not implemented.'); - } - - getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string { - throw new Error('Method not implemented.'); - } - - getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier { - throw new Error('Method not implemented.'); - } - - getConfigurationFiles(modeId: string): URI[] { - throw new Error('Method not implemented.'); - } - - create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection { - throw new Error('Method not implemented.'); - } - - createByLanguageName(languageName: string): ILanguageSelection { - throw new Error('Method not implemented.'); - } - - createByFilepathOrFirstLine(rsource: URI, firstLine?: string): ILanguageSelection { - throw new Error('Method not implemented.'); - } - - triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void { - throw new Error('Method not implemented.'); - } -} - -class ServiceAccessor { - constructor( - @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService - ) { } -} diff --git a/src/sql/workbench/contrib/notebook/browser/models/notebookInputFactory.ts b/src/sql/workbench/contrib/notebook/browser/models/notebookInputFactory.ts index e17fac9fd6..d9ada47159 100644 --- a/src/sql/workbench/contrib/notebook/browser/models/notebookInputFactory.ts +++ b/src/sql/workbench/contrib/notebook/browser/models/notebookInputFactory.ts @@ -21,6 +21,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur const editorInputFactoryRegistry = Registry.as(EditorExtensions.EditorInputFactories); export class NotebookEditorInputAssociation implements ILanguageAssociation { + /** + * The language IDs that are associated with Notebooks. These are case sensitive for comparing with what's + * registered in the ModeService registry. + */ static readonly languages = [NotebookLanguage.Notebook, NotebookLanguage.Ipynb]; constructor(@IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService) { } @@ -40,6 +44,10 @@ export class NotebookEditorInputAssociation implements ILanguageAssociation { return undefined; } + syncConvertinput(activeEditor: IEditorInput): NotebookInput | DiffNotebookInput | undefined { + return this.convertInput(activeEditor); + } + createBase(activeEditor: NotebookInput): IEditorInput { return activeEditor.textInput; } @@ -65,7 +73,7 @@ export class FileNoteBookEditorInputSerializer implements IEditorInputSerializer } } -export class UntitledNoteBookEditorInputFactory implements IEditorInputSerializer { +export class UntitledNotebookEditorInputSerializer implements IEditorInputSerializer { serialize(editorInput: UntitledNotebookInput): string { const factory = editorInputFactoryRegistry.getEditorInputSerializer(UntitledTextEditorInput.ID); if (factory) { diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts index 0dd5e0eff5..e900286243 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -9,11 +9,10 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; import { IEditorInputFactoryRegistry, ActiveEditorContext, IEditorInput, EditorExtensions } from 'vs/workbench/common/editor'; - import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation'; import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/browser/models/untitledNotebookInput'; import { FileNotebookInput } from 'sql/workbench/contrib/notebook/browser/models/fileNotebookInput'; -import { FileNoteBookEditorInputSerializer, UntitledNoteBookEditorInputFactory, NotebookEditorInputAssociation } from 'sql/workbench/contrib/notebook/browser/models/notebookInputFactory'; +import { FileNoteBookEditorInputSerializer, NotebookEditorInputAssociation, UntitledNotebookEditorInputSerializer } from 'sql/workbench/contrib/notebook/browser/models/notebookInputFactory'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionsExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor, registerAction2, MenuRegistry, MenuId, Action2 } from 'vs/platform/actions/common/actions'; @@ -43,7 +42,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { MarkdownOutputComponent } from 'sql/workbench/contrib/notebook/browser/outputs/markdownOutput.component'; import { registerCellComponent } from 'sql/platform/notebooks/common/outputRegistry'; import { TextCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/textCell.component'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { NotebookThemingContribution } from 'sql/workbench/contrib/notebook/browser/notebookThemingContribution'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; @@ -56,18 +55,24 @@ import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/not import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { INotebookManager } from 'sql/workbench/services/notebook/browser/notebookService'; import { NotebookExplorerViewletViewsContribution } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ContributedEditorPriority, IEditorOverrideService } from 'vs/workbench/services/editor/common/editorOverrideService'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; Registry.as(EditorExtensions.EditorInputFactories) .registerEditorInputSerializer(FileNotebookInput.ID, FileNoteBookEditorInputSerializer); Registry.as(EditorExtensions.EditorInputFactories) - .registerEditorInputSerializer(UntitledNotebookInput.ID, UntitledNoteBookEditorInputFactory); + .registerEditorInputSerializer(UntitledNotebookInput.ID, UntitledNotebookEditorInputSerializer); Registry.as(LanguageAssociationExtensions.LanguageAssociations) .registerLanguageAssociation(NotebookEditorInputAssociation.languages, NotebookEditorInputAssociation); Registry.as(EditorExtensions.Editors) - .registerEditor(EditorDescriptor.create(NotebookEditor, NotebookEditor.ID, localize('notebookEditor.name', "Notebook Editor")), [new SyncDescriptor(UntitledNotebookInput), new SyncDescriptor(FileNotebookInput)]); + .registerEditor(EditorDescriptor.create(NotebookEditor, NotebookEditor.ID, NotebookEditor.LABEL), [new SyncDescriptor(UntitledNotebookInput), new SyncDescriptor(FileNotebookInput)]); Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(NotebookThemingContribution, LifecyclePhase.Restored); @@ -658,3 +663,74 @@ configurationRegistry.registerConfiguration({ }, } }); + +const languageAssociationRegistry = Registry.as(LanguageAssociationExtensions.LanguageAssociations); + +export class NotebookEditorOverrideContribution extends Disposable implements IWorkbenchContribution { + + private _registeredOverrides = new DisposableStore(); + + constructor( + @ILogService private _logService: ILogService, + @IEditorService private _editorService: IEditorService, + @IEditorOverrideService private _editorOverrideService: IEditorOverrideService, + @IModeService private _modeService: IModeService + ) { + super(); + this.registerEditorOverride(); + } + + private registerEditorOverride(): void { + // Refresh the editor overrides whenever the languages change so we ensure we always have + // the latest up to date list of extensions for each language + this._modeService.onLanguagesMaybeChanged(() => { + this._registeredOverrides.clear(); + // List of language IDs to associate the query editor for. These are case sensitive. + NotebookEditorInputAssociation.languages.map(lang => { + const langExtensions = this._modeService.getExtensions(lang); + if (langExtensions.length === 0) { + return; + } + // Create the selector from the list of all the language extensions we want to associate with the + // notebook editor (filtering out any languages which didn't have any extensions registered yet) + const selector = `*{${langExtensions.join(',')}}`; + this._registeredOverrides.add(this._editorOverrideService.registerContributionPoint( + selector, + { + id: NotebookEditor.ID, + label: NotebookEditor.LABEL, + describes: (currentEditor) => currentEditor instanceof FileNotebookInput, + priority: ContributedEditorPriority.builtin + }, + {}, + (resource, options, group) => { + const fileInput = this._editorService.createEditorInput({ + resource: resource + }) as FileEditorInput; + // Try to convert the input, falling back to just a plain file input if we're unable to + const newInput = this.tryConvertInput(fileInput, lang) ?? fileInput; + return { editor: newInput, options: options, group: group }; + }, + (diffEditorInput, options, group) => { + // Try to convert the input, falling back to the original input if we're unable to + const newInput = this.tryConvertInput(diffEditorInput, lang) ?? diffEditorInput; + return { editor: newInput, options: options, group: group }; + } + )); + }); + }); + } + + private tryConvertInput(input: IEditorInput, lang: string): IEditorInput | undefined { + const langAssociation = languageAssociationRegistry.getAssociationForLanguage(lang); + const notebookEditorInput = langAssociation?.syncConvertinput?.(input); + if (!notebookEditorInput) { + this._logService.warn('Unable to create input for overriding editor ', input instanceof DiffEditorInput ? `${input.primary.resource.toString()} <-> ${input.secondary.resource.toString()}` : input.resource.toString()); + return undefined; + } + return notebookEditorInput; + } +} + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(NotebookEditorOverrideContribution, LifecyclePhase.Restored); diff --git a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts index 3664b4c5e3..1dc6ddd82c 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts @@ -2,6 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; @@ -9,7 +11,6 @@ import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import * as DOM from 'vs/base/browser/dom'; import { bootstrapAngular } from 'sql/workbench/services/bootstrap/browser/bootstrapService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; - import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput'; import { NotebookModule } from 'sql/workbench/contrib/notebook/browser/notebook.module'; @@ -38,6 +39,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class NotebookEditor extends EditorPane implements IFindNotebookController { public static ID: string = 'workbench.editor.notebookEditor'; + public static LABEL: string = localize('notebookEditor.name', "Notebook Editor"); + private _notebookContainer: HTMLElement; private _currentDimensions: DOM.Dimension; private _overlay: HTMLElement; diff --git a/src/sql/workbench/contrib/query/browser/query.contribution.ts b/src/sql/workbench/contrib/query/browser/query.contribution.ts index 6436556f20..70f0c55eee 100644 --- a/src/sql/workbench/contrib/query/browser/query.contribution.ts +++ b/src/sql/workbench/contrib/query/browser/query.contribution.ts @@ -24,14 +24,14 @@ import { import * as gridActions from 'sql/workbench/contrib/editData/browser/gridActions'; import * as gridCommands from 'sql/workbench/contrib/editData/browser/gridCommands'; import { localize } from 'vs/nls'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TimeElapsedStatusBarContributions, RowCountStatusBarContributions, QueryStatusStatusBarContributions, QueryResultSelectionSummaryStatusBarContribution } from 'sql/workbench/contrib/query/browser/statusBarItems'; import { SqlFlavorStatusbarItem, ChangeFlavorAction } from 'sql/workbench/contrib/query/browser/flavorStatus'; import { EditorExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { FileQueryEditorInput } from 'sql/workbench/contrib/query/common/fileQueryEditorInput'; -import { FileQueryEditorInputSerializer, UntitledQueryEditorInputSerializer, QueryEditorLanguageAssociation } from 'sql/workbench/contrib/query/browser/queryInputFactory'; +import { FileQueryEditorInputSerializer, QueryEditorLanguageAssociation, UntitledQueryEditorInputSerializer } from 'sql/workbench/contrib/query/browser/queryInputFactory'; import { UntitledQueryEditorInput } from 'sql/workbench/common/editor/query/untitledQueryEditorInput'; import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation'; import { NewQueryTask, OE_NEW_QUERY_ACTION_ID, DE_NEW_QUERY_COMMAND_ID } from 'sql/workbench/contrib/query/browser/queryActions'; @@ -41,6 +41,12 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c import { ManageActionContext } from 'sql/workbench/browser/actions'; import { ItemContextKey } from 'sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerContext'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { IEditorOverrideService, ContributedEditorPriority } from 'vs/workbench/services/editor/common/editorOverrideService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ILogService } from 'vs/platform/log/common/log'; export const QueryEditorVisibleCondition = ContextKeyExpr.has(queryContext.queryEditorVisibleId); export const ResultsGridFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(queryContext.resultsVisibleId), ContextKeyExpr.has(queryContext.resultsGridFocussedId)); @@ -59,7 +65,7 @@ Registry.as(EditorExtensions.Editors) .registerEditor(EditorDescriptor.create(QueryResultsEditor, QueryResultsEditor.ID, localize('queryResultsEditor.name', "Query Results")), [new SyncDescriptor(QueryResultsInput)]); Registry.as(EditorExtensions.Editors) - .registerEditor(EditorDescriptor.create(QueryEditor, QueryEditor.ID, localize('queryEditor.name', "Query Editor")), [new SyncDescriptor(FileQueryEditorInput), new SyncDescriptor(UntitledQueryEditorInput)]); + .registerEditor(EditorDescriptor.create(QueryEditor, QueryEditor.ID, QueryEditor.LABEL), [new SyncDescriptor(FileQueryEditorInput), new SyncDescriptor(UntitledQueryEditorInput)]); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); @@ -490,3 +496,61 @@ workbenchRegistry.registerWorkbenchContribution(RowCountStatusBarContributions, workbenchRegistry.registerWorkbenchContribution(QueryStatusStatusBarContributions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(SqlFlavorStatusbarItem, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(QueryResultSelectionSummaryStatusBarContribution, LifecyclePhase.Restored); + +const languageAssociationRegistry = Registry.as(LanguageAssociationExtensions.LanguageAssociations); + +export class QueryEditorOverrideContribution extends Disposable implements IWorkbenchContribution { + private _registeredOverrides = new DisposableStore(); + + constructor( + @ILogService private _logService: ILogService, + @IEditorService private _editorService: IEditorService, + @IEditorOverrideService private _editorOverrideService: IEditorOverrideService, + @IModeService private _modeService: IModeService + ) { + super(); + this.registerEditorOverrides(); + } + + private registerEditorOverrides(): void { + // Refresh the editor overrides whenever the languages change so we ensure we always have + // the latest up to date list of extensions for each language + this._modeService.onLanguagesMaybeChanged(() => { + this._registeredOverrides.clear(); + // List of language IDs to associate the query editor for. These are case sensitive. + QueryEditorLanguageAssociation.languages.map(lang => { + const langExtensions = this._modeService.getExtensions(lang); + if (langExtensions.length === 0) { + return; + } + // Create the selector from the list of all the language extensions we want to associate with the + // query editor (filtering out any languages which didn't have any extensions registered yet) + const selector = `*{${langExtensions.join(',')}}`; + this._registeredOverrides.add(this._editorOverrideService.registerContributionPoint( + selector, + { + id: QueryEditor.ID, + label: QueryEditor.LABEL, + describes: (currentEditor) => currentEditor instanceof FileQueryEditorInput, + priority: ContributedEditorPriority.builtin + }, + {}, + (resource, options, group) => { + const fileInput = this._editorService.createEditorInput({ + resource: resource + }) as FileEditorInput; + const langAssociation = languageAssociationRegistry.getAssociationForLanguage(lang); + const queryEditorInput = langAssociation?.syncConvertinput?.(fileInput); + if (!queryEditorInput) { + this._logService.warn('Unable to create input for overriding editor ', resource); + return undefined; + } + return { editor: queryEditorInput, options: options, group: group }; + } + )); + }); + }); + } +} + +workbenchRegistry.registerWorkbenchContribution(QueryEditorOverrideContribution, LifecyclePhase.Restored); diff --git a/src/sql/workbench/contrib/query/browser/queryEditor.ts b/src/sql/workbench/contrib/query/browser/queryEditor.ts index 3815861f1b..7dc9526aa6 100644 --- a/src/sql/workbench/contrib/query/browser/queryEditor.ts +++ b/src/sql/workbench/contrib/query/browser/queryEditor.ts @@ -5,6 +5,7 @@ import 'vs/css!./media/queryEditor'; +import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import * as path from 'vs/base/common/path'; import { EditorOptions, IEditorControl, IEditorMemento, IEditorOpenContext } from 'vs/workbench/common/editor'; @@ -54,6 +55,7 @@ interface IQueryEditorViewState { export class QueryEditor extends EditorPane { public static ID: string = 'workbench.editor.queryEditor'; + public static LABEL = localize('queryEditor.name', "Query Editor"); private dimension: DOM.Dimension = new DOM.Dimension(0, 0); diff --git a/src/sql/workbench/contrib/query/browser/queryInputFactory.ts b/src/sql/workbench/contrib/query/browser/queryInputFactory.ts index 2714ad56fc..d66f5224d7 100644 --- a/src/sql/workbench/contrib/query/browser/queryInputFactory.ts +++ b/src/sql/workbench/contrib/query/browser/queryInputFactory.ts @@ -28,7 +28,11 @@ const editorInputFactoryRegistry = Registry.as(Edit export class QueryEditorLanguageAssociation implements ILanguageAssociation { static readonly isDefault = true; - static readonly languages = ['sql', 'kusto', 'loganalytics']; //TODO Add language id here for new languages supported in query editor. Make it easier to contribute new extension's languageID + /** + * The language IDs that are associated with the query editor. These are case sensitive for comparing with what's + * registered in the ModeService registry. + */ + static readonly languages = ['Kusto', 'LogAnalytics', 'SQL']; //TODO Add language id here for new languages supported in query editor. Make it easier to contribute new extension's languageID constructor(@IInstantiationService private readonly instantiationService: IInstantiationService, @@ -47,9 +51,11 @@ export class QueryEditorLanguageAssociation implements ILanguageAssociation { queryEditorInput = this.instantiationService.createInstance(FileQueryEditorInput, '', activeEditor, queryResultsInput); } else if (activeEditor instanceof UntitledTextEditorInput) { const content = (await activeEditor.resolve()).textEditorModel.getValue(); - queryEditorInput = await this.queryEditorService.newSqlEditor({ resource: this.editorService.isOpened(activeEditor) ? activeEditor.resource : undefined, open: false, initalContent: content }) as UntitledQueryEditorInput; + queryEditorInput = await this.queryEditorService.newSqlEditor({ + resource: this.editorService.isOpened(activeEditor) ? activeEditor.resource : undefined, + open: false, initalContent: content + }) as UntitledQueryEditorInput; } - const profile = getCurrentGlobalConnection(this.objectExplorerService, this.connectionManagementService, this.editorService); if (profile) { const options: IConnectionCompletionOptions = { diff --git a/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts b/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts index 5772505c09..0c04656958 100644 --- a/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts +++ b/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts @@ -34,7 +34,7 @@ suite('Query Input Factory', () => { return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, undefined, preferredMode); } - test('sync query editor input is connected if global connection exists (OE)', () => { + test('sync query editor input is connected if global connection exists (OE)', async () => { const editorService = new MockEditorService(); instantiationService = workbenchInstantiationService(); const connectionManagementService = new MockConnectionManagementService(); @@ -43,7 +43,7 @@ suite('Query Input Factory', () => { instantiationService.stub(IEditorService, editorService); const queryEditorLanguageAssociation = instantiationService.createInstance(QueryEditorLanguageAssociation); const input = createFileInput(URI.file('/test/file.sql'), undefined, undefined, undefined); - queryEditorLanguageAssociation.convertInput(input); + await queryEditorLanguageAssociation.convertInput(input); assert(connectionManagementService.numberConnects === 1, 'Convert input should have called connect when active OE connection exists'); }); diff --git a/src/sql/workbench/contrib/queryPlan/browser/queryPlan.contribution.ts b/src/sql/workbench/contrib/queryPlan/browser/queryPlan.contribution.ts index 225e0baf0e..5d333e659d 100644 --- a/src/sql/workbench/contrib/queryPlan/browser/queryPlan.contribution.ts +++ b/src/sql/workbench/contrib/queryPlan/browser/queryPlan.contribution.ts @@ -3,24 +3,55 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { QueryPlanInput, QueryPlanConverter } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput'; +import { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput'; import { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Registry } from 'vs/platform/registry/common/platform'; import { QueryPlanEditor } from 'sql/workbench/contrib/queryPlan/browser/queryPlanEditor'; -import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ContributedEditorPriority, IEditorOverrideService } from 'vs/workbench/services/editor/common/editorOverrideService'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; // Query Plan editor registration const queryPlanEditorDescriptor = EditorDescriptor.create( QueryPlanEditor, QueryPlanEditor.ID, - 'QueryPlan' + QueryPlanEditor.LABEL ); Registry.as(EditorExtensions.Editors) .registerEditor(queryPlanEditorDescriptor, [new SyncDescriptor(QueryPlanInput)]); -Registry.as(LanguageAssociationExtensions.LanguageAssociations) - .registerLanguageAssociation(QueryPlanConverter.languages, QueryPlanConverter); +export class QueryPlanEditorOverrideContribution extends Disposable implements IWorkbenchContribution { + constructor( + @IInstantiationService private _instantiationService: IInstantiationService, + @IEditorOverrideService private _editorOverrideService: IEditorOverrideService + ) { + super(); + this.registerEditorOverride(); + } + + private registerEditorOverride(): void { + this._editorOverrideService.registerContributionPoint( + '*.sqlplan', + { + id: QueryPlanEditor.ID, + label: QueryPlanEditor.LABEL, + describes: (currentEditor) => currentEditor instanceof QueryPlanInput, + priority: ContributedEditorPriority.builtin + }, + {}, + (resource, options, group) => { + const queryPlanInput = this._instantiationService.createInstance(QueryPlanInput, resource); + return { editor: queryPlanInput }; + } + ); + } +} + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(QueryPlanEditorOverrideContribution, LifecyclePhase.Restored); diff --git a/src/sql/workbench/contrib/queryPlan/browser/queryPlanEditor.ts b/src/sql/workbench/contrib/queryPlan/browser/queryPlanEditor.ts index b235f22d61..090ba50c6b 100644 --- a/src/sql/workbench/contrib/queryPlan/browser/queryPlanEditor.ts +++ b/src/sql/workbench/contrib/queryPlan/browser/queryPlanEditor.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import { localize } from 'vs/nls'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -16,6 +17,7 @@ import { QueryPlanView } from 'sql/workbench/contrib/queryPlan/browser/queryPlan export class QueryPlanEditor extends EditorPane { public static ID: string = 'workbench.editor.queryplan'; + public static LABEL: string = localize('queryPlanEditor', "Query Plan Editor"); private view = this._register(new QueryPlanView()); diff --git a/src/sql/workbench/contrib/queryPlan/common/queryPlanInput.ts b/src/sql/workbench/contrib/queryPlan/common/queryPlanInput.ts index 6a2525560f..821359d6e6 100644 --- a/src/sql/workbench/contrib/queryPlan/common/queryPlanInput.ts +++ b/src/sql/workbench/contrib/queryPlan/common/queryPlanInput.ts @@ -3,34 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILanguageAssociation } from 'sql/workbench/services/languageAssociation/common/languageAssociation'; import { URI } from 'vs/base/common/uri'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/editor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -export class QueryPlanConverter implements ILanguageAssociation { - static readonly languages = ['sqlplan']; - - constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEditorService private readonly editorService: IEditorService - ) { } - - convertInput(activeEditor: IEditorInput): QueryPlanInput | undefined { - if (activeEditor.resource) { - return this.instantiationService.createInstance(QueryPlanInput, activeEditor.resource); - } - return undefined; - } - - createBase(activeEditor: QueryPlanInput): IEditorInput { - return this.editorService.createEditorInput({ resource: activeEditor.resource }); - } -} - export class QueryPlanInput extends EditorInput { public static ID: string = 'workbench.editorinputs.queryplan'; diff --git a/src/sql/workbench/services/languageAssociation/common/languageAssociation.ts b/src/sql/workbench/services/languageAssociation/common/languageAssociation.ts index 4247ca189c..8887c367d0 100644 --- a/src/sql/workbench/services/languageAssociation/common/languageAssociation.ts +++ b/src/sql/workbench/services/languageAssociation/common/languageAssociation.ts @@ -25,6 +25,10 @@ type ILanguageAssociationSignature = new (... export interface ILanguageAssociationRegistry { registerLanguageAssociation(languages: string[], contribution: ILanguageAssociationSignature, isDefault?: boolean): IDisposable; + /** + * Gets the registered association for a language if one is registered + * @param language The case-insensitive language ID to get the association for + */ getAssociationForLanguage(language: string): ILanguageAssociation | undefined; readonly defaultAssociation: [string, ILanguageAssociation] | undefined; @@ -54,6 +58,7 @@ const languageAssociationRegistry = new class implements ILanguageAssociationReg } registerLanguageAssociation(languages: string[], contribution: ILanguageAssociationSignature, isDefault?: boolean): IDisposable { + languages = languages.map(lang => lang.toLowerCase()); for (const language of languages) { this.associationContructors.set(language, contribution); } @@ -71,7 +76,7 @@ const languageAssociationRegistry = new class implements ILanguageAssociationReg } getAssociationForLanguage(language: string): ILanguageAssociation | undefined { - return this.associationsInstances.get(language); + return this.associationsInstances.get(language.toLowerCase()); } get defaultAssociation(): [string, ILanguageAssociation] | undefined { diff --git a/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts b/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts index 5f7213ae25..d8836c0ed6 100644 --- a/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts +++ b/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts @@ -5,7 +5,7 @@ import { QueryResultsInput } from 'sql/workbench/common/editor/query/queryResultsInput'; import { EditDataInput } from 'sql/workbench/browser/editData/editDataInput'; -import { IConnectableInput, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; +import { ConnectionType, IConnectableInput, IConnectionCompletionOptions, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IQueryEditorService, INewSqlEditorOptions } from 'sql/workbench/services/queryEditor/common/queryEditorService'; import { UntitledQueryEditorInput } from 'sql/workbench/common/editor/query/untitledQueryEditorInput'; @@ -20,7 +20,10 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { mixin } from 'vs/base/common/objects'; import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; - +import { getCurrentGlobalConnection } from 'sql/workbench/browser/taskUtilities'; +import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; const defaults: INewSqlEditorOptions = { open: true }; @@ -37,7 +40,8 @@ export class QueryEditorService implements IQueryEditorService { @IInstantiationService private _instantiationService: IInstantiationService, @IEditorService private _editorService: IEditorService, @IConfigurationService private _configurationService: IConfigurationService, - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService + @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService ) { } @@ -63,10 +67,25 @@ export class QueryEditorService implements IQueryEditorService { const queryResultsInput: QueryResultsInput = this._instantiationService.createInstance(QueryResultsInput, docUri.toString()); let queryInput = this._instantiationService.createInstance(UntitledQueryEditorInput, options.description, fileInput, queryResultsInput); + let profile: IConnectionProfile | undefined = undefined; + // If we're told to connect then get the connection before opening the editor since it will try to get the connection for the current + // active editor and so we need to get this before opening a new one. + if (options.connectWithGlobal) { + profile = getCurrentGlobalConnection(this._objectExplorerService, this._connectionManagementService, this._editorService); + } if (options.open) { await this._editorService.openEditor(queryInput, { pinned: true }); } - + if (profile) { + const options: IConnectionCompletionOptions = { + params: { connectionType: ConnectionType.editor, runQueryOnCompletion: undefined, input: queryInput }, + saveTheConnection: false, + showDashboard: false, + showConnectionDialogOnError: true, + showFirewallRuleOnError: true + }; + this._connectionManagementService.connect(profile, queryInput.uri, options).catch(err => onUnexpectedError(err)); + } return queryInput; } diff --git a/src/sql/workbench/services/queryEditor/common/queryEditorService.ts b/src/sql/workbench/services/queryEditor/common/queryEditorService.ts index 45025215b9..2eb4f0ef30 100644 --- a/src/sql/workbench/services/queryEditor/common/queryEditorService.ts +++ b/src/sql/workbench/services/queryEditor/common/queryEditorService.ts @@ -32,6 +32,11 @@ export interface INewSqlEditorOptions { * use an existing resource, if this matches a resource already open that resource will be opened instead */ resource?: URI + + /** + * Whether to connect the editor using the current global connection + */ + connectWithGlobal?: boolean } export interface IQueryEditorService { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index f7274e78e7..086d81df5e 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -45,6 +45,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { toAction } from 'vs/base/common/actions'; import { EditorOverride } from 'vs/platform/editor/common/editor'; import { hash } from 'vs/base/common/hash'; +import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; // {{SQL CARBON EDIT}} New query command // Commands @@ -676,16 +677,10 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ ] }, handler: async (accessor, args?: { viewType: string }) => { - const editorService = accessor.get(IEditorService); - - if (typeof args?.viewType === 'string') { - const editorGroupsService = accessor.get(IEditorGroupsService); - - const group = editorGroupsService.activeGroup; - await editorService.openEditor({ options: { override: args.viewType, pinned: true } }, group); - } else { - await editorService.openEditor({ options: { pinned: true } }); // untitled are always pinned - } + // {{SQL CARBON EDIT}} Modify to open untitled query editor + // We don't use the viewType arg since we always want to open a query editor + const queryEditorService = accessor.get(IQueryEditorService); + await queryEditorService.newSqlEditor({ connectWithGlobal: true }); } }); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index c1a76d42df..66eb51fafe 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -438,9 +438,6 @@ import 'sql/workbench/contrib/query/common/resultsGrid.contribution'; import 'sql/workbench/contrib/dataExplorer/browser/dataExplorer.contribution'; import 'sql/workbench/contrib/dataExplorer/browser/nodeActions.common.contribution'; -//editor replacement -import 'sql/workbench/contrib/editorReplacement/common/editorReplacer.contribution'; - //configurationUpgrader replacement import 'sql/workbench/contrib/configuration/common/configurationUpgrader.contribution';