mirror of
https://github.com/Microsoft/vscode
synced 2024-10-30 10:15:41 +00:00
history - implement more tests around in-editor selection changes
This commit is contained in:
parent
f83c30b73a
commit
cfaa9caf89
6 changed files with 147 additions and 42 deletions
|
@ -375,6 +375,7 @@ export class SideBySideEditor extends AbstractEditorWithViewState<ISideBySideEdi
|
|||
}
|
||||
|
||||
override setOptions(options: ISideBySideEditorOptions | undefined): void {
|
||||
super.setOptions(options);
|
||||
|
||||
// Update focus if target is provided
|
||||
if (typeof options?.target === 'number') {
|
||||
|
|
|
@ -194,6 +194,8 @@ export abstract class BaseTextEditor<T extends IEditorViewState> extends Abstrac
|
|||
}
|
||||
|
||||
override setOptions(options: ITextEditorOptions | undefined): void {
|
||||
super.setOptions(options);
|
||||
|
||||
if (options) {
|
||||
applyTextEditorOptions(options, assertIsDefined(this.getControl()), ScrollType.Smooth);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { EditorActivation, EditorResolution, IResourceEditorInput } from 'vs/pla
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { DEFAULT_EDITOR_ASSOCIATION, EditorCloseContext, EditorsOrder, IEditorCloseEvent, EditorInputWithOptions, IEditorPane, IResourceDiffEditorInput, isEditorInputWithOptions, IUntitledTextResourceEditorInput, IUntypedEditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart, registerTestFileEditor, TestEditorWithOptions, TestTextFileEditor, TestSingletonFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart, registerTestFileEditor, TestTextFileEditor, TestSingletonFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
|
||||
import { IEditorGroup, IEditorGroupsService, GroupDirection, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
|
@ -1408,9 +1408,8 @@ suite('EditorService', () => {
|
|||
let pane = await service.openEditor(new TestFileEditorInput(URI.parse('my://resource-openEditors'), TEST_EDITOR_INPUT_ID));
|
||||
pane = await service.openEditor(new TestFileEditorInput(URI.parse('my://resource-openEditors'), TEST_EDITOR_INPUT_ID), { sticky: true, preserveFocus: true });
|
||||
|
||||
assert.ok(pane instanceof TestEditorWithOptions);
|
||||
assert.strictEqual(pane.lastSetOptions?.sticky, true);
|
||||
assert.strictEqual(pane.lastSetOptions?.preserveFocus, true);
|
||||
assert.strictEqual(pane?.options?.sticky, true);
|
||||
assert.strictEqual(pane?.options?.preserveFocus, true);
|
||||
|
||||
await pane.group?.closeAllEditors();
|
||||
|
||||
|
@ -1419,16 +1418,15 @@ suite('EditorService', () => {
|
|||
pane = await service.openEditor({ resource: URI.file('resource-openEditors'), options: { sticky: true, preserveFocus: true } });
|
||||
|
||||
assert.ok(pane instanceof TestTextFileEditor);
|
||||
assert.strictEqual(pane.lastSetOptions?.sticky, true);
|
||||
assert.strictEqual(pane.lastSetOptions?.preserveFocus, true);
|
||||
assert.strictEqual(pane?.options?.sticky, true);
|
||||
assert.strictEqual(pane?.options?.preserveFocus, true);
|
||||
|
||||
// Untyped editor (with registered editor)
|
||||
pane = await service.openEditor({ resource: URI.file('file.editor-service-override-tests') });
|
||||
pane = await service.openEditor({ resource: URI.file('file.editor-service-override-tests'), options: { sticky: true, preserveFocus: true } });
|
||||
|
||||
assert.ok(pane instanceof TestEditorWithOptions);
|
||||
assert.strictEqual(pane.lastSetOptions?.sticky, true);
|
||||
assert.strictEqual(pane.lastSetOptions?.preserveFocus, true);
|
||||
assert.strictEqual(pane?.options?.sticky, true);
|
||||
assert.strictEqual(pane?.options?.preserveFocus, true);
|
||||
});
|
||||
|
||||
test('isOpen() with side by side editor', async () => {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { dispose, Disposable, DisposableStore, IDisposable } from 'vs/base/commo
|
|||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { getExcludes, ISearchConfiguration, SEARCH_EXCLUDE_CONFIG } from 'vs/workbench/services/search/common/search';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
|
||||
|
@ -278,6 +278,12 @@ export class HistoryService extends Disposable implements IHistoryService {
|
|||
this.globalNavigationsEditorNavigationStack
|
||||
];
|
||||
|
||||
readonly onDidChangeEditorNavigationStack = Event.any(
|
||||
this.globalDefaultEditorNavigationStack.onDidChange,
|
||||
this.globalModificationsEditorNavigationStack.onDidChange,
|
||||
this.globalNavigationsEditorNavigationStack.onDidChange
|
||||
);
|
||||
|
||||
private createEditorNavigationStack(): EditorNavigationStack {
|
||||
const editorNavigationStack = this._register(this.instantiationService.createInstance(EditorNavigationStack));
|
||||
|
||||
|
@ -1054,9 +1060,12 @@ export class EditorNavigationStack extends Disposable {
|
|||
this._register(this.editorGroupService.onDidRemoveGroup(e => this.onDidRemoveGroup(e.id)));
|
||||
}
|
||||
|
||||
private registerGroupListeners(group: IEditorGroup): void {
|
||||
if (!this.mapGroupToDisposable.has(group.id)) {
|
||||
this.mapGroupToDisposable.set(group.id, group.onWillMoveEditor(e => this.onWillMoveEditor(e)));
|
||||
private registerGroupListeners(groupId: GroupIdentifier): void {
|
||||
if (!this.mapGroupToDisposable.has(groupId)) {
|
||||
const group = this.editorGroupService.getGroup(groupId);
|
||||
if (group) {
|
||||
this.mapGroupToDisposable.set(groupId, group.onWillMoveEditor(e => this.onWillMoveEditor(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1091,11 +1100,6 @@ export class EditorNavigationStack extends Disposable {
|
|||
const isSelectionAwareEditorPane = isEditorPaneWithSelection(editorPane);
|
||||
const hasValidEditor = editorPane?.group && editorPane.input && !editorPane.input.isDisposed();
|
||||
|
||||
// Ensure we listen to changes in group
|
||||
if (hasValidEditor) {
|
||||
this.registerGroupListeners(editorPane.group);
|
||||
}
|
||||
|
||||
// Treat editor changes that happen as part of stack navigation specially
|
||||
// we do not want to add a new stack entry as a matter of navigating the
|
||||
// stack but we need to keep our currentEditorSelectionState up to date
|
||||
|
@ -1170,6 +1174,9 @@ export class EditorNavigationStack extends Disposable {
|
|||
|
||||
addOrReplace(groupId: GroupIdentifier, editorCandidate: EditorInput | IResourceEditorInput, selection?: IEditorPaneSelection, forceReplace?: boolean): void {
|
||||
|
||||
// Ensure we listen to changes in group
|
||||
this.registerGroupListeners(groupId);
|
||||
|
||||
// Check whether to replace an existing entry or not
|
||||
let replace = false;
|
||||
if (this.current) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as assert from 'assert';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor, createEditorPart, registerTestFileEditor, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor, createEditorPart, registerTestFileEditor, TestServiceAccessor, TestTextFileEditor } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IEditorGroupsService, GroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
|
@ -18,12 +18,15 @@ import { GoFilter, IHistoryService } from 'vs/workbench/services/history/common/
|
|||
import { DeferredPromise, timeout } from 'vs/base/common/async';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { EditorPaneSelectionChangeReason, isResourceEditorInput, IUntypedEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileChangesEvent, FileChangeType, FileOperation, FileOperationEvent } from 'vs/platform/files/common/files';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { TextEditorPaneSelection } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
|
||||
suite('HistoryService', function () {
|
||||
|
||||
|
@ -122,6 +125,103 @@ suite('HistoryService', function () {
|
|||
assert.strictEqual(part.activeGroup.activeEditor?.resource?.toString(), otherResource.toString());
|
||||
});
|
||||
|
||||
test('back / forward: in-editor text selection changes (user)', async function () {
|
||||
const [, historyService, editorService] = await createServices();
|
||||
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
||||
const pane = await editorService.openEditor({ resource, options: { pinned: true } }) as TestTextFileEditor;
|
||||
|
||||
await setTextSelection(historyService, pane, new Selection(1, 2, 1, 2));
|
||||
await setTextSelection(historyService, pane, new Selection(15, 1, 15, 1)); // will be merged and dropped
|
||||
await setTextSelection(historyService, pane, new Selection(16, 1, 16, 1)); // will be merged and dropped
|
||||
await setTextSelection(historyService, pane, new Selection(17, 1, 17, 1));
|
||||
await setTextSelection(historyService, pane, new Selection(30, 5, 30, 8));
|
||||
await setTextSelection(historyService, pane, new Selection(40, 1, 40, 1));
|
||||
|
||||
await historyService.goBack(GoFilter.NONE);
|
||||
assertTextSelection(new Selection(30, 5, 30, 8), pane);
|
||||
|
||||
await historyService.goBack(GoFilter.NONE);
|
||||
assertTextSelection(new Selection(17, 1, 17, 1), pane);
|
||||
|
||||
await historyService.goBack(GoFilter.NONE);
|
||||
assertTextSelection(new Selection(1, 2, 1, 2), pane);
|
||||
|
||||
await historyService.goForward(GoFilter.NONE);
|
||||
assertTextSelection(new Selection(17, 1, 17, 1), pane);
|
||||
});
|
||||
|
||||
test('back / forward: in-editor text selection changes (navigation)', async function () {
|
||||
const [, historyService, editorService] = await createServices();
|
||||
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
||||
const pane = await editorService.openEditor({ resource, options: { pinned: true } }) as TestTextFileEditor;
|
||||
|
||||
await setTextSelection(historyService, pane, new Selection(2, 2, 2, 10)); // this is our starting point
|
||||
await setTextSelection(historyService, pane, new Selection(5, 3, 5, 20), EditorPaneSelectionChangeReason.NAVIGATION); // this is our first target definition
|
||||
await setTextSelection(historyService, pane, new Selection(120, 8, 120, 18), EditorPaneSelectionChangeReason.NAVIGATION); // this is our second target definition
|
||||
await setTextSelection(historyService, pane, new Selection(300, 3, 300, 20)); // unrelated user navigation
|
||||
await setTextSelection(historyService, pane, new Selection(500, 3, 500, 20)); // unrelated user navigation
|
||||
await setTextSelection(historyService, pane, new Selection(200, 3, 200, 20)); // unrelated user navigation
|
||||
|
||||
await historyService.goLast(GoFilter.NAVIGATION);
|
||||
assertTextSelection(new Selection(120, 8, 120, 18), pane);
|
||||
|
||||
await historyService.goBack(GoFilter.NAVIGATION);
|
||||
assertTextSelection(new Selection(5, 3, 5, 20), pane);
|
||||
|
||||
await historyService.goBack(GoFilter.NAVIGATION);
|
||||
assertTextSelection(new Selection(2, 2, 2, 10), pane);
|
||||
|
||||
await historyService.goForward(GoFilter.NAVIGATION);
|
||||
assertTextSelection(new Selection(5, 3, 5, 20), pane);
|
||||
});
|
||||
|
||||
test('back / forward: edit selection changes', async function () {
|
||||
const [, historyService, editorService] = await createServices();
|
||||
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
||||
const pane = await editorService.openEditor({ resource, options: { pinned: true } }) as TestTextFileEditor;
|
||||
|
||||
await setTextSelection(historyService, pane, new Selection(2, 2, 2, 10));
|
||||
await setTextSelection(historyService, pane, new Selection(50, 3, 50, 20), EditorPaneSelectionChangeReason.EDIT);
|
||||
await setTextSelection(historyService, pane, new Selection(300, 3, 300, 20)); // unrelated user navigation
|
||||
await setTextSelection(historyService, pane, new Selection(500, 3, 500, 20)); // unrelated user navigation
|
||||
await setTextSelection(historyService, pane, new Selection(200, 3, 200, 20)); // unrelated user navigation
|
||||
await setTextSelection(historyService, pane, new Selection(5, 3, 5, 20), EditorPaneSelectionChangeReason.EDIT);
|
||||
await setTextSelection(historyService, pane, new Selection(200, 3, 200, 20)); // unrelated user navigation
|
||||
|
||||
await historyService.goLast(GoFilter.EDITS);
|
||||
assertTextSelection(new Selection(5, 3, 5, 20), pane);
|
||||
|
||||
await historyService.goBack(GoFilter.EDITS);
|
||||
assertTextSelection(new Selection(50, 3, 50, 20), pane);
|
||||
|
||||
await historyService.goForward(GoFilter.EDITS);
|
||||
assertTextSelection(new Selection(5, 3, 5, 20), pane);
|
||||
});
|
||||
|
||||
async function setTextSelection(historyService: IHistoryService, pane: TestTextFileEditor, selection: Selection, reason = EditorPaneSelectionChangeReason.USER): Promise<void> {
|
||||
const promise = Event.toPromise((historyService as HistoryService).onDidChangeEditorNavigationStack);
|
||||
pane.setSelection(new TextEditorPaneSelection(selection), reason);
|
||||
await promise;
|
||||
}
|
||||
|
||||
function assertTextSelection(expected: Selection, pane: EditorPane): void {
|
||||
const options: ITextEditorOptions | undefined = pane.options;
|
||||
if (!options) {
|
||||
assert.fail('EditorPane has no selection');
|
||||
}
|
||||
|
||||
assert.strictEqual(expected.startLineNumber, options.selection?.startLineNumber);
|
||||
assert.strictEqual(expected.startColumn, options.selection?.startColumn);
|
||||
assert.strictEqual(expected.endLineNumber, options.selection?.endLineNumber);
|
||||
assert.strictEqual(expected.endColumn, options.selection?.endColumn);
|
||||
}
|
||||
|
||||
test('back / forward: tracks editor moves across groups', async function () {
|
||||
const [part, historyService, editorService] = await createServices();
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { EditorInputWithOptions, IEditorIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorPane, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorsOrder, IFileEditorInput, IEditorFactoryRegistry, IEditorSerializer, EditorExtensions, ISaveOptions, IMoveResult, ITextDiffEditorPane, IVisibleEditorPane, IEditorOpenContext, EditorExtensions as Extensions, EditorInputCapabilities, IUntypedEditorInput, IEditorWillMoveEvent, IEditorWillOpenEvent, IActiveEditorChangeEvent } from 'vs/workbench/common/editor';
|
||||
import { EditorInputWithOptions, IEditorIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorPane, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorsOrder, IFileEditorInput, IEditorFactoryRegistry, IEditorSerializer, EditorExtensions, ISaveOptions, IMoveResult, ITextDiffEditorPane, IVisibleEditorPane, IEditorOpenContext, EditorExtensions as Extensions, EditorInputCapabilities, IUntypedEditorInput, IEditorWillMoveEvent, IEditorWillOpenEvent, IActiveEditorChangeEvent, EditorPaneSelectionChangeReason, IEditorPaneSelection } from 'vs/workbench/common/editor';
|
||||
import { EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor, IEditorGroupTitleHeight } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IResolvedWorkingCopyBackup, IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
|
||||
|
@ -18,7 +18,7 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur
|
|||
import { IWorkbenchLayoutService, PanelAlignment, 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, IResourceEditorInput, IEditorModel, IResourceEditorInputIdentifier, ITextResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorOptions, IResourceEditorInput, IEditorModel, IResourceEditorInputIdentifier, ITextResourceEditorInput } 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, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent, BeforeShutdownErrorEvent, InternalBeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
@ -180,17 +180,25 @@ export class TestTextResourceEditor extends TextResourceEditor {
|
|||
|
||||
export class TestTextFileEditor extends TextFileEditor {
|
||||
|
||||
lastSetOptions: ITextEditorOptions | undefined = undefined;
|
||||
|
||||
override setOptions(options: ITextEditorOptions | undefined): void {
|
||||
this.lastSetOptions = options;
|
||||
|
||||
super.setOptions(options);
|
||||
}
|
||||
|
||||
protected override createEditorControl(parent: HTMLElement, configuration: any): IEditor {
|
||||
return this.instantiationService.createInstance(TestCodeEditor, parent, configuration, {});
|
||||
}
|
||||
|
||||
fireSelectionChangeEvent(reason: EditorPaneSelectionChangeReason) {
|
||||
this._onDidChangeSelection.fire({ reason });
|
||||
}
|
||||
|
||||
private _testSelection: IEditorPaneSelection | undefined = undefined;
|
||||
|
||||
setSelection(selection: IEditorPaneSelection | undefined, reason: EditorPaneSelectionChangeReason): void {
|
||||
this._testSelection = selection;
|
||||
|
||||
this._onDidChangeSelection.fire({ reason });
|
||||
}
|
||||
|
||||
override getSelection(): IEditorPaneSelection | undefined {
|
||||
return this._testSelection ?? super.getSelection();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITestInstantiationService extends IInstantiationService {
|
||||
|
@ -1422,19 +1430,8 @@ export class TestEditorInput extends EditorInput {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class TestEditorWithOptions extends EditorPane {
|
||||
|
||||
lastSetOptions: ITextEditorOptions | undefined = undefined;
|
||||
|
||||
override setOptions(options: ITextEditorOptions | undefined): void {
|
||||
this.lastSetOptions = options;
|
||||
|
||||
super.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
export function registerTestEditor(id: string, inputs: SyncDescriptor<EditorInput>[], serializerInputId?: string): IDisposable {
|
||||
class TestEditor extends TestEditorWithOptions {
|
||||
class TestEditor extends EditorPane {
|
||||
|
||||
private _scopedContextKeyService: IContextKeyService;
|
||||
|
||||
|
|
Loading…
Reference in a new issue