From 9b90dbb53f54e7b3cfa09c1fef1030a5ca65eb74 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 19 Jul 2022 15:42:20 -0400 Subject: [PATCH] Switch to using object for editor resolver --- .../customEditor/browser/customEditors.ts | 25 +-- .../browser/interactive.contribution.ts | 40 ++--- .../mergeEditor/browser/view/mergeEditor.ts | 92 +++++------ .../notebook/browser/notebookServiceImpl.ts | 47 +++++- .../test/browser/notebookServiceImpl.test.ts | 4 +- .../common/preferencesContribution.ts | 44 +++--- .../browser/searchEditor.contribution.ts | 7 +- .../browser/terminalMainContribution.ts | 35 ++--- .../editor/browser/editorResolverService.ts | 62 ++------ .../editor/common/editorResolverService.ts | 18 +-- .../browser/editorResolverService.test.ts | 144 ++++++++++-------- .../editor/test/browser/editorService.test.ts | 97 +++++++----- .../textfile/common/textEditorService.ts | 8 +- 13 files changed, 335 insertions(+), 288 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index db0ce90b3b1e91..3be7a7f1b1641a 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -26,7 +26,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { CONTEXT_ACTIVE_CUSTOM_EDITOR_ID, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorResolverService, IEditorType, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, IEditorResolverService, IEditorType, RegisteredEditorPriority, UntitledEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ContributedCustomEditors } from '../common/contributedCustomEditors'; import { CustomEditorInput } from './customEditorInput'; @@ -116,6 +116,17 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ if (!globPattern.filenamePattern) { continue; } + + const editorInputFactory: EditorInputFactoryFunction = ({ resource }, group) => { + return { editor: CustomEditorInput.create(this.instantiationService, resource, contributedEditor.id, group.id) }; + }; + const untitledEditorInputFactory: UntitledEditorInputFactoryFunction = ({ resource }, group) => { + return { editor: CustomEditorInput.create(this.instantiationService, resource ?? URI.from({ scheme: Schemas.untitled, authority: `Untitled-${this._untitledCounter++}` }), contributedEditor.id, group.id) }; + }; + const diffEditorInputFactory: DiffEditorInputFactoryFunction = (diffEditorInput, group) => { + return { editor: this.createDiffEditorInput(diffEditorInput, contributedEditor.id, group) }; + }; + this._editorResolverDisposables.add(this.editorResolverService.registerEditor( globPattern.filenamePattern, { @@ -127,14 +138,10 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ { singlePerResource: () => !this.getCustomEditorCapabilities(contributedEditor.id)?.supportsMultipleEditorsPerDocument ?? true }, - ({ resource }, group) => { - return { editor: CustomEditorInput.create(this.instantiationService, resource, contributedEditor.id, group.id) }; - }, - ({ resource }, group) => { - return { editor: CustomEditorInput.create(this.instantiationService, resource ?? URI.from({ scheme: Schemas.untitled, authority: `Untitled-${this._untitledCounter++}` }), contributedEditor.id, group.id) }; - }, - (diffEditorInput, group) => { - return { editor: this.createDiffEditorInput(diffEditorInput, contributedEditor.id, group) }; + { + createEditorInput: editorInputFactory, + createUntitledEditorInput: untitledEditorInputFactory, + createDiffEditorInput: diffEditorInputFactory, } )); } diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 9969bd9c645635..c6442b9ce9cfea 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -203,9 +203,11 @@ export class InteractiveDocumentContribution extends Disposable implements IWork canSupportResource: uri => uri.scheme === Schemas.vscodeInteractiveInput, singlePerResource: true }, - ({ resource }) => { - const editorInput = editorService.getEditors(EditorsOrder.SEQUENTIAL).find(editor => editor.editor instanceof InteractiveEditorInput && editor.editor.inputResource.toString() === resource.toString()); - return editorInput!; + { + createEditorInput: ({ resource }) => { + const editorInput = editorService.getEditors(EditorsOrder.SEQUENTIAL).find(editor => editor.editor instanceof InteractiveEditorInput && editor.editor.inputResource.toString() === resource.toString()); + return editorInput!; + } } ); @@ -220,23 +222,25 @@ export class InteractiveDocumentContribution extends Disposable implements IWork canSupportResource: uri => uri.scheme === Schemas.vscodeInteractive || (uri.scheme === Schemas.vscodeNotebookCell && extname(uri) === '.interactive'), singlePerResource: true }, - ({ resource, options }) => { - const data = CellUri.parse(resource); - let notebookUri: URI = resource; - let cellOptions: IResourceEditorInput | undefined; - - if (data) { - notebookUri = data.notebook; - cellOptions = { resource, options }; - } + { + createEditorInput: ({ resource, options }) => { + const data = CellUri.parse(resource); + let notebookUri: URI = resource; + let cellOptions: IResourceEditorInput | undefined; + + if (data) { + notebookUri = data.notebook; + cellOptions = { resource, options }; + } - const notebookOptions = { ...options, cellOptions } as INotebookEditorOptions; + const notebookOptions = { ...options, cellOptions } as INotebookEditorOptions; - const editorInput = editorService.getEditors(EditorsOrder.SEQUENTIAL).find(editor => editor.editor instanceof InteractiveEditorInput && editor.editor.resource?.toString() === notebookUri.toString()); - return { - editor: editorInput!.editor, - options: notebookOptions - }; + const editorInput = editorService.getEditors(EditorsOrder.SEQUENTIAL).find(editor => editor.editor instanceof InteractiveEditorInput && editor.editor.resource?.toString() === notebookUri.toString()); + return { + editor: editorInput!.editor, + options: notebookOptions + }; + } } ); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 9212069bb61a0f..4f71c690a1bed9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -47,7 +47,7 @@ import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/v import { ctxBaseResourceScheme, ctxIsMergeEditor, ctxMergeEditorLayout, MergeEditorLayoutTypes } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { EditorInputFactoryFunction, IEditorResolverService, MergeEditorInputFactoryFunction, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import './colors'; import { InputCodeEditorView } from './editors/inputCodeEditorView'; @@ -510,6 +510,50 @@ export class MergeEditorResolverContribution extends Disposable { ) { super(); + const editorInputFactory: EditorInputFactoryFunction = (editor) => { + return { + editor: instantiationService.createInstance( + MergeEditorInput, + editor.resource, + { + uri: editor.resource, + title: '', + description: '', + detail: '' + }, + { + uri: editor.resource, + title: '', + description: '', + detail: '' + }, + editor.resource + ) + }; + }; + + const mergeEditorInputFactory: MergeEditorInputFactoryFunction = (mergeEditor: IResourceMergeEditorInput): EditorInputWithOptions => { + return { + editor: instantiationService.createInstance( + MergeEditorInput, + mergeEditor.base.resource, + { + uri: mergeEditor.input1.resource, + title: basename(mergeEditor.input1.resource), + description: '', + detail: '' + }, + { + uri: mergeEditor.input2.resource, + title: basename(mergeEditor.input2.resource), + description: '', + detail: '' + }, + mergeEditor.result.resource + ) + }; + }; + this._register(editorResolverService.registerEditor( `*`, { @@ -519,49 +563,9 @@ export class MergeEditorResolverContribution extends Disposable { priority: RegisteredEditorPriority.option }, {}, - (editor) => { - return { - editor: instantiationService.createInstance( - MergeEditorInput, - editor.resource, - { - uri: editor.resource, - title: '', - description: '', - detail: '' - }, - { - uri: editor.resource, - title: '', - description: '', - detail: '' - }, - editor.resource - ) - }; - }, - undefined, - undefined, - (mergeEditor: IResourceMergeEditorInput): EditorInputWithOptions => { - return { - editor: instantiationService.createInstance( - MergeEditorInput, - mergeEditor.base.resource, - { - uri: mergeEditor.input1.resource, - title: basename(mergeEditor.input1.resource), - description: '', - detail: '' - }, - { - uri: mergeEditor.input2.resource, - title: basename(mergeEditor.input2.resource), - description: '', - detail: '' - }, - mergeEditor.result.resource - ) - }; + { + createEditorInput: editorInputFactory, + createMergeEditorInput: mergeEditorInputFactory } )); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index e93082dac76e44..8d10507c8748fb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -35,7 +35,7 @@ import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/not import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, IEditorResolverService, IEditorType, RegisteredEditorInfo, RegisteredEditorPriority, UntitledEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService'; +import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, EditorInputFactoryObject, IEditorResolverService, IEditorType, RegisteredEditorInfo, RegisteredEditorPriority, UntitledEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -195,23 +195,56 @@ export class NotebookProviderInfoStore extends Disposable { const notebookDiffEditorInputFactory: DiffEditorInputFactoryFunction = ({ modified, original, label, description }) => { return { editor: NotebookDiffEditorInput.create(this._instantiationService, modified.resource!, label, description, original.resource!, notebookProviderInfo.id) }; }; + + const notebookFactoryObject: EditorInputFactoryObject = { + createEditorInput: notebookEditorInputFactory, + createDiffEditorInput: notebookDiffEditorInputFactory, + createUntitledEditorInput: notebookUntitledEditorFactory, + }; + const notebookCellFactoryObject: EditorInputFactoryObject = { + createEditorInput: notebookEditorInputFactory, + createDiffEditorInput: notebookDiffEditorInputFactory, + }; + + // TODO @lramos15 find a better way to toggle handling diff editors than needing these listeners for every registration + // This is a lot of event listeners especially if there are many notebooks + disposables.add(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(NotebookSetting.textDiffEditorPreview)) { + const canHandleDiff = !!this._configurationService.getValue(NotebookSetting.textDiffEditorPreview) && !this._accessibilityService.isScreenReaderOptimized(); + if (canHandleDiff) { + notebookFactoryObject.createDiffEditorInput = notebookDiffEditorInputFactory; + notebookCellFactoryObject.createDiffEditorInput = notebookDiffEditorInputFactory; + } else { + notebookFactoryObject.createDiffEditorInput = undefined; + notebookCellFactoryObject.createDiffEditorInput = undefined; + } + } + })); + + disposables.add(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { + const canHandleDiff = !!this._configurationService.getValue(NotebookSetting.textDiffEditorPreview) && !this._accessibilityService.isScreenReaderOptimized(); + if (canHandleDiff) { + notebookFactoryObject.createDiffEditorInput = notebookDiffEditorInputFactory; + notebookCellFactoryObject.createDiffEditorInput = notebookDiffEditorInputFactory; + } else { + notebookFactoryObject.createDiffEditorInput = undefined; + notebookCellFactoryObject.createDiffEditorInput = undefined; + } + })); + // Register the notebook editor disposables.add(this._editorResolverService.registerEditor( globPattern, notebookEditorInfo, notebookEditorOptions, - notebookEditorInputFactory, - notebookUntitledEditorFactory, - notebookDiffEditorInputFactory + notebookFactoryObject, )); // Then register the schema handler as exclusive for that notebook disposables.add(this._editorResolverService.registerEditor( `${Schemas.vscodeNotebookCell}:/**/${globPattern}`, { ...notebookEditorInfo, priority: RegisteredEditorPriority.exclusive }, notebookEditorOptions, - notebookEditorInputFactory, - undefined, - notebookDiffEditorInputFactory + notebookCellFactoryObject )); } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts index 575b935cfc1345..32e71906a47a82 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts @@ -35,7 +35,9 @@ suite('NotebookProviderInfoStore', function () { }, instantiationService.createInstance(EditorResolverService), new TestConfigurationService(), - new class extends mock() { }, + new class extends mock() { + override onDidChangeScreenReaderOptimized: Event = Event.None; + }, instantiationService, new class extends mock() { override hasProvider() { return true; } diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index be64900c807869..b0f2ac1333d661 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -66,35 +66,35 @@ export class PreferencesContribution implements IWorkbenchContribution { label: nls.localize('splitSettingsEditorLabel', "Split Settings Editor"), priority: RegisteredEditorPriority.builtin, }, + {}, { - canHandleDiff: false, - }, - ({ resource, options }): EditorInputWithOptions => { - // Global User Settings File - if (isEqual(resource, this.userDataProfileService.currentProfile.settingsResource)) { - return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.USER_LOCAL, resource), options }; - } + createEditorInput: ({ resource, options }): EditorInputWithOptions => { + // Global User Settings File + if (isEqual(resource, this.userDataProfileService.currentProfile.settingsResource)) { + return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.USER_LOCAL, resource), options }; + } - // Single Folder Workspace Settings File - const state = this.workspaceService.getWorkbenchState(); - if (state === WorkbenchState.FOLDER) { - const folders = this.workspaceService.getWorkspace().folders; - if (isEqual(resource, folders[0].toResource(FOLDER_SETTINGS_PATH))) { - return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.WORKSPACE, resource), options }; + // Single Folder Workspace Settings File + const state = this.workspaceService.getWorkbenchState(); + if (state === WorkbenchState.FOLDER) { + const folders = this.workspaceService.getWorkspace().folders; + if (isEqual(resource, folders[0].toResource(FOLDER_SETTINGS_PATH))) { + return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.WORKSPACE, resource), options }; + } } - } - // Multi Folder Workspace Settings File - else if (state === WorkbenchState.WORKSPACE) { - const folders = this.workspaceService.getWorkspace().folders; - for (const folder of folders) { - if (isEqual(resource, folder.toResource(FOLDER_SETTINGS_PATH))) { - return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.WORKSPACE_FOLDER, resource), options }; + // Multi Folder Workspace Settings File + else if (state === WorkbenchState.WORKSPACE) { + const folders = this.workspaceService.getWorkspace().folders; + for (const folder of folders) { + if (isEqual(resource, folder.toResource(FOLDER_SETTINGS_PATH))) { + return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.WORKSPACE_FOLDER, resource), options }; + } } } - } - return { editor: this.textEditorService.createTextEditor({ resource }), options }; + return { editor: this.textEditorService.createTextEditor({ resource }), options }; + } } ); } diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index a75b4184bd21d3..a8a22c38b18a9a 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -88,11 +88,12 @@ class SearchEditorContribution implements IWorkbenchContribution { }, { singlePerResource: true, - canHandleDiff: false, canSupportResource: resource => (extname(resource) === SEARCH_EDITOR_EXT) }, - ({ resource }) => { - return { editor: instantiationService.invokeFunction(getOrMakeSearchEditorInput, { from: 'existingFile', fileUri: resource }) }; + { + createEditorInput: ({ resource }) => { + return { editor: instantiationService.invokeFunction(getOrMakeSearchEditorInput, { from: 'existingFile', fileUri: resource }) }; + } } ); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts index edd1bf0aedafee..dac305a6c634cf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -46,27 +46,28 @@ export class TerminalMainContribution extends Disposable implements IWorkbenchCo priority: RegisteredEditorPriority.exclusive }, { - canHandleDiff: false, canSupportResource: uri => uri.scheme === Schemas.vscodeTerminal, singlePerResource: true }, - ({ resource, options }) => { - const instance = terminalService.getInstanceFromResource(resource); - if (instance) { - const sourceGroup = terminalGroupService.getGroupForInstance(instance); - sourceGroup?.removeInstance(instance); - } - const resolvedResource = terminalEditorService.resolveResource(instance || resource); - const editor = terminalEditorService.getInputFromResource(resolvedResource) || { editor: resolvedResource }; - return { - editor, - options: { - ...options, - pinned: true, - forceReload: true, - override: terminalEditorId + { + createEditorInput: ({ resource, options }) => { + const instance = terminalService.getInstanceFromResource(resource); + if (instance) { + const sourceGroup = terminalGroupService.getGroupForInstance(instance); + sourceGroup?.removeInstance(instance); } - }; + const resolvedResource = terminalEditorService.resolveResource(instance || resource); + const editor = terminalEditorService.getInputFromResource(resolvedResource) || { editor: resolvedResource }; + return { + editor, + options: { + ...options, + pinned: true, + forceReload: true, + override: terminalEditorId + } + }; + } } ); diff --git a/src/vs/workbench/services/editor/browser/editorResolverService.ts b/src/vs/workbench/services/editor/browser/editorResolverService.ts index 666798d3beaace..ec9fc2b563b501 100644 --- a/src/vs/workbench/services/editor/browser/editorResolverService.ts +++ b/src/vs/workbench/services/editor/browser/editorResolverService.ts @@ -14,7 +14,7 @@ import { DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, EditorInputWithOpti import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { Schemas } from 'vs/base/common/network'; -import { RegisteredEditorInfo, RegisteredEditorPriority, RegisteredEditorOptions, DiffEditorInputFactoryFunction, EditorAssociation, EditorAssociations, EditorInputFactoryFunction, editorsAssociationsSettingId, globMatchesResource, IEditorResolverService, priorityToRank, ResolvedEditor, ResolvedStatus, UntitledEditorInputFactoryFunction, MergeEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService'; +import { RegisteredEditorInfo, RegisteredEditorPriority, RegisteredEditorOptions, EditorAssociation, EditorAssociations, editorsAssociationsSettingId, globMatchesResource, IEditorResolverService, priorityToRank, ResolvedEditor, ResolvedStatus, EditorInputFactoryObject } from 'vs/workbench/services/editor/common/editorResolverService'; import { IKeyMods, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { localize } from 'vs/nls'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -33,10 +33,7 @@ interface RegisteredEditor { globPattern: string | glob.IRelativePattern; editorInfo: RegisteredEditorInfo; options?: RegisteredEditorOptions; - createEditorInput: EditorInputFactoryFunction; - createUntitledEditorInput?: UntitledEditorInputFactoryFunction; - createDiffEditorInput?: DiffEditorInputFactoryFunction; - createMergeEditorInput?: MergeEditorInputFactoryFunction; + editorFactoryObject: EditorInputFactoryObject; } type RegisteredEditors = Array; @@ -72,7 +69,6 @@ export class EditorResolverService extends Disposable implements IEditorResolver // Read in the cache on statup this.cache = new Set(JSON.parse(this.storageService.get(EditorResolverService.cacheStorageID, StorageScope.PROFILE, JSON.stringify([])))); this.storageService.remove(EditorResolverService.cacheStorageID, StorageScope.PROFILE); - this.convertOldAssociationFormat(); this._register(this.storageService.onWillSaveState(() => { // We want to store the glob patterns we would activate on, this allows us to know if we need to await the ext host on startup for opening a resource @@ -83,13 +79,6 @@ export class EditorResolverService extends Disposable implements IEditorResolver this.extensionService.onDidRegisterExtensions(() => { this.cache = undefined; }); - - // When the setting changes we want to ensure that it is properly converted - this._register(this.configurationService.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration(editorsAssociationsSettingId)) { - this.convertOldAssociationFormat(); - } - })); } private resolveUntypedInputAndGroup(editor: EditorInputWithOptions | IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): [IUntypedEditorInput, IEditorGroup, EditorActivation | undefined] | undefined { @@ -195,10 +184,8 @@ export class EditorResolverService extends Disposable implements IEditorResolver // If no override we take the selected editor id so that matches works with the isActive check untypedEditor.options = { override: selectedEditor.editorInfo.id, ...untypedEditor.options }; - let handlesDiff = typeof selectedEditor.options?.canHandleDiff === 'function' ? selectedEditor.options.canHandleDiff() : selectedEditor.options?.canHandleDiff; - // Also check that it has a factory function or else it doesn't matter - handlesDiff = handlesDiff && selectedEditor.createDiffEditorInput !== undefined; - if (handlesDiff === false && isResourceDiffEditorInput(untypedEditor)) { + // Check if diff can be created based on prescene of factory function + if (selectedEditor.editorFactoryObject.createDiffEditorInput === undefined && isResourceDiffEditorInput(untypedEditor)) { return ResolvedStatus.NONE; } @@ -244,10 +231,7 @@ export class EditorResolverService extends Disposable implements IEditorResolver globPattern: string | glob.IRelativePattern, editorInfo: RegisteredEditorInfo, options: RegisteredEditorOptions, - createEditorInput: EditorInputFactoryFunction, - createUntitledEditorInput?: UntitledEditorInputFactoryFunction, - createDiffEditorInput?: DiffEditorInputFactoryFunction, - createMergeEditorInput?: MergeEditorInputFactoryFunction + editorFactoryObject: EditorInputFactoryObject ): IDisposable { let registeredEditor = this._editors.get(globPattern); if (registeredEditor === undefined) { @@ -258,10 +242,7 @@ export class EditorResolverService extends Disposable implements IEditorResolver globPattern, editorInfo, options, - createEditorInput, - createUntitledEditorInput, - createDiffEditorInput, - createMergeEditorInput + editorFactoryObject }); this._onDidChangeEditorRegistrations.fire(); return toDisposable(() => { @@ -278,23 +259,6 @@ export class EditorResolverService extends Disposable implements IEditorResolver return matchingAssociations.filter(association => allEditors.find(c => c.editorInfo.id === association.viewType)); } - private convertOldAssociationFormat(): void { - const rawAssociations = this.configurationService.getValue(editorsAssociationsSettingId) || []; - // If it's not an array, then it's the new format - if (!Array.isArray(rawAssociations)) { - return; - } - const newSettingObject = Object.create(null); - // Make the correctly formatted object from the array and then set that object - for (const association of rawAssociations) { - if (association.filenamePattern) { - newSettingObject[association.filenamePattern] = association.viewType; - } - } - this.logService.info(`Migrating ${editorsAssociationsSettingId}`); - this.configurationService.updateValue(editorsAssociationsSettingId, newSettingObject); - } - private getAllUserAssociations(): EditorAssociations { const inspectedEditorAssociations = this.configurationService.inspect<{ [fileNamePattern: string]: string }>(editorsAssociationsSettingId) || {}; const workspaceAssociations = inspectedEditorAssociations.workspaceValue ?? {}; @@ -440,19 +404,19 @@ export class EditorResolverService extends Disposable implements IEditorResolver // If it's a merge editor we trigger the create merge editor input if (isResourceMergeEditorInput(editor)) { - if (!selectedEditor.createMergeEditorInput) { + if (!selectedEditor.editorFactoryObject.createMergeEditorInput) { return; } - const inputWithOptions = await selectedEditor.createMergeEditorInput(editor, group); + const inputWithOptions = await selectedEditor.editorFactoryObject.createMergeEditorInput(editor, group); return { editor: inputWithOptions.editor, options: inputWithOptions.options ?? options }; } // If it's a diff editor we trigger the create diff editor input if (isResourceDiffEditorInput(editor)) { - if (!selectedEditor.createDiffEditorInput) { + if (!selectedEditor.editorFactoryObject.createDiffEditorInput) { return; } - const inputWithOptions = await selectedEditor.createDiffEditorInput(editor, group); + const inputWithOptions = await selectedEditor.editorFactoryObject.createDiffEditorInput(editor, group); return { editor: inputWithOptions.editor, options: inputWithOptions.options ?? options }; } @@ -461,10 +425,10 @@ export class EditorResolverService extends Disposable implements IEditorResolver } if (isUntitledResourceEditorInput(editor)) { - if (!selectedEditor.createUntitledEditorInput) { + if (!selectedEditor.editorFactoryObject.createUntitledEditorInput) { return; } - const inputWithOptions = await selectedEditor.createUntitledEditorInput(editor, group); + const inputWithOptions = await selectedEditor.editorFactoryObject.createUntitledEditorInput(editor, group); return { editor: inputWithOptions.editor, options: inputWithOptions.options ?? options }; } @@ -483,7 +447,7 @@ export class EditorResolverService extends Disposable implements IEditorResolver } // Respect options passed back - const inputWithOptions = await selectedEditor.createEditorInput(editor, group); + const inputWithOptions = await selectedEditor.editorFactoryObject.createEditorInput(editor, group); options = inputWithOptions.options ?? options; const input = inputWithOptions.editor; diff --git a/src/vs/workbench/services/editor/common/editorResolverService.ts b/src/vs/workbench/services/editor/common/editorResolverService.ts index 57f3e2c1508aad..b4e47e8d4f2360 100644 --- a/src/vs/workbench/services/editor/common/editorResolverService.ts +++ b/src/vs/workbench/services/editor/common/editorResolverService.ts @@ -84,10 +84,6 @@ export type RegisteredEditorOptions = { * If your editor cannot be opened in multiple groups for the same resource */ singlePerResource?: boolean | (() => boolean); - /** - * If your editor supports diffs - */ - canHandleDiff?: boolean | (() => boolean); /** * Whether or not you can support opening the given resource. @@ -113,6 +109,13 @@ export type DiffEditorInputFactoryFunction = (diffEditorInput: IResourceDiffEdit export type MergeEditorInputFactoryFunction = (mergeEditorInput: IResourceMergeEditorInput, group: IEditorGroup) => EditorInputFactoryResult; +export type EditorInputFactoryObject = { + createEditorInput: EditorInputFactoryFunction; + createUntitledEditorInput?: UntitledEditorInputFactoryFunction; + createDiffEditorInput?: DiffEditorInputFactoryFunction; + createMergeEditorInput?: MergeEditorInputFactoryFunction; +}; + export interface IEditorResolverService { readonly _serviceBrand: undefined; /** @@ -139,16 +142,13 @@ export interface IEditorResolverService { * @param globPattern The glob pattern for this registration * @param editorInfo Information about the registration * @param options Specific options which apply to this registration - * @param createEditorInput The factory method for creating inputs + * @param editorFactoryObject The editor input factory functions */ registerEditor( globPattern: string | glob.IRelativePattern, editorInfo: RegisteredEditorInfo, options: RegisteredEditorOptions, - createEditorInput: EditorInputFactoryFunction, - createUntitledEditorInput?: UntitledEditorInputFactoryFunction, - createDiffEditorInput?: DiffEditorInputFactoryFunction, - createMergeEditorInput?: MergeEditorInputFactoryFunction + editorFactoryObject: EditorInputFactoryObject ): IDisposable; /** diff --git a/src/vs/workbench/services/editor/test/browser/editorResolverService.test.ts b/src/vs/workbench/services/editor/test/browser/editorResolverService.test.ts index 3158684613d724..5bfe40ad314d0b 100644 --- a/src/vs/workbench/services/editor/test/browser/editorResolverService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorResolverService.test.ts @@ -40,8 +40,10 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.default }, - { canHandleDiff: false }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + } ); const resultingResolution = await service.resolveEditor({ resource: URI.file('my://resource-basics.test') }, part.activeGroup); @@ -64,9 +66,11 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.default }, - { canHandleDiff: false }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), - ({ resource, options }, group) => ({ editor: new TestFileEditorInput((resource ? resource : URI.from({ scheme: Schemas.untitled })), UNTITLED_TEST_EDITOR_INPUT_ID) }), + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + createUntitledEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput((resource ? resource : URI.from({ scheme: Schemas.untitled })), UNTITLED_TEST_EDITOR_INPUT_ID) }), + } ); // Untyped untitled - no resource @@ -105,8 +109,10 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details Primary', priority: RegisteredEditorPriority.default }, - { canHandleDiff: false }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + } ); const registeredEditorSecondary = service.registerEditor('*.test-secondary', @@ -116,8 +122,10 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details Secondary', priority: RegisteredEditorPriority.default }, - { canHandleDiff: false }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + } ); const resultingResolution = await service.resolveEditor({ @@ -145,18 +153,19 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.default }, - { canHandleDiff: true }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), - undefined, - ({ modified, original, options }, group) => ({ - editor: accessor.instantiationService.createInstance( - DiffEditorInput, - 'name', - 'description', - new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), - new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), - undefined) - }) + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + createDiffEditorInput: ({ modified, original, options }, group) => ({ + editor: accessor.instantiationService.createInstance( + DiffEditorInput, + 'name', + 'description', + new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), + new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), + undefined) + }) + } ); const resultingResolution = await service.resolveEditor({ @@ -186,20 +195,21 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.default }, - { canHandleDiff: true }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), - undefined, - ({ modified, original, options }, group) => { - diffOneCounter++; - return { - editor: accessor.instantiationService.createInstance( - DiffEditorInput, - 'name', - 'description', - new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), - new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), - undefined) - }; + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + createDiffEditorInput: ({ modified, original, options }, group) => { + diffOneCounter++; + return { + editor: accessor.instantiationService.createInstance( + DiffEditorInput, + 'name', + 'description', + new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), + new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), + undefined) + }; + } } ); @@ -210,20 +220,21 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.default }, - { canHandleDiff: true }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), - undefined, - ({ modified, original, options }, group) => { - diffTwoCounter++; - return { - editor: accessor.instantiationService.createInstance( - DiffEditorInput, - 'name', - 'description', - new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), - new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), - undefined) - }; + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + createDiffEditorInput: ({ modified, original, options }, group) => { + diffTwoCounter++; + return { + editor: accessor.instantiationService.createInstance( + DiffEditorInput, + 'name', + 'description', + new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), + new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), + undefined) + }; + } } ); @@ -234,20 +245,21 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.option }, - { canHandleDiff: true }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), - undefined, - ({ modified, original, options }, group) => { - defaultDiffCounter++; - return { - editor: accessor.instantiationService.createInstance( - DiffEditorInput, - 'name', - 'description', - new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), - new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), - undefined) - }; + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + createDiffEditorInput: ({ modified, original, options }, group) => { + defaultDiffCounter++; + return { + editor: accessor.instantiationService.createInstance( + DiffEditorInput, + 'name', + 'description', + new TestFileEditorInput(URI.parse(original.toString()), TEST_EDITOR_INPUT_ID), + new TestFileEditorInput(URI.parse(modified.toString()), TEST_EDITOR_INPUT_ID), + undefined) + }; + } } ); @@ -354,8 +366,10 @@ suite('EditorResolverService', () => { detail: 'Test Editor Details', priority: RegisteredEditorPriority.default }, - { canHandleDiff: false }, - ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }), + {}, + { + createEditorInput: ({ resource, options }, group) => ({ editor: new TestFileEditorInput(URI.parse(resource.toString()), TEST_EDITOR_INPUT_ID) }) + } ); assert.strictEqual(eventCounter, 1); diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index f193e7d69b02a6..e0c3b499650b8d 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -252,7 +252,9 @@ suite('EditorService', () => { '*.editor-service-locked-group-tests', { id: TEST_EDITOR_INPUT_ID, label: 'Label', priority: RegisteredEditorPriority.exclusive }, {}, - editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + { + createEditorInput: editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + } )); const input1: IResourceEditorInput = { resource: URI.parse('file://resource-basics.editor-service-locked-group-tests'), options: { pinned: true } }; @@ -388,7 +390,9 @@ suite('EditorService', () => { '*.editor-service-locked-group-tests', { id: TEST_EDITOR_INPUT_ID, label: 'Label', priority: RegisteredEditorPriority.exclusive }, {}, - editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + { + createEditorInput: editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + } )); const rootGroup = part.activeGroup; @@ -436,7 +440,9 @@ suite('EditorService', () => { '*.editor-service-locked-group-tests', { id: TEST_EDITOR_INPUT_ID, label: 'Label', priority: RegisteredEditorPriority.exclusive }, {}, - editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + { + createEditorInput: editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + } )); const rootGroup = part.activeGroup; @@ -484,7 +490,9 @@ suite('EditorService', () => { '*.editor-service-locked-group-tests', { id: TEST_EDITOR_INPUT_ID, label: 'Label', priority: RegisteredEditorPriority.exclusive }, {}, - editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + { + createEditorInput: editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + } )); const rootGroup = part.activeGroup; @@ -549,24 +557,26 @@ suite('EditorService', () => { disposables.add(accessor.editorResolverService.registerEditor( '*.editor-service-override-tests', { id: TEST_EDITOR_INPUT_ID, label: 'Label', priority: RegisteredEditorPriority.exclusive }, - { canHandleDiff: true }, - editor => { - editorFactoryCalled++; - lastEditorFactoryEditor = editor; - - return { editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }; - }, - untitledEditor => { - untitledEditorFactoryCalled++; - lastUntitledEditorFactoryEditor = untitledEditor; - - return { editor: new TestFileEditorInput(untitledEditor.resource ?? URI.parse(`untitled://my-untitled-editor-${untitledEditorFactoryCalled}`), TEST_EDITOR_INPUT_ID) }; - }, - diffEditor => { - diffEditorFactoryCalled++; - lastDiffEditorFactoryEditor = diffEditor; - - return { editor: new TestFileEditorInput(URI.file(`diff-editor-${diffEditorFactoryCalled}`), TEST_EDITOR_INPUT_ID) }; + {}, + { + createEditorInput: editor => { + editorFactoryCalled++; + lastEditorFactoryEditor = editor; + + return { editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }; + }, + createUntitledEditorInput: untitledEditor => { + untitledEditorFactoryCalled++; + lastUntitledEditorFactoryEditor = untitledEditor; + + return { editor: new TestFileEditorInput(untitledEditor.resource ?? URI.parse(`untitled://my-untitled-editor-${untitledEditorFactoryCalled}`), TEST_EDITOR_INPUT_ID) }; + }, + createDiffEditorInput: diffEditor => { + diffEditorFactoryCalled++; + lastDiffEditorFactoryEditor = diffEditor; + + return { editor: new TestFileEditorInput(URI.file(`diff-editor-${diffEditorFactoryCalled}`), TEST_EDITOR_INPUT_ID) }; + } } )); @@ -1401,7 +1411,9 @@ suite('EditorService', () => { '*.editor-service-override-tests', { id: TEST_EDITOR_INPUT_ID, label: 'Label', priority: RegisteredEditorPriority.exclusive }, {}, - editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + { + createEditorInput: editor => ({ editor: new TestFileEditorInput(editor.resource, TEST_EDITOR_INPUT_ID) }) + } )); // Typed editor @@ -2268,12 +2280,13 @@ suite('EditorService', () => { priority: RegisteredEditorPriority.builtin }, {}, - (editorInput) => { - editorCount++; - return ({ editor: textEditorService.createTextEditor(editorInput) }); - }, - undefined, - diffEditor => ({ editor: textEditorService.createTextEditor(diffEditor) }) + { + createEditorInput: (editorInput) => { + editorCount++; + return ({ editor: textEditorService.createTextEditor(editorInput) }); + }, + createDiffEditorInput: diffEditor => ({ editor: textEditorService.createTextEditor(diffEditor) }) + } ); assert.strictEqual(editorCount, 0); @@ -2311,12 +2324,13 @@ suite('EditorService', () => { priority: RegisteredEditorPriority.builtin }, {}, - (editorInput) => { - editorCount++; - return ({ editor: textEditorService.createTextEditor(editorInput) }); - }, - undefined, - diffEditor => ({ editor: textEditorService.createTextEditor(diffEditor) }) + { + createEditorInput: (editorInput) => { + editorCount++; + return ({ editor: textEditorService.createTextEditor(editorInput) }); + }, + createDiffEditorInput: diffEditor => ({ editor: textEditorService.createTextEditor(diffEditor) }) + } ); assert.strictEqual(editorCount, 0); @@ -2348,12 +2362,13 @@ suite('EditorService', () => { priority: RegisteredEditorPriority.builtin }, {}, - (editorInput) => { - editorCount++; - return ({ editor: textEditorService.createTextEditor(editorInput) }); - }, - undefined, - diffEditor => ({ editor: textEditorService.createTextEditor(diffEditor) }) + { + createEditorInput: (editorInput) => { + editorCount++; + return ({ editor: textEditorService.createTextEditor(editorInput) }); + }, + createDiffEditorInput: diffEditor => ({ editor: textEditorService.createTextEditor(diffEditor) }) + } ); assert.strictEqual(editorCount, 0); diff --git a/src/vs/workbench/services/textfile/common/textEditorService.ts b/src/vs/workbench/services/textfile/common/textEditorService.ts index a4a51339630d87..886acda45d2b4e 100644 --- a/src/vs/workbench/services/textfile/common/textEditorService.ts +++ b/src/vs/workbench/services/textfile/common/textEditorService.ts @@ -75,9 +75,11 @@ export class TextEditorService extends Disposable implements ITextEditorService priority: RegisteredEditorPriority.builtin }, {}, - editor => ({ editor: this.createTextEditor(editor) }), - untitledEditor => ({ editor: this.createTextEditor(untitledEditor) }), - diffEditor => ({ editor: this.createTextEditor(diffEditor) }) + { + createEditorInput: editor => ({ editor: this.createTextEditor(editor) }), + createUntitledEditorInput: untitledEditor => ({ editor: this.createTextEditor(untitledEditor) }), + createDiffEditorInput: diffEditor => ({ editor: this.createTextEditor(diffEditor) }) + } )); }