Skip to content

Commit bb7910d

Browse files
author
Benjamin Pasero
committed
1 parent 0acc996 commit bb7910d

9 files changed

Lines changed: 111 additions & 52 deletions

File tree

src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ export class BreadcrumbsControl {
256256
input = input.primary;
257257
}
258258
if (Registry.as<IEditorInputFactoryRegistry>(Extensions.EditorInputFactories).getFileEditorInputFactory().isFileEditorInput(input)) {
259-
fileInfoUri = input.label;
259+
fileInfoUri = input.preferredResource;
260260
}
261261

262262
this.domNode.classList.toggle('hidden', false);

src/vs/workbench/common/editor.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export interface IFileEditorInputFactory {
167167
/**
168168
* Creates new new editor input capable of showing files.
169169
*/
170-
createFileEditorInput(resource: URI, label: URI | undefined, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput;
170+
createFileEditorInput(resource: URI, preferredResource: URI | undefined, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput;
171171

172172
/**
173173
* Check if the provided object is a file editor input.
@@ -649,20 +649,25 @@ export interface IModeSupport {
649649
export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeSupport {
650650

651651
/**
652-
* Gets the resource this file input is about.
652+
* Gets the resource this file input is about. This will always be the
653+
* canonical form of the resource, so it may differ from the original
654+
* resource that was provided to create the input. Use `preferredResource`
655+
* for the form as it was created.
653656
*/
654657
readonly resource: URI;
655658

656659
/**
657-
* Gets the label of the editor. In most cases this will
658-
* be identical to the resource.
660+
* Gets the preferred resource of the editor. In most cases this will
661+
* be identical to the resource. But in some cases the preferredResource
662+
* may differ in path casing to the actual resource because we keep
663+
* canonical forms of resources in-memory.
659664
*/
660-
readonly label: URI;
665+
readonly preferredResource: URI;
661666

662667
/**
663-
* Sets the preferred label to use for this file input.
668+
* Sets the preferred resource to use for this file input.
664669
*/
665-
setLabel(label: URI): void;
670+
setPreferredResource(preferredResource: URI): void;
666671

667672
/**
668673
* Sets the preferred encoding to use for this file input.

src/vs/workbench/common/editor/textResourceEditorInput.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput {
2222

2323
private static readonly MEMOIZER = createMemoizer();
2424

25-
private _label: URI;
26-
get label(): URI { return this._label; }
25+
private _preferredResource: URI;
26+
get preferredResource(): URI { return this._preferredResource; }
2727

2828
constructor(
2929
public readonly resource: URI,
30-
preferredLabel: URI | undefined,
30+
preferredResource: URI | undefined,
3131
@IEditorService protected readonly editorService: IEditorService,
3232
@IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService,
3333
@ITextFileService protected readonly textFileService: ITextFileService,
@@ -37,7 +37,7 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput {
3737
) {
3838
super();
3939

40-
this._label = preferredLabel || resource;
40+
this._preferredResource = preferredResource || resource;
4141

4242
this.registerListeners();
4343
}
@@ -51,7 +51,7 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput {
5151
}
5252

5353
private onLabelEvent(scheme: string): void {
54-
if (scheme === this._label.scheme) {
54+
if (scheme === this._preferredResource.scheme) {
5555
this.updateLabel();
5656
}
5757
}
@@ -65,25 +65,21 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput {
6565
this._onDidChangeLabel.fire();
6666
}
6767

68-
setLabel(label: URI): void {
69-
if (!extUri.isEqual(label, this._label)) {
70-
this._label = label;
68+
setPreferredResource(preferredResource: URI): void {
69+
if (!extUri.isEqual(preferredResource, this._preferredResource)) {
70+
this._preferredResource = preferredResource;
7171

7272
this.updateLabel();
7373
}
7474
}
7575

76-
getLabel(): URI {
77-
return this._label;
78-
}
79-
8076
getName(): string {
8177
return this.basename;
8278
}
8379

8480
@AbstractTextResourceEditorInput.MEMOIZER
8581
private get basename(): string {
86-
return this.labelService.getUriBasenameLabel(this._label);
82+
return this.labelService.getUriBasenameLabel(this._preferredResource);
8783
}
8884

8985
getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined {
@@ -100,17 +96,17 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput {
10096

10197
@AbstractTextResourceEditorInput.MEMOIZER
10298
private get shortDescription(): string {
103-
return this.labelService.getUriBasenameLabel(dirname(this._label));
99+
return this.labelService.getUriBasenameLabel(dirname(this._preferredResource));
104100
}
105101

106102
@AbstractTextResourceEditorInput.MEMOIZER
107103
private get mediumDescription(): string {
108-
return this.labelService.getUriLabel(dirname(this._label), { relative: true });
104+
return this.labelService.getUriLabel(dirname(this._preferredResource), { relative: true });
109105
}
110106

111107
@AbstractTextResourceEditorInput.MEMOIZER
112108
private get longDescription(): string {
113-
return this.labelService.getUriLabel(dirname(this._label));
109+
return this.labelService.getUriLabel(dirname(this._preferredResource));
114110
}
115111

116112
@AbstractTextResourceEditorInput.MEMOIZER
@@ -120,12 +116,12 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput {
120116

121117
@AbstractTextResourceEditorInput.MEMOIZER
122118
private get mediumTitle(): string {
123-
return this.labelService.getUriLabel(this._label, { relative: true });
119+
return this.labelService.getUriLabel(this._preferredResource, { relative: true });
124120
}
125121

126122
@AbstractTextResourceEditorInput.MEMOIZER
127123
private get longTitle(): string {
128-
return this.labelService.getUriLabel(this._label);
124+
return this.labelService.getUriLabel(this._preferredResource);
129125
}
130126

131127
getTitle(verbosity: Verbosity): string {

src/vs/workbench/contrib/files/browser/files.contribution.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
102102

103103
// Register default file input factory
104104
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerFileEditorInputFactory({
105-
createFileEditorInput: (resource, label, encoding, mode, instantiationService): IFileEditorInput => {
106-
return instantiationService.createInstance(FileEditorInput, resource, label, encoding, mode);
105+
createFileEditorInput: (resource, preferredResource, encoding, mode, instantiationService): IFileEditorInput => {
106+
return instantiationService.createInstance(FileEditorInput, resource, preferredResource, encoding, mode);
107107
},
108108

109109
isFileEditorInput: (obj): obj is IFileEditorInput => {
@@ -113,7 +113,7 @@ Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactor
113113

114114
interface ISerializedFileEditorInput {
115115
resourceJSON: UriComponents;
116-
labelJSON?: UriComponents;
116+
preferredResourceJSON?: UriComponents;
117117
encoding?: string;
118118
modeId?: string;
119119
}
@@ -128,10 +128,10 @@ class FileEditorInputFactory implements IEditorInputFactory {
128128
serialize(editorInput: EditorInput): string {
129129
const fileEditorInput = <FileEditorInput>editorInput;
130130
const resource = fileEditorInput.resource;
131-
const label = fileEditorInput.getLabel();
131+
const preferredResource = fileEditorInput.preferredResource;
132132
const serializedFileEditorInput: ISerializedFileEditorInput = {
133133
resourceJSON: resource.toJSON(),
134-
labelJSON: extUri.isEqual(resource, label) ? undefined : label, // only storing label if it differs from the resource
134+
preferredResourceJSON: extUri.isEqual(resource, preferredResource) ? undefined : preferredResource, // only storing preferredResource if it differs from the resource
135135
encoding: fileEditorInput.getEncoding(),
136136
modeId: fileEditorInput.getPreferredMode() // only using the preferred user associated mode here if available to not store redundant data
137137
};
@@ -143,13 +143,18 @@ class FileEditorInputFactory implements IEditorInputFactory {
143143
return instantiationService.invokeFunction<FileEditorInput>(accessor => {
144144
const serializedFileEditorInput: ISerializedFileEditorInput = JSON.parse(serializedEditorInput);
145145
const resource = URI.revive(serializedFileEditorInput.resourceJSON);
146-
const label = URI.revive(serializedFileEditorInput.labelJSON);
146+
const preferredResource = URI.revive(serializedFileEditorInput.preferredResourceJSON);
147147
const encoding = serializedFileEditorInput.encoding;
148148
const mode = serializedFileEditorInput.modeId;
149149

150-
const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput;
151-
if (label) {
152-
fileEditorInput.setLabel(label);
150+
const fileEditorInput = accessor.get(IEditorService).createEditorInput({
151+
resource: preferredResource || resource, // prefer the preferred resource when creating the input again (https://github.com/microsoft/vscode/issues/102627)
152+
encoding,
153+
mode,
154+
forceFile: true
155+
}) as FileEditorInput;
156+
if (preferredResource) {
157+
fileEditorInput.setPreferredResource(preferredResource);
153158
}
154159

155160
return fileEditorInput;

src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
4545

4646
constructor(
4747
resource: URI,
48-
preferredLabel: URI | undefined,
48+
preferredResource: URI | undefined,
4949
preferredEncoding: string | undefined,
5050
preferredMode: string | undefined,
5151
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -57,7 +57,7 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
5757
@IEditorService editorService: IEditorService,
5858
@IEditorGroupsService editorGroupService: IEditorGroupsService
5959
) {
60-
super(resource, preferredLabel, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService);
60+
super(resource, preferredResource, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService);
6161

6262
this.model = this.textFileService.files.get(resource);
6363

src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { toResource } from 'vs/base/test/common/utils';
99
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
1010
import { workbenchInstantiationService, TestServiceAccessor, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
1111
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
12-
import { EncodingMode, Verbosity } from 'vs/workbench/common/editor';
12+
import { EncodingMode, IEditorInputFactoryRegistry, Verbosity, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
1313
import { TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
1414
import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
1515
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
@@ -18,6 +18,7 @@ import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRe
1818
import { DisposableStore } from 'vs/base/common/lifecycle';
1919
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
2020
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
21+
import { Registry } from 'vs/platform/registry/common/platform';
2122

2223
suite('Files - FileEditorInput', () => {
2324
let instantiationService: IInstantiationService;
@@ -92,18 +93,32 @@ suite('Files - FileEditorInput', () => {
9293
}
9394
});
9495

95-
test('label', function () {
96-
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined);
96+
test('preferred resource', function () {
97+
const resource = toResource.call(this, '/foo/bar/updatefile.js');
98+
const preferredResource = toResource.call(this, '/foo/bar/UPDATEFILE.js');
99+
100+
const inputWithoutPreferredResource = instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined);
101+
assert.equal(inputWithoutPreferredResource.resource.toString(), resource.toString());
102+
assert.equal(inputWithoutPreferredResource.preferredResource.toString(), resource.toString());
103+
104+
const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, resource, preferredResource, undefined, undefined);
105+
106+
assert.equal(inputWithPreferredResource.resource.toString(), resource.toString());
107+
assert.equal(inputWithPreferredResource.preferredResource.toString(), preferredResource.toString());
97108

98109
let didChangeLabel = false;
99-
const listener = input.onDidChangeLabel(e => {
110+
const listener = inputWithPreferredResource.onDidChangeLabel(e => {
100111
didChangeLabel = true;
101112
});
102113

103-
assert.equal(input.getName(), 'UPDATEFILE.js');
114+
assert.equal(inputWithPreferredResource.getName(), 'UPDATEFILE.js');
104115

105-
input.setLabel(toResource.call(this, '/FOO/BAR/updateFILE.js'));
106-
assert.equal(input.getName(), 'updateFILE.js');
116+
const otherPreferredResource = toResource.call(this, '/FOO/BAR/updateFILE.js');
117+
inputWithPreferredResource.setPreferredResource(otherPreferredResource);
118+
119+
assert.equal(inputWithPreferredResource.resource.toString(), resource.toString());
120+
assert.equal(inputWithPreferredResource.preferredResource.toString(), otherPreferredResource.toString());
121+
assert.equal(inputWithPreferredResource.getName(), 'updateFILE.js');
107122
assert.equal(didChangeLabel, true);
108123

109124
listener.dispose();
@@ -239,4 +254,37 @@ suite('Files - FileEditorInput', () => {
239254

240255
resolved.dispose();
241256
});
257+
258+
test('file editor input factory', async function () {
259+
instantiationService.invokeFunction(accessor => Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).start(accessor));
260+
261+
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined);
262+
263+
const factory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getEditorInputFactory(input.getTypeId());
264+
if (!factory) {
265+
assert.fail('File Editor Input Factory missing');
266+
}
267+
268+
assert.equal(factory.canSerialize(input), true);
269+
270+
const inputSerialized = factory.serialize(input);
271+
if (!inputSerialized) {
272+
assert.fail('Unexpected serialized file input');
273+
}
274+
275+
const inputDeserialized = factory.deserialize(instantiationService, inputSerialized);
276+
assert.equal(input.matches(inputDeserialized), true);
277+
278+
const preferredResource = toResource.call(this, '/foo/bar/UPDATEfile.js');
279+
const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), preferredResource, undefined, undefined);
280+
281+
const inputWithPreferredResourceSerialized = factory.serialize(inputWithPreferredResource);
282+
if (!inputWithPreferredResourceSerialized) {
283+
assert.fail('Unexpected serialized file input');
284+
}
285+
286+
const inputWithPreferredResourceDeserialized = factory.deserialize(instantiationService, inputWithPreferredResourceSerialized) as FileEditorInput;
287+
assert.equal(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.resource.toString());
288+
assert.equal(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.preferredResource.toString());
289+
});
242290
});

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -874,16 +874,21 @@ export class EditorService extends Disposable implements EditorServiceImpl {
874874
// Derive the label from the path if not provided explicitly
875875
const label = resourceEditorInput.label || basename(resourceEditorInput.resource);
876876

877+
// We keep track of the preferred resource this input is to be created
878+
// with but it may be different from the canonical resource (see below)
879+
const preferredResource = resourceEditorInput.resource;
880+
877881
// From this moment on, only operate on the canonical resource
878882
// to ensure we reduce the chance of opening the same resource
879883
// with different resource forms (e.g. path casing on Windows)
880-
const canonicalResource = this.asCanonicalEditorResource(resourceEditorInput.resource);
884+
const canonicalResource = this.asCanonicalEditorResource(preferredResource);
885+
881886

882887
return this.createOrGetCached(canonicalResource, () => {
883888

884889
// File
885-
if (resourceEditorInput.forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(canonicalResource)) {
886-
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, resourceEditorInput.resource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
890+
if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) {
891+
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
887892
}
888893

889894
// Resource
@@ -897,7 +902,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
897902

898903
// Files
899904
else if (!(cachedInput instanceof ResourceEditorInput)) {
900-
cachedInput.setLabel(resourceEditorInput.resource);
905+
cachedInput.setPreferredResource(preferredResource);
901906

902907
if (resourceEditorInput.encoding) {
903908
cachedInput.setPreferredEncoding(resourceEditorInput.encoding);

src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,14 @@ class NonSerializableTestEditorInput extends EditorInput {
150150

151151
class TestFileEditorInput extends EditorInput implements IFileEditorInput {
152152

153-
readonly label = this.resource;
153+
readonly preferredResource = this.resource;
154154

155155
constructor(public id: string, public resource: URI) {
156156
super();
157157
}
158158
getTypeId() { return 'testFileEditorInputForGroups'; }
159159
resolve(): Promise<IEditorModel> { return Promise.resolve(null!); }
160-
setLabel(label: URI): void { }
160+
setPreferredResource(resource: URI): void { }
161161
setEncoding(encoding: string) { }
162162
getEncoding() { return undefined; }
163163
setPreferredEncoding(encoding: string) { }

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor<EditorInpu
11231123

11241124
export class TestFileEditorInput extends EditorInput implements IFileEditorInput {
11251125

1126-
readonly label = this.resource;
1126+
readonly preferredResource = this.resource;
11271127

11281128
gotDisposed = false;
11291129
gotSaved = false;
@@ -1142,7 +1142,7 @@ export class TestFileEditorInput extends EditorInput implements IFileEditorInput
11421142
getTypeId() { return this.typeId; }
11431143
resolve(): Promise<IEditorModel | null> { return !this.fails ? Promise.resolve(null) : Promise.reject(new Error('fails')); }
11441144
matches(other: EditorInput): boolean { return !!(other?.resource && this.resource.toString() === other.resource.toString() && other instanceof TestFileEditorInput && other.getTypeId() === this.typeId); }
1145-
setLabel(label: URI): void { }
1145+
setPreferredResource(resource: URI): void { }
11461146
setEncoding(encoding: string) { }
11471147
getEncoding() { return undefined; }
11481148
setPreferredEncoding(encoding: string) { }

0 commit comments

Comments
 (0)