Add editor contribution tests (#8784)

* wip

* rewrite association

* fix tests

* add more tests

* fix tests

* fix more tests

* fix tests
This commit is contained in:
Anthony Dresser
2020-01-10 18:57:47 -08:00
committed by GitHub
parent 0059ffb777
commit 2f6c944317
16 changed files with 421 additions and 75 deletions

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IModeSupport, IEditorInput } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -20,14 +20,13 @@ const languageAssociationRegistry = Registry.as<ILanguageAssociationRegistry>(La
*/
export async function setMode(accessor: ServicesAccessor, modeSupport: IModeSupport, activeEditor: IEditorInput, language: string): Promise<void> {
const editorService = accessor.get(IEditorService);
const instantiationService = accessor.get(IInstantiationService);
const activeWidget = getCodeEditor(editorService.activeTextEditorWidget);
const activeControl = editorService.activeControl;
const textModel = activeWidget.getModel();
const oldLanguage = textModel.getLanguageIdentifier().language;
if (language !== oldLanguage) {
const oldInputCreator = languageAssociationRegistry.getAssociations().filter(e => e.language === oldLanguage)[0]; // who knows how to handle the current language
const newInputCreator = languageAssociationRegistry.getAssociations().filter(e => e.language === language)[0]; // who knows how to handle the requested language
const oldInputCreator = languageAssociationRegistry.getAssociationForLanguage(oldLanguage); // who knows how to handle the current language
const newInputCreator = languageAssociationRegistry.getAssociationForLanguage(language); // who knows how to handle the requested language
if ((oldInputCreator || newInputCreator) && activeEditor.isDirty()) { // theres some issues with changing the language on a dirty file with one of our editors (we should look into this)
const notificationService = accessor.get(INotificationService);
notificationService.error(localize('languageChangeUnsupported', "Changing editor types on unsaved files is unsupported"));
@@ -36,11 +35,11 @@ export async function setMode(accessor: ServicesAccessor, modeSupport: IModeSupp
modeSupport.setMode(language);
let input: IEditorInput;
if (oldInputCreator) { // only transform the input if we have someone who knows how to deal with it (e.x QueryInput -> UntitledInput, etc)
input = oldInputCreator.baseInputCreator(activeEditor);
input = oldInputCreator.createBase(activeEditor);
}
if (newInputCreator) { // if we know how to handle the new language, tranform the input and replace the editor (e.x notebook, sql, etc)
const newInput = instantiationService.invokeFunction(newInputCreator.creator, input || activeEditor);
const newInput = newInputCreator.convertInput(input || activeEditor);
if (newInput) { // the factory will return undefined if it doesn't know how to handle the input
await editorService.replaceEditors([{ editor: activeEditor, replacement: newInput }], activeControl.group);
}

View File

@@ -11,7 +11,6 @@ import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/ed
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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import * as path from 'vs/base/common/path';
@@ -25,7 +24,6 @@ export class EditorReplacementContribution implements IWorkbenchContribution {
constructor(
@IEditorService private readonly editorService: IEditorService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IModeService private readonly modeService: IModeService
) {
this.editorOpeningListener = this.editorService.overrideOpenEditor((editor, options, group) => this.onEditorOpening(editor, options, group));
@@ -48,8 +46,6 @@ export class EditorReplacementContribution implements IWorkbenchContribution {
language = editor.getPreferredMode();
} else if (editor instanceof UntitledTextEditorInput) {
language = editor.getMode();
} else {
return undefined;
}
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
@@ -62,18 +58,18 @@ export class EditorReplacementContribution implements IWorkbenchContribution {
}
if (!language) {
const defaultInputCreator = languageAssociationRegistry.getAssociations().filter(e => e.isDefault)[0];
const defaultInputCreator = languageAssociationRegistry.defaultAssociation;
if (defaultInputCreator) {
editor.setMode(defaultInputCreator.language);
const newInput = this.instantiationService.invokeFunction(defaultInputCreator.creator, editor);
editor.setMode(defaultInputCreator[0]);
const newInput = defaultInputCreator[1].convertInput(editor);
if (newInput) {
return { override: this.editorService.openEditor(newInput, options, group) };
}
}
} else {
const inputCreator = languageAssociationRegistry.getAssociations().filter(e => e.language === language)[0];
const inputCreator = languageAssociationRegistry.getAssociationForLanguage(language);
if (inputCreator) {
const newInput = this.instantiationService.invokeFunction(inputCreator.creator, editor);
const newInput = inputCreator.convertInput(editor);
if (newInput) {
return { override: this.editorService.openEditor(newInput, options, group) };
}

View File

@@ -5,29 +5,75 @@
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorInput, EditorInput } from 'vs/workbench/common/editor';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { find } from 'vs/base/common/arrays';
import { ServicesAccessor, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
export type InputCreator = (servicesAccessor: ServicesAccessor, activeEditor: IEditorInput) => EditorInput | undefined;
export type BaseInputCreator = (activeEditor: IEditorInput) => IEditorInput;
export interface ILanguageAssociation {
convertInput(activeEditor: IEditorInput): EditorInput | undefined;
createBase(activeEditor: IEditorInput): IEditorInput;
}
type ILanguageAssociationSignature<Services extends BrandedService[]> = new (...services: Services) => ILanguageAssociation;
export interface ILanguageAssociationRegistry {
registerLanguageAssociation(language: string, creator: InputCreator, baseInputCreator: BaseInputCreator, isDefault?: boolean): void;
getAssociations(): Array<{ language: string, creator: InputCreator, baseInputCreator: BaseInputCreator, isDefault: boolean }>;
registerLanguageAssociation<Services extends BrandedService[]>(languages: string[], contribution: ILanguageAssociationSignature<Services>, isDefault?: boolean): IDisposable;
getAssociationForLanguage(language: string): ILanguageAssociation;
readonly defaultAssociation: [string, ILanguageAssociation];
/**
* Starts the registry by providing the required services.
*/
start(accessor: ServicesAccessor): void;
}
const languageAssociationRegistery = new class implements ILanguageAssociationRegistry {
private associations = new Array<{ language: string, creator: InputCreator, baseInputCreator: BaseInputCreator, isDefault: boolean }>();
private associationsInstances = new Map<string, ILanguageAssociation>();
private associationContructors = new Map<string, ILanguageAssociationSignature<BrandedService[]>>();
private defaultAssociationsInstance?: [string, ILanguageAssociation];
private defaultAssociationsConstructor?: [string, ILanguageAssociationSignature<BrandedService[]>];
registerLanguageAssociation(language: string, creator: InputCreator, baseInputCreator: BaseInputCreator, isDefault: boolean = false): void {
this.associations.push({ language, creator, baseInputCreator, isDefault });
start(accessor: ServicesAccessor): void {
const instantiationService = accessor.get(IInstantiationService);
for (const [language, ctor] of this.associationContructors) {
const instance = instantiationService.createInstance(ctor);
this.associationsInstances.set(language, instance);
}
if (this.defaultAssociationsConstructor) {
this.defaultAssociationsInstance = [this.defaultAssociationsConstructor[0], instantiationService.createInstance(this.defaultAssociationsConstructor[1])];
}
}
getAssociations(): Array<{ language: string, creator: InputCreator, baseInputCreator: BaseInputCreator, isDefault: boolean }> {
return this.associations.slice();
registerLanguageAssociation<Services extends BrandedService[]>(languages: string[], contribution: ILanguageAssociationSignature<Services>, isDefault?: boolean): IDisposable {
for (const language of languages) {
this.associationContructors.set(language, contribution);
}
if (isDefault) {
this.defaultAssociationsConstructor = [languages[0], contribution];
}
return toDisposable(() => {
for (const language of languages) {
this.associationContructors.delete(language);
this.associationsInstances.delete(language);
}
});
}
getAssociationForLanguage(language: string): ILanguageAssociation | undefined {
return this.associationsInstances.get(language);
}
get defaultAssociation(): [string, ILanguageAssociation] | undefined {
return this.defaultAssociationsInstance;
}
};
@@ -37,15 +83,14 @@ export const Extensions = {
Registry.add(Extensions.LanguageAssociations, languageAssociationRegistery);
export function doHandleUpgrade(accessor: ServicesAccessor, editor: EditorInput): EditorInput {
export function doHandleUpgrade(editor: EditorInput): EditorInput {
if (editor instanceof UntitledTextEditorInput || editor instanceof FileEditorInput) {
const instantiationService = accessor.get(IInstantiationService);
const activeWidget = getCodeEditor(editor);
const textModel = activeWidget.getModel();
const oldLanguage = textModel.getLanguageIdentifier().language;
const association = find(languageAssociationRegistery.getAssociations(), l => l.language === oldLanguage);
const association = languageAssociationRegistery.getAssociationForLanguage(oldLanguage);
if (association) {
return instantiationService.invokeFunction(association.creator, editor);
return association.convertInput(editor);
}
}
return editor;

View File

@@ -6,18 +6,16 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { localize } from 'vs/nls';
import { IEditorInputFactoryRegistry, Extensions as EditorInputFactoryExtensions } from 'vs/workbench/common/editor';
import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/common/languageAssociation';
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/models/untitledNotebookInput';
import { FileNotebookInput } from 'sql/workbench/contrib/notebook/common/models/fileNotebookInput';
import { FileNoteBookEditorInputFactory, UntitledNoteBookEditorInputFactory } from 'sql/workbench/contrib/notebook/common/models/nodebookInputFactory';
import { FileNoteBookEditorInputFactory, UntitledNoteBookEditorInputFactory, NotebookEditorInputAssociation } from 'sql/workbench/contrib/notebook/common/models/nodebookInputFactory';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionsExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor, registerAction, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { NotebookEditor } from 'sql/workbench/contrib/notebook/browser/notebookEditor';
import { NewNotebookAction } from 'sql/workbench/contrib/notebook/browser/notebookActions';
import { KeyMod } from 'vs/editor/common/standalone/standaloneBase';
@@ -45,7 +43,6 @@ 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 { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
Registry.as<IEditorInputFactoryRegistry>(EditorInputFactoryExtensions.EditorInputFactories)
.registerEditorInputFactory(FileNotebookInput.ID, FileNoteBookEditorInputFactory);
@@ -54,16 +51,7 @@ Registry.as<IEditorInputFactoryRegistry>(EditorInputFactoryExtensions.EditorInpu
.registerEditorInputFactory(UntitledNotebookInput.ID, UntitledNoteBookEditorInputFactory);
Registry.as<ILanguageAssociationRegistry>(LanguageAssociationExtensions.LanguageAssociations)
.registerLanguageAssociation('notebook', (accessor, editor) => {
const instantiationService = accessor.get(IInstantiationService);
if (editor instanceof FileEditorInput) {
return instantiationService.createInstance(FileNotebookInput, editor.getName(), editor.getResource(), editor);
} else if (editor instanceof UntitledTextEditorInput) {
return instantiationService.createInstance(UntitledNotebookInput, editor.getName(), editor.getResource(), editor);
} else {
return undefined;
}
}, (editor: NotebookInput) => editor.textInput);
.registerLanguageAssociation(NotebookEditorInputAssociation.languages, NotebookEditorInputAssociation);
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(new EditorDescriptor(NotebookEditor, NotebookEditor.ID, localize('notebookEditor.name', "Notebook Editor")), [new SyncDescriptor(UntitledNotebookInput), new SyncDescriptor(FileNotebookInput)]);

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, IEditorInput } from 'vs/workbench/common/editor';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
@@ -11,9 +11,31 @@ import { FileNotebookInput } from 'sql/workbench/contrib/notebook/common/models/
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/models/untitledNotebookInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { ILanguageAssociation } from 'sql/workbench/common/languageAssociation';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
const editorInputFactoryRegistry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
export class NotebookEditorInputAssociation implements ILanguageAssociation {
static readonly languages = ['notebook'];
constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) { }
convertInput(activeEditor: IEditorInput): NotebookInput {
if (activeEditor instanceof FileEditorInput) {
return this.instantiationService.createInstance(FileNotebookInput, activeEditor.getName(), activeEditor.getResource(), activeEditor);
} else if (activeEditor instanceof UntitledTextEditorInput) {
return this.instantiationService.createInstance(UntitledNotebookInput, activeEditor.getName(), activeEditor.getResource(), activeEditor);
} else {
return undefined;
}
}
createBase(activeEditor: NotebookInput): IEditorInput {
return activeEditor.textInput;
}
}
export class FileNoteBookEditorInputFactory implements IEditorInputFactory {
serialize(editorInput: FileNotebookInput): string {
const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID);

View File

@@ -220,7 +220,7 @@ export class NotebookServiceStub implements INotebookService {
throw new Error('Method not implemented.');
}
getProvidersForFileType(fileType: string): string[] {
throw new Error('Method not implemented.');
return [];
}
getStandardKernelsForProvider(provider: string): nb.IStandardKernel[] {
throw new Error('Method not implemented.');

View File

@@ -34,18 +34,15 @@ import { TimeElapsedStatusBarContributions, RowCountStatusBarContributions, Quer
import { SqlFlavorStatusbarItem, ChangeFlavorAction } from 'sql/workbench/contrib/query/browser/flavorStatus';
import { IEditorInputFactoryRegistry, Extensions as EditorInputFactoryExtensions } from 'vs/workbench/common/editor';
import { FileQueryEditorInput } from 'sql/workbench/contrib/query/common/fileQueryEditorInput';
import { FileQueryEditorInputFactory, UntitledQueryEditorInputFactory } from 'sql/workbench/contrib/query/common/queryInputFactory';
import { FileQueryEditorInputFactory, UntitledQueryEditorInputFactory, QueryEditorLanguageAssociation } from 'sql/workbench/contrib/query/common/queryInputFactory';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/common/languageAssociation';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
import { NewQueryTask, OE_NEW_QUERY_ACTION_ID, DE_NEW_QUERY_COMMAND_ID } from 'sql/workbench/contrib/query/browser/queryActions';
import { TreeNodeContextKey } from 'sql/workbench/contrib/objectExplorer/common/treeNodeContextKey';
import { MssqlNodeContext } from 'sql/workbench/contrib/dataExplorer/browser/mssqlNodeContext';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { ManageActionContext } from 'sql/workbench/browser/actions';
import { ItemContextKey } from 'sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerTreeContext';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
export const QueryEditorVisibleCondition = ContextKeyExpr.has(queryContext.queryEditorVisibleId);
export const ResultsGridFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(queryContext.resultsVisibleId), ContextKeyExpr.has(queryContext.resultsGridFocussedId));
@@ -58,17 +55,7 @@ Registry.as<IEditorInputFactoryRegistry>(EditorInputFactoryExtensions.EditorInpu
.registerEditorInputFactory(UntitledQueryEditorInput.ID, UntitledQueryEditorInputFactory);
Registry.as<ILanguageAssociationRegistry>(LanguageAssociationExtensions.LanguageAssociations)
.registerLanguageAssociation('sql', (accessor, editor) => {
const instantiationService = accessor.get(IInstantiationService);
const queryResultsInput = instantiationService.createInstance(QueryResultsInput, editor.getResource().toString(true));
if (editor instanceof FileEditorInput) {
return instantiationService.createInstance(FileQueryEditorInput, '', editor, queryResultsInput);
} else if (editor instanceof UntitledTextEditorInput) {
return instantiationService.createInstance(UntitledQueryEditorInput, '', editor, queryResultsInput);
} else {
return undefined;
}
}, (editor: QueryEditorInput) => editor.text, true);
.registerLanguageAssociation(QueryEditorLanguageAssociation.languages, QueryEditorLanguageAssociation, QueryEditorLanguageAssociation.isDefault);
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(new EditorDescriptor(QueryResultsEditor, QueryResultsEditor.ID, localize('queryResultsEditor.name', "Query Results")), [new SyncDescriptor(QueryResultsInput)]);

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, IEditorInput } from 'vs/workbench/common/editor';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { QueryResultsInput } from 'sql/workbench/contrib/query/common/queryResultsInput';
@@ -12,9 +12,33 @@ import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/unt
import { FileQueryEditorInput } from 'sql/workbench/contrib/query/common/fileQueryEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { ILanguageAssociation } from 'sql/workbench/common/languageAssociation';
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
const editorInputFactoryRegistry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
export class QueryEditorLanguageAssociation implements ILanguageAssociation {
static readonly isDefault = true;
static readonly languages = ['sql'];
constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) { }
convertInput(activeEditor: IEditorInput): QueryEditorInput {
const queryResultsInput = this.instantiationService.createInstance(QueryResultsInput, activeEditor.getResource().toString(true));
if (activeEditor instanceof FileEditorInput) {
return this.instantiationService.createInstance(FileQueryEditorInput, '', activeEditor, queryResultsInput);
} else if (activeEditor instanceof UntitledTextEditorInput) {
return this.instantiationService.createInstance(UntitledQueryEditorInput, '', activeEditor, queryResultsInput);
} else {
return undefined;
}
}
createBase(activeEditor: QueryEditorInput): IEditorInput {
return activeEditor.text;
}
}
export class FileQueryEditorInputFactory implements IEditorInputFactory {
serialize(editorInput: FileQueryEditorInput): string {
const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID);

View File

@@ -3,13 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { QueryPlanInput } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
import { QueryPlanInput, QueryPlanConverter } from 'sql/workbench/contrib/queryPlan/common/queryPlanInput';
import { EditorDescriptor, IEditorRegistry, Extensions } from 'vs/workbench/browser/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/common/languageAssociation';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// Query Plan editor registration
@@ -23,7 +22,4 @@ Registry.as<IEditorRegistry>(Extensions.Editors)
.registerEditor(queryPlanEditorDescriptor, [new SyncDescriptor(QueryPlanInput)]);
Registry.as<ILanguageAssociationRegistry>(LanguageAssociationExtensions.LanguageAssociations)
.registerLanguageAssociation('sqlplan', (accessor, editor) => {
const instantiationService = accessor.get(IInstantiationService);
return instantiationService.createInstance(QueryPlanInput, editor.getResource());
}, (editor: QueryPlanInput) => undefined);
.registerLanguageAssociation(QueryPlanConverter.languages, QueryPlanConverter);

View File

@@ -3,11 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EditorInput, EditorModel } from 'vs/workbench/common/editor';
import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/editor';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILanguageAssociation } from 'sql/workbench/common/languageAssociation';
export class QueryPlanConverter implements ILanguageAssociation {
static readonly languages = ['sqlplan'];
constructor(@IInstantiationService private instantiationService: IInstantiationService) { }
convertInput(activeEditor: IEditorInput): QueryPlanInput {
return this.instantiationService.createInstance(QueryPlanInput, activeEditor.getResource());
}
createBase(activeEditor: QueryPlanInput): IEditorInput {
return undefined;
}
}
export class QueryPlanInput extends EditorInput {

View File

@@ -0,0 +1,256 @@
/*---------------------------------------------------------------------------------------------
* 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/common/editorReplacerContribution';
import { TestEditorService } from 'vs/workbench/test/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 } 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/contrib/query/common/queryEditorInput';
import { Registry } from 'vs/platform/registry/common/platform';
import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/common/languageAssociation';
import { QueryEditorLanguageAssociation } from 'sql/workbench/contrib/query/common/queryInputFactory';
import { workbenchInstantiationService } from 'sql/workbench/test/workbenchTestServices';
import { NotebookEditorInputAssociation } from 'sql/workbench/contrib/notebook/common/models/nodebookInputFactory';
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs';
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());
instantiationService.invokeFunction(accessor => {
languageAssociations.start(accessor);
});
});
teardown(() => {
disposables = dispose(disposables);
});
test('does proper lifecycle', () => {
const editorService = new MockEditorService();
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 editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.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 sql file input using input mode', async () => {
const editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.other'), undefined, 'sql');
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 mode', async () => {
const editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.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 input using input mode', async () => {
const editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const input = instantiationService.createInstance(FileEditorInput, URI.file('/test/file.iynb'), undefined, 'notebook');
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 input using input mode', async () => {
const editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const input = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file'), false, 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('does not replace editors that it shouldnt', async () => {
const editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const untitled = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file'), false, undefined, undefined, undefined);
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 editorService = new MockEditorService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
const input = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file.unknown'), false, undefined, undefined, undefined);
const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup);
assert(response === undefined);
contrib.dispose();
});
});
class MockEditorService extends TestEditorService {
readonly overridenOpens: IOpenEditorOverrideHandler[] = [];
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(editor, options, group)) {
return response;
}
}
return undefined;
}
openEditor(_editor: any, _options?: any, _group?: any): Promise<any> {
return Promise.resolve(_editor);
}
}
class TestModeService implements IModeService {
_serviceBrand: undefined;
onDidCreateMode: Event<IMode>;
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.');
}
}

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITestInstantiationService, workbenchInstantiationService as vsworkbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { TestQueryModelService } from 'sql/platform/query/test/common/testQueryModelService';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
export function workbenchInstantiationService(): ITestInstantiationService {
const instantiationService = vsworkbenchInstantiationService();
instantiationService.stub(IConnectionManagementService, new TestConnectionManagementService());
instantiationService.stub(IQueryModelService, new TestQueryModelService());
return instantiationService;
}