mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
parent
6449b6b725
commit
e1a46ca69b
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { EditorInput, EditorResourceAccessor, IEditorInput, IEditorInputFactoryRegistry, SideBySideEditorInput, EditorExtensions } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorResourceAccessor, IEditorInput, EditorExtensions, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
|
@ -15,7 +15,6 @@ import { Promises } from 'vs/base/common/async';
|
|||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { NO_TYPE_ID } from 'vs/workbench/services/workingCopy/common/workingCopy';
|
||||
import { URI } from 'vs/workbench/workbench.web.api';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
|
||||
|
@ -196,39 +195,20 @@ Registry.add(EditorExtensions.Editors, new EditorRegistry());
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Text Editor Close Tracker
|
||||
//#region Editor Close Tracker
|
||||
|
||||
export function whenTextEditorClosed(accessor: ServicesAccessor, resources: URI[]): Promise<void> {
|
||||
export function whenEditorClosed(accessor: ServicesAccessor, resources: URI[]): Promise<void> {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const uriIdentityService = accessor.get(IUriIdentityService);
|
||||
const workingCopyService = accessor.get(IWorkingCopyService);
|
||||
|
||||
const fileEditorInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getFileEditorInputFactory();
|
||||
|
||||
return new Promise(resolve => {
|
||||
let remainingResources = [...resources];
|
||||
|
||||
// Observe any editor closing from this moment on
|
||||
const listener = editorService.onDidCloseEditor(async event => {
|
||||
let primaryResource: URI | undefined = undefined;
|
||||
let secondaryResource: URI | undefined = undefined;
|
||||
|
||||
// Resolve the resources from the editor that closed
|
||||
// but only consider file editor inputs, given we
|
||||
// are only tracking text editors.
|
||||
if (event.editor instanceof SideBySideEditorInput) {
|
||||
if (fileEditorInputFactory.isFileEditorInput(event.editor.primary)) {
|
||||
primaryResource = EditorResourceAccessor.getOriginalUri(event.editor.primary);
|
||||
}
|
||||
|
||||
if (fileEditorInputFactory.isFileEditorInput(event.editor.secondary)) {
|
||||
secondaryResource = EditorResourceAccessor.getOriginalUri(event.editor.secondary);
|
||||
}
|
||||
} else {
|
||||
if (fileEditorInputFactory.isFileEditorInput(event.editor)) {
|
||||
primaryResource = EditorResourceAccessor.getOriginalUri(event.editor);
|
||||
}
|
||||
}
|
||||
const primaryResource = EditorResourceAccessor.getOriginalUri(event.editor, { supportSideBySide: SideBySideEditor.PRIMARY });
|
||||
const secondaryResource = EditorResourceAccessor.getOriginalUri(event.editor, { supportSideBySide: SideBySideEditor.SECONDARY });
|
||||
|
||||
// Remove from resources to wait for being closed based on the
|
||||
// resources from editors that got closed
|
||||
|
@ -247,10 +227,10 @@ export function whenTextEditorClosed(accessor: ServicesAccessor, resources: URI[
|
|||
// to close the editor while the save still continues in the background. As such
|
||||
// we have to also check if the editors to track for are dirty and if so wait
|
||||
// for them to get saved.
|
||||
const dirtyResources = resources.filter(resource => workingCopyService.isDirty(resource, NO_TYPE_ID /* only check on text file working copies */));
|
||||
const dirtyResources = resources.filter(resource => workingCopyService.isDirty(resource));
|
||||
if (dirtyResources.length > 0) {
|
||||
await Promises.settled(dirtyResources.map(async resource => await new Promise<void>(resolve => {
|
||||
if (!workingCopyService.isDirty(resource, NO_TYPE_ID /* only check on text file working copies */)) {
|
||||
if (!workingCopyService.isDirty(resource)) {
|
||||
return resolve(); // return early if resource is not dirty
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { whenTextEditorClosed } from 'vs/workbench/browser/editor';
|
||||
import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
|
||||
suite('Files - TextFileEditorTracker', () => {
|
||||
|
@ -182,26 +181,4 @@ suite('Files - TextFileEditorTracker', () => {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('whenTextEditorClosed (single editor)', async function () {
|
||||
return testWhenTextEditorClosed(toResource.call(this, '/path/index.txt'));
|
||||
});
|
||||
|
||||
test('whenTextEditorClosed (multiple editor)', async function () {
|
||||
return testWhenTextEditorClosed(toResource.call(this, '/path/index.txt'), toResource.call(this, '/test.html'));
|
||||
});
|
||||
|
||||
async function testWhenTextEditorClosed(...resources: URI[]): Promise<void> {
|
||||
const accessor = await createTracker(false);
|
||||
|
||||
for (const resource of resources) {
|
||||
await accessor.editorService.openEditor({ resource, options: { pinned: true } });
|
||||
}
|
||||
|
||||
const closedPromise = accessor.instantitionService.invokeFunction(accessor => whenTextEditorClosed(accessor, resources));
|
||||
|
||||
accessor.editorGroupService.activeGroup.closeAllEditors();
|
||||
|
||||
await closedPromise;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -59,7 +59,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|||
import { AuthInfo } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { whenTextEditorClosed } from 'vs/workbench/browser/editor';
|
||||
import { whenEditorClosed } from 'vs/workbench/browser/editor';
|
||||
|
||||
export class NativeWindow extends Disposable {
|
||||
|
||||
|
@ -669,7 +669,7 @@ export class NativeWindow extends Disposable {
|
|||
private async trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): Promise<void> {
|
||||
|
||||
// Wait for the resources to be closed in the text editor...
|
||||
await this.instantiationService.invokeFunction(accessor => whenTextEditorClosed(accessor, resourcesToWaitFor));
|
||||
await this.instantiationService.invokeFunction(accessor => whenEditorClosed(accessor, resourcesToWaitFor));
|
||||
|
||||
// ...before deleting the wait marker file
|
||||
await this.fileService.del(waitMarkerFile);
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen, IOpenEmptyWindowOptions, IPathData, IFileToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { pathsToEditors } from 'vs/workbench/common/editor';
|
||||
import { whenTextEditorClosed } from 'vs/workbench/browser/editor';
|
||||
import { whenEditorClosed } from 'vs/workbench/browser/editor';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IModifierKeyStatus, ModifierKeyEmitter, trackFocus } from 'vs/base/browser/dom';
|
||||
|
@ -315,7 +315,7 @@ export class BrowserHostService extends Disposable implements IHostService {
|
|||
(async () => {
|
||||
|
||||
// Wait for the resources to be closed in the text editor...
|
||||
await this.instantiationService.invokeFunction(accessor => whenTextEditorClosed(accessor, fileOpenables.map(fileOpenable => fileOpenable.fileUri)));
|
||||
await this.instantiationService.invokeFunction(accessor => whenEditorClosed(accessor, fileOpenables.map(fileOpenable => fileOpenable.fileUri)));
|
||||
|
||||
// ...before deleting the wait marker file
|
||||
await this.fileService.del(waitMarkerFileURI);
|
||||
|
|
|
@ -8,9 +8,16 @@ import { EditorResourceAccessor, SideBySideEditor, IEditorInputWithPreferredReso
|
|||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { workbenchInstantiationService, TestServiceAccessor, TestEditorInput } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestServiceAccessor, TestEditorInput, registerTestDiffEditor, registerTestEditor, registerTestFileEditor, registerTestResourceEditor, TestFileEditorInput, createEditorPart } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { whenEditorClosed } from 'vs/workbench/browser/editor';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
suite('Workbench editor', () => {
|
||||
|
||||
|
@ -21,16 +28,40 @@ suite('Workbench editor', () => {
|
|||
}
|
||||
}
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const TEST_EDITOR_ID = 'MyTestEditorForEditors';
|
||||
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: TestServiceAccessor;
|
||||
|
||||
async function createServices(): Promise<TestServiceAccessor> {
|
||||
const instantiationService = workbenchInstantiationService();
|
||||
|
||||
const part = await createEditorPart(instantiationService, disposables);
|
||||
|
||||
instantiationService.stub(IEditorGroupsService, part);
|
||||
|
||||
const editorService = instantiationService.createInstance(EditorService);
|
||||
instantiationService.stub(IEditorService, editorService);
|
||||
|
||||
return instantiationService.createInstance(TestServiceAccessor);
|
||||
}
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(TestServiceAccessor);
|
||||
|
||||
disposables.add(registerTestFileEditor());
|
||||
disposables.add(registerTestDiffEditor());
|
||||
disposables.add(registerTestResourceEditor());
|
||||
disposables.add(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)]));
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
accessor.untitledTextEditorService.dispose();
|
||||
|
||||
disposables.clear();
|
||||
});
|
||||
|
||||
test('EditorResourceAccessor', () => {
|
||||
|
@ -123,4 +154,48 @@ suite('Workbench editor', () => {
|
|||
assert.strictEqual(EditorResourceAccessor.getCanonicalUri(fileWithPreferredResource)?.toString(), resource.toString());
|
||||
assert.strictEqual(EditorResourceAccessor.getOriginalUri(fileWithPreferredResource)?.toString(), preferredResource.toString());
|
||||
});
|
||||
|
||||
test('whenEditorClosed (single editor)', async function () {
|
||||
return testWhenEditorClosed(false, false, toResource.call(this, '/path/index.txt'));
|
||||
});
|
||||
|
||||
test('whenEditorClosed (multiple editor)', async function () {
|
||||
return testWhenEditorClosed(false, false, toResource.call(this, '/path/index.txt'), toResource.call(this, '/test.html'));
|
||||
});
|
||||
|
||||
test('whenEditorClosed (single editor, diff editor)', async function () {
|
||||
return testWhenEditorClosed(true, false, toResource.call(this, '/path/index.txt'));
|
||||
});
|
||||
|
||||
test('whenEditorClosed (multiple editor, diff editor)', async function () {
|
||||
return testWhenEditorClosed(true, false, toResource.call(this, '/path/index.txt'), toResource.call(this, '/test.html'));
|
||||
});
|
||||
|
||||
test('whenEditorClosed (single custom editor)', async function () {
|
||||
return testWhenEditorClosed(false, true, toResource.call(this, '/path/index.txt'));
|
||||
});
|
||||
|
||||
test('whenEditorClosed (multiple custom editor)', async function () {
|
||||
return testWhenEditorClosed(false, true, toResource.call(this, '/path/index.txt'), toResource.call(this, '/test.html'));
|
||||
});
|
||||
|
||||
async function testWhenEditorClosed(sideBySide: boolean, custom: boolean, ...resources: URI[]): Promise<void> {
|
||||
const accessor = await createServices();
|
||||
|
||||
for (const resource of resources) {
|
||||
if (custom) {
|
||||
await accessor.editorService.openEditor(new TestFileEditorInput(resource, 'testTypeId'), { pinned: true });
|
||||
} else if (sideBySide) {
|
||||
await accessor.editorService.openEditor({ leftResource: resource, rightResource: resource, options: { pinned: true } });
|
||||
} else {
|
||||
await accessor.editorService.openEditor({ resource, options: { pinned: true } });
|
||||
}
|
||||
}
|
||||
|
||||
const closedPromise = accessor.instantitionService.invokeFunction(accessor => whenEditorClosed(accessor, resources));
|
||||
|
||||
accessor.editorGroupService.activeGroup.closeAllEditors();
|
||||
|
||||
await closedPromise;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -42,7 +42,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'
|
|||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { IDialogService, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
|
@ -96,7 +96,7 @@ import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogSer
|
|||
import { CodeEditorService } from 'vs/workbench/services/editor/browser/codeEditorService';
|
||||
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDiffEditor, IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IChange, IDiffEditor, IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IInputBox, IInputOptions, IPickOptions, IQuickInputButton, IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { QuickInputService } from 'vs/workbench/services/quickinput/browser/quickInputService';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
|
@ -136,6 +136,10 @@ import { IEditorOverrideService } from 'vs/workbench/services/editor/common/edit
|
|||
import { IWorkingCopyEditorService, WorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService';
|
||||
import { IElevatedFileService } from 'vs/workbench/services/files/common/elevatedFileService';
|
||||
import { BrowserElevatedFileService } from 'vs/workbench/services/files/browser/elevatedFileService';
|
||||
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { TextEdit, IInplaceReplaceSupportResult } from 'vs/editor/common/modes';
|
||||
|
||||
export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
|
||||
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined);
|
||||
|
@ -183,6 +187,7 @@ export function workbenchInstantiationService(
|
|||
): ITestInstantiationService {
|
||||
const instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]));
|
||||
|
||||
instantiationService.stub(IEditorWorkerService, new TestEditorWorkerService());
|
||||
instantiationService.stub(IWorkingCopyService, disposables.add(new WorkingCopyService()));
|
||||
instantiationService.stub(IEnvironmentService, TestEnvironmentService);
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService);
|
||||
|
@ -1384,6 +1389,23 @@ export function registerTestSideBySideEditor(): IDisposable {
|
|||
return disposables;
|
||||
}
|
||||
|
||||
export function registerTestDiffEditor(): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
disposables.add(Registry.as<IEditorRegistry>(Extensions.Editors).registerEditor(
|
||||
EditorDescriptor.create(
|
||||
TextDiffEditor,
|
||||
TextDiffEditor.ID,
|
||||
'Text Diff Editor'
|
||||
),
|
||||
[
|
||||
new SyncDescriptor(DiffEditorInput)
|
||||
]
|
||||
));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
export class TestFileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
|
||||
readonly preferredResource = this.resource;
|
||||
|
@ -1650,3 +1672,18 @@ export class TestQuickInputService implements IQuickInputService {
|
|||
back(): Promise<void> { throw new Error('not implemented.'); }
|
||||
cancel(): Promise<void> { throw new Error('not implemented.'); }
|
||||
}
|
||||
|
||||
export class TestEditorWorkerService implements IEditorWorkerService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
canComputeDiff(original: URI, modified: URI): boolean { return false; }
|
||||
async computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> { return null; }
|
||||
canComputeDirtyDiff(original: URI, modified: URI): boolean { return false; }
|
||||
async computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IChange[] | null> { return null; }
|
||||
async computeMoreMinimalEdits(resource: URI, edits: TextEdit[] | null | undefined): Promise<TextEdit[] | undefined> { return undefined; }
|
||||
canComputeWordRanges(resource: URI): boolean { return false; }
|
||||
async computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[]; } | null> { return null; }
|
||||
canNavigateValueSet(resource: URI): boolean { return false; }
|
||||
async navigateValueSet(resource: URI, range: IRange, up: boolean): Promise<IInplaceReplaceSupportResult | null> { return null; }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue