mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-24 01:25:37 -05:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.');
|
||||
|
||||
@@ -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)]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
256
src/sql/workbench/test/common/editorReplacerContribution.test.ts
Normal file
256
src/sql/workbench/test/common/editorReplacerContribution.test.ts
Normal 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.');
|
||||
}
|
||||
}
|
||||
17
src/sql/workbench/test/workbenchTestServices.ts
Normal file
17
src/sql/workbench/test/workbenchTestServices.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user