mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
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:
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
) { }
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user