diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 24ac77635c96b2919b3bbb3bd02ab2b7393035f8..856a58552a839716b6af8986fcb395e7be509e4b 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -31,6 +31,7 @@ import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { MutableDisposable } from 'vs/base/common/lifecycle'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; /** * An implementation of editor for file system resources. @@ -49,14 +50,15 @@ export class TextFileEditor extends BaseTextEditor { @IInstantiationService instantiationService: IInstantiationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @ITextFileService private readonly textFileService: ITextFileService, - @IExplorerService private readonly explorerService: IExplorerService + @IExplorerService private readonly explorerService: IExplorerService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { - super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); + super(TextFileEditor.ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService); this.updateRestoreViewStateConfiguration(); @@ -87,7 +89,7 @@ export class TextFileEditor extends BaseTextEditor { } private updateRestoreViewStateConfiguration(): void { - this.restoreViewState = this.textResourceConfigurationService.getValue(undefined, 'workbench.editor.restoreViewState'); + this.restoreViewState = this.configurationService.getValue('workbench.editor.restoreViewState') ?? true /* default */; } getTitle(): string { diff --git a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts b/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts index 0390ba2cceb042bdeaed7ec16317b238b3171dc5..4ea06a470564a25f4d24741f0b81272b04d50c50 100644 --- a/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/electron-browser/textFileEditor.ts @@ -24,6 +24,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; /** * An implementation of editor for file system resources. @@ -37,16 +38,17 @@ export class NativeTextFileEditor extends TextFileEditor { @IInstantiationService instantiationService: IInstantiationService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, + @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @ITextFileService textFileService: ITextFileService, @IElectronService private readonly electronService: IElectronService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @IExplorerService explorerService: IExplorerService + @IExplorerService explorerService: IExplorerService, + @IConfigurationService configurationService: IConfigurationService ) { - super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, configurationService, editorService, themeService, editorGroupService, textFileService, explorerService); + super(telemetryService, fileService, viewletService, instantiationService, contextService, storageService, textResourceConfigurationService, editorService, themeService, editorGroupService, textFileService, explorerService, configurationService); } protected handleSetInputError(error: Error, input: FileEditorInput, options: EditorOptions | undefined): void { diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..a58dd887f65c73cd6c6ffb08788b4d0208dc2a37 --- /dev/null +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditor.test.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { toResource } from 'vs/base/test/common/utils'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; +import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; +import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { Selection } from 'vs/editor/common/core/selection'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; + +suite('Files - TextFileEditor', () => { + + let disposables: IDisposable[] = []; + + setup(() => { + disposables.push(Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + TextFileEditor, + TextFileEditor.ID, + 'Text File Editor' + ), + [new SyncDescriptor(FileEditorInput)] + )); + }); + + teardown(() => { + dispose(disposables); + disposables = []; + }); + + async function createPart(restoreViewState: boolean): Promise<[EditorPart, TestServiceAccessor, IInstantiationService, IEditorService]> { + const instantiationService = workbenchInstantiationService(); + + const configurationService = new TestConfigurationService(); + configurationService.setUserConfiguration('workbench', { editor: { restoreViewState } }); + instantiationService.stub(IConfigurationService, configurationService); + + instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + instantiationService.createInstance(MockContextKeyService), + configurationService, + TestEnvironmentService + )); + + const part = instantiationService.createInstance(EditorPart); + part.create(document.createElement('div')); + part.layout(400, 300); + + instantiationService.stub(IEditorGroupsService, part); + + const editorService: EditorService = instantiationService.createInstance(EditorService); + instantiationService.stub(IEditorService, editorService); + + const accessor = instantiationService.createInstance(TestServiceAccessor); + + await part.whenRestored; + + return [part, accessor, instantiationService, editorService]; + } + + test('text file editor preserves viewstate', async function () { + return viewStateTest(this, true); + }); + + test('text file editor resets viewstate if configured as such', async function () { + return viewStateTest(this, false); + }); + + async function viewStateTest(context: Mocha.ITestCallbackContext, restoreViewState: boolean): Promise { + const [part, accessor] = await createPart(restoreViewState); + + let editor = await accessor.editorService.openEditor(accessor.editorService.createInput({ resource: toResource.call(context, '/path/index.txt'), forceFile: true })); + + let codeEditor = editor?.getControl() as CodeEditorWidget; + const selection = new Selection(1, 3, 1, 4); + codeEditor.setSelection(selection); + + editor = await accessor.editorService.openEditor(accessor.editorService.createInput({ resource: toResource.call(context, '/path/index-other.txt'), forceFile: true })); + editor = await accessor.editorService.openEditor(accessor.editorService.createInput({ resource: toResource.call(context, '/path/index.txt'), forceFile: true })); + + codeEditor = editor?.getControl() as CodeEditorWidget; + + if (restoreViewState) { + assert.ok(codeEditor.getSelection()?.equalsSelection(selection)); + } else { + assert.ok(!codeEditor.getSelection()?.equalsSelection(selection)); + } + + part.dispose(); + (accessor.textFileService.files).dispose(); + } +}); 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 dc7c8c63fce8dabbc5910fba4c91a745a7de6a9a..26d6709a157189dce41fe53704fc8fcfc2f25979 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -8,7 +8,7 @@ import { EditorActivation } from 'vs/platform/editor/common/editor'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, EditorsOrder } from 'vs/workbench/common/editor'; +import { EditorInput, EditorsOrder, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { workbenchInstantiationService, TestStorageService, TestServiceAccessor, registerTestEditor, TestFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; @@ -26,6 +26,7 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; const TEST_EDITOR_ID = 'MyTestEditorForEditorService'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorService'; @@ -233,6 +234,10 @@ suite('EditorService', () => { let contentInput = input; assert.strictEqual(contentInput.resource.fsPath, toResource.call(this, '/index.html').fsPath); + // Typed Input + assert.equal(service.createInput(input), input); + assert.equal(service.createInput({ editor: input }), input); + // Untyped Input (file, encoding) input = service.createInput({ resource: toResource.call(this, '/index.html'), encoding: 'utf16le', options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof FileEditorInput); @@ -289,6 +294,20 @@ suite('EditorService', () => { // Untyped Input (resource) input = service.createInput({ resource: URI.parse('custom:resource') }); assert(input instanceof ResourceEditorInput); + + // Untyped Input (side by side) + input = service.createInput({ + masterResource: toResource.call(this, '/master.html'), + detailResource: toResource.call(this, '/detail.html') + }); + assert(input instanceof SideBySideEditorInput); + + // Untyped Input (diff) + input = service.createInput({ + leftResource: toResource.call(this, '/master.html'), + rightResource: toResource.call(this, '/detail.html') + }); + assert(input instanceof DiffEditorInput); }); test('delegate', function (done) { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 4e86c5a1bb37d491da7632ca4d3e1f28a335983d..8147ecb50bf1ab62bb69623244eab831e47ae325 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -10,7 +10,7 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor'; +import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult, ITextEditor, ITextDiffEditor, ITextSideBySideEditor } from 'vs/workbench/common/editor'; import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; @@ -18,7 +18,7 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IEditorOptions, IResourceInput, IEditorModel } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, IResourceInput, IEditorModel, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; @@ -51,8 +51,8 @@ import { IExtensionService, NullExtensionService } from 'vs/workbench/services/e import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService, IOpenEditorOverrideHandler, IVisibleEditor, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService, IOpenEditorOverrideHandler, IVisibleEditor, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditor, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; @@ -104,6 +104,8 @@ export import TestTextResourcePropertiesService = CommonWorkbenchTestServices.Te export import TestContextService = CommonWorkbenchTestServices.TestContextService; export import TestStorageService = CommonWorkbenchTestServices.TestStorageService; export import TestWorkingCopyService = CommonWorkbenchTestServices.TestWorkingCopyService; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IDiffEditor } from 'vs/editor/common/editorCommon'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); @@ -469,7 +471,7 @@ export class TestEditorGroupsService implements IEditorGroupsService { onDidLayout: Event = Event.None; onDidEditorPartOptionsChange = Event.None; - orientation: any; + orientation = GroupOrientation.HORIZONTAL; whenRestored: Promise = Promise.resolve(undefined); willRestoreEditors = false; @@ -488,7 +490,7 @@ export class TestEditorGroupsService implements IEditorGroupsService { setSize(_group: number | IEditorGroup, _size: { width: number, height: number }): void { } arrangeGroups(_arrangement: GroupsArrangement): void { } applyLayout(_layout: EditorGroupLayout): void { } - setGroupOrientation(_orientation: any): void { } + setGroupOrientation(_orientation: GroupOrientation): void { } addGroup(_location: number | IEditorGroup, _direction: GroupDirection, _options?: IAddGroupOptions): IEditorGroup { throw new Error('not implemented'); } removeGroup(_group: number | IEditorGroup): void { } moveGroup(_group: number | IEditorGroup, _location: number | IEditorGroup, _direction: GroupDirection): IEditorGroup { throw new Error('not implemented'); } @@ -591,10 +593,10 @@ export class TestEditorService implements EditorServiceImpl { onDidOpenEditorFail: Event = Event.None; onDidMostRecentlyActiveEditorsChange: Event = Event.None; - activeControl!: IVisibleEditor; - activeTextEditorWidget: any; - activeTextEditorMode: any; - activeEditor!: IEditorInput; + activeControl: IVisibleEditor | undefined; + activeTextEditorWidget: ICodeEditor | IDiffEditor | undefined; + activeTextEditorMode: string | undefined; + activeEditor: IEditorInput | undefined; editors: ReadonlyArray = []; mostRecentlyActiveEditors: ReadonlyArray = []; visibleControls: ReadonlyArray = []; @@ -606,7 +608,13 @@ export class TestEditorService implements EditorServiceImpl { getEditors() { return []; } overrideOpenEditor(_handler: IOpenEditorOverrideHandler): IDisposable { return toDisposable(() => undefined); } - openEditor(_editor: any, _options?: any, _group?: any): Promise { throw new Error('not implemented'); } + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledTextResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + async openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise { + throw new Error('not implemented'); + } doResolveEditorOpenRequest(editor: IEditorInput | IResourceEditor): [IEditorGroup, EditorInput, EditorOptions | undefined] | undefined { if (!this.editorGroupService) { return undefined; @@ -615,8 +623,7 @@ export class TestEditorService implements EditorServiceImpl { return [this.editorGroupService.activeGroup, editor as EditorInput, undefined]; } openEditors(_editors: any, _group?: any): Promise { throw new Error('not implemented'); } - isOpen(_editor: IEditorInput | IResourceInput | IUntitledTextResourceInput): boolean { return false; } - getOpened(_editor: IEditorInput | IResourceInput | IUntitledTextResourceInput): IEditorInput { throw new Error('not implemented'); } + isOpen(_editor: IEditorInput): boolean { return false; } replaceEditors(_editors: any, _group: any) { return Promise.resolve(undefined); } invokeWithinEditorContext(fn: (accessor: ServicesAccessor) => T): T { throw new Error('not implemented'); } createInput(_input: IResourceInput | IUntitledTextResourceInput | IResourceDiffInput | IResourceSideBySideInput): EditorInput { throw new Error('not implemented'); } @@ -946,7 +953,7 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor