editorReplacer -> editorOverride (#16041)

* editorReplacer -> editorOverride

* Update lifecycle phsae

* add back input factories

* Add comment

* add back tests

* comments

* fix log

* comments
This commit is contained in:
Charles Gagnon
2021-07-09 08:46:50 -07:00
committed by GitHub
parent 7ba0e49673
commit 8f202d91b6
19 changed files with 253 additions and 509 deletions

View File

@@ -30,7 +30,7 @@ export const InputBoxFocusedKey = new RawContextKey<boolean>('inputBoxFocus', fa
export const SearchInputBoxFocusedKey = new RawContextKey<boolean>('searchInputBoxFocus', false);
export const enum NotebookLanguage {
Notebook = 'notebook',
Notebook = 'Notebook',
Ipynb = 'ipynb'
}
export interface INotebookSearchConfigurationProperties {

View File

@@ -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<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(EditorReplacementContribution, LifecyclePhase.Starting);

View File

@@ -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<ILanguageAssociationRegistry>(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);
}
}

View File

@@ -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<ILanguageAssociationRegistry>(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 = <any>(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 = <any>(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 = <any>(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 = <any>(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 = <any>(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<any> {
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<IMode>;
onLanguagesMaybeChanged: Event<void>;
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
) { }
}

View File

@@ -21,6 +21,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
const editorInputFactoryRegistry = Registry.as<IEditorInputFactoryRegistry>(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) {

View File

@@ -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<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories)
.registerEditorInputSerializer(FileNotebookInput.ID, FileNoteBookEditorInputSerializer);
Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories)
.registerEditorInputSerializer(UntitledNotebookInput.ID, UntitledNoteBookEditorInputFactory);
.registerEditorInputSerializer(UntitledNotebookInput.ID, UntitledNotebookEditorInputSerializer);
Registry.as<ILanguageAssociationRegistry>(LanguageAssociationExtensions.LanguageAssociations)
.registerLanguageAssociation(NotebookEditorInputAssociation.languages, NotebookEditorInputAssociation);
Registry.as<IEditorRegistry>(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<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(NotebookThemingContribution, LifecyclePhase.Restored);
@@ -658,3 +663,74 @@ configurationRegistry.registerConfiguration({
},
}
});
const languageAssociationRegistry = Registry.as<ILanguageAssociationRegistry>(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<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(NotebookEditorOverrideContribution, LifecyclePhase.Restored);

View File

@@ -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;

View File

@@ -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<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(EditorDescriptor.create(QueryResultsEditor, QueryResultsEditor.ID, localize('queryResultsEditor.name', "Query Results")), [new SyncDescriptor(QueryResultsInput)]);
Registry.as<IEditorRegistry>(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 = <IWorkbenchActionRegistry>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<ILanguageAssociationRegistry>(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);

View File

@@ -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);

View File

@@ -28,7 +28,11 @@ const editorInputFactoryRegistry = Registry.as<IEditorInputFactoryRegistry>(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 = {

View File

@@ -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');
});

View File

@@ -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<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(queryPlanEditorDescriptor, [new SyncDescriptor(QueryPlanInput)]);
Registry.as<ILanguageAssociationRegistry>(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<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(QueryPlanEditorOverrideContribution, LifecyclePhase.Restored);

View File

@@ -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());

View File

@@ -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';

View File

@@ -25,6 +25,10 @@ type ILanguageAssociationSignature<Services extends BrandedService[]> = new (...
export interface ILanguageAssociationRegistry {
registerLanguageAssociation<Services extends BrandedService[]>(languages: string[], contribution: ILanguageAssociationSignature<Services>, 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<BrandedService[]>, 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 {

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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 });
}
});

View File

@@ -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';