Skip to content

Commit 1e90cca

Browse files
author
Benjamin Pasero
authored
editors - use canonical URIs when creating editor inputs (microsoft#93368) (microsoft#98795)
1 parent a0b1c3d commit 1e90cca

3 files changed

Lines changed: 46 additions & 22 deletions

File tree

src/vs/workbench/services/editor/browser/editorService.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { indexOfPath } from 'vs/base/common/extpath';
3737
import { DEFAULT_CUSTOM_EDITOR, updateViewTypeSchema, editorAssociationsConfigurationNode } from 'vs/workbench/services/editor/common/editorAssociationsSetting';
3838
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
3939
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
40+
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
4041

4142
type CachedEditorInput = ResourceEditorInput | IFileEditorInput | UntitledTextEditorInput;
4243
type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE;
@@ -74,7 +75,8 @@ export class EditorService extends Disposable implements EditorServiceImpl {
7475
@IFileService private readonly fileService: IFileService,
7576
@IConfigurationService private readonly configurationService: IConfigurationService,
7677
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
77-
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService
78+
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
79+
@IUriIdentityService private readonly uriIdentitiyService: IUriIdentityService
7880
) {
7981
super();
8082

@@ -867,20 +869,25 @@ export class EditorService extends Disposable implements EditorServiceImpl {
867869
// Resource Editor Support
868870
const resourceEditorInput = input as IResourceEditorInput;
869871
if (resourceEditorInput.resource instanceof URI) {
870-
let label = resourceEditorInput.label;
871-
if (!label) {
872-
label = basename(resourceEditorInput.resource); // derive the label from the path
873-
}
874872

875-
return this.createOrGetCached(resourceEditorInput.resource, () => {
873+
// We do not trust the resource that is being passed in as being the truth
874+
// (e.g. in terms of path casing) and as such we ask the URI service to give
875+
// us the canconical form of the URI. As such we ensure that any editor that
876+
// is being opened will use the same canonical form of the URI.
877+
const canonicalResource = this.uriIdentitiyService.asCanonicalUri(resourceEditorInput.resource);
878+
879+
// Derive the label from the path if not provided explicitly
880+
const label = resourceEditorInput.label || basename(canonicalResource);
881+
882+
return this.createOrGetCached(canonicalResource, () => {
876883

877884
// File
878-
if (resourceEditorInput.forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(resourceEditorInput.resource)) {
879-
return this.fileEditorInputFactory.createFileEditorInput(resourceEditorInput.resource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
885+
if (resourceEditorInput.forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(canonicalResource)) {
886+
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
880887
}
881888

882889
// Resource
883-
return this.instantiationService.createInstance(ResourceEditorInput, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.resource, resourceEditorInput.mode);
890+
return this.instantiationService.createInstance(ResourceEditorInput, resourceEditorInput.label, resourceEditorInput.description, canonicalResource, resourceEditorInput.mode);
884891
}, cachedInput => {
885892

886893
// Untitled

src/vs/workbench/services/editor/test/browser/editorService.test.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/u
2828
import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider';
2929
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
3030
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
31+
import { isLinux } from 'vs/base/common/platform';
3132

3233
const TEST_EDITOR_ID = 'MyTestEditorForEditorService';
3334
const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorService';
@@ -111,7 +112,7 @@ suite('EditorService', () => {
111112
assert.equal(visibleEditorChangeEventCounter, 1);
112113

113114
// Close input
114-
await editor!.group!.closeEditor(input);
115+
await editor?.group?.closeEditor(input);
115116

116117
assert.equal(0, service.count);
117118
assert.equal(0, service.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).length);
@@ -255,13 +256,13 @@ suite('EditorService', () => {
255256
const fileEditorInput1Again = service.createEditorInput({ resource: fileResource1 });
256257
assert.equal(fileEditorInput1Again, fileEditorInput1);
257258

258-
fileEditorInput1Again!.dispose();
259+
fileEditorInput1Again.dispose();
259260

260-
assert.ok(fileEditorInput1!.isDisposed());
261+
assert.ok(fileEditorInput1.isDisposed());
261262

262263
const fileEditorInput1AgainAndAgain = service.createEditorInput({ resource: fileResource1 });
263264
assert.notEqual(fileEditorInput1AgainAndAgain, fileEditorInput1);
264-
assert.ok(!fileEditorInput1AgainAndAgain!.isDisposed());
265+
assert.ok(!fileEditorInput1AgainAndAgain.isDisposed());
265266

266267
// Cached Input (Resource)
267268
const resource1 = URI.from({ scheme: 'custom', path: '/foo/bar/cache1.js' });
@@ -277,13 +278,13 @@ suite('EditorService', () => {
277278
const input1Again = service.createEditorInput({ resource: resource1 });
278279
assert.equal(input1Again, input1);
279280

280-
input1Again!.dispose();
281+
input1Again.dispose();
281282

282-
assert.ok(input1!.isDisposed());
283+
assert.ok(input1.isDisposed());
283284

284285
const input1AgainAndAgain = service.createEditorInput({ resource: resource1 });
285286
assert.notEqual(input1AgainAndAgain, input1);
286-
assert.ok(!input1AgainAndAgain!.isDisposed());
287+
assert.ok(!input1AgainAndAgain.isDisposed());
287288
});
288289

289290
test('createEditorInput', async function () {
@@ -301,6 +302,18 @@ suite('EditorService', () => {
301302
let contentInput = <FileEditorInput>input;
302303
assert.strictEqual(contentInput.resource.fsPath, toResource.call(this, '/index.html').fsPath);
303304

305+
// Untyped Input (file casing)
306+
input = service.createEditorInput({ resource: toResource.call(this, '/index.html') });
307+
let inputDifferentCase = service.createEditorInput({ resource: toResource.call(this, '/INDEX.html') });
308+
309+
if (!isLinux) {
310+
assert.equal(input, inputDifferentCase);
311+
assert.equal(input.resource?.toString(), inputDifferentCase.resource?.toString());
312+
} else {
313+
assert.notEqual(input, inputDifferentCase);
314+
assert.notEqual(input.resource?.toString(), inputDifferentCase.resource?.toString());
315+
}
316+
304317
// Typed Input
305318
assert.equal(service.createEditorInput(input), input);
306319
assert.equal(service.createEditorInput({ editor: input }), input);
@@ -331,7 +344,7 @@ suite('EditorService', () => {
331344
input = service.createEditorInput({ contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } });
332345
assert(input instanceof UntitledTextEditorInput);
333346
let model = await input.resolve() as UntitledTextEditorModel;
334-
assert.equal(model.textEditorModel!.getValue(), 'Hello Untitled');
347+
assert.equal(model.textEditorModel?.getValue(), 'Hello Untitled');
335348

336349
// Untyped Input (untitled with mode)
337350
input = service.createEditorInput({ mode, options: { selection: { startLineNumber: 1, startColumn: 1 } } });
@@ -446,13 +459,13 @@ suite('EditorService', () => {
446459

447460
assert.equal(part.activeGroup, rootGroup);
448461
assert.equal(part.count, 2);
449-
assert.equal(editor!.group, part.groups[1]);
462+
assert.equal(editor?.group, part.groups[1]);
450463

451464
// Open to the side uses existing neighbour group if any
452465
editor = await service.openEditor(input2, { pinned: true, preserveFocus: true }, SIDE_GROUP);
453466
assert.equal(part.activeGroup, rootGroup);
454467
assert.equal(part.count, 2);
455-
assert.equal(editor!.group, part.groups[1]);
468+
assert.equal(editor?.group, part.groups[1]);
456469

457470
part.dispose();
458471
});
@@ -469,7 +482,7 @@ suite('EditorService', () => {
469482

470483
await service.openEditor(input1, { pinned: true }, rootGroup);
471484
let editor = await service.openEditor(input2, { pinned: true, preserveFocus: true, activation: EditorActivation.ACTIVATE }, SIDE_GROUP);
472-
const sideGroup = editor!.group;
485+
const sideGroup = editor?.group;
473486

474487
assert.equal(part.activeGroup, sideGroup);
475488

@@ -527,7 +540,7 @@ suite('EditorService', () => {
527540

528541
// 1.) open, open same, open other, close
529542
let editor = await service.openEditor(input, { pinned: true });
530-
const group = editor!.group!;
543+
const group = editor?.group!;
531544
assertActiveEditorChangedEvent(true);
532545
assertVisibleEditorsChangedEvent(true);
533546

src/vs/workbench/test/browser/workbenchTestServices.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ import { TestWorkingCopyService, TestContextService, TestStorageService, TestTex
107107
import { IViewsService, IView, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
108108
import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
109109
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
110+
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
111+
import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService';
110112

111113
export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
112114
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
@@ -149,7 +151,9 @@ export function workbenchInstantiationService(overrides?: {
149151
const themeService = new TestThemeService();
150152
instantiationService.stub(IThemeService, themeService);
151153
instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl));
152-
instantiationService.stub(IFileService, new TestFileService());
154+
const fileService = new TestFileService();
155+
instantiationService.stub(IFileService, fileService);
156+
instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService));
153157
instantiationService.stub(IBackupFileService, new TestBackupFileService());
154158
instantiationService.stub(ITelemetryService, NullTelemetryService);
155159
instantiationService.stub(INotificationService, new TestNotificationService());

0 commit comments

Comments
 (0)