Skip to content

Commit 76a069d

Browse files
author
Benjamin Pasero
authored
Fix file system providers from extensions to resolve save conflicts (microsoft#72954)
* fix microsoft#72924 * add test for FileOnDiskContentProvider * make test assert more
1 parent 029a4e8 commit 76a069d

4 files changed

Lines changed: 62 additions & 8 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { ResourceMap } from 'vs/base/common/map';
1919
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
2020
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
2121
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
22-
import { FileOnDiskContentProvider } from 'vs/workbench/contrib/files/common/files';
22+
import { FileOnDiskContentProvider, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files';
2323
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
2424
import { IModelService } from 'vs/editor/common/services/modelService';
2525
import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands';
@@ -248,7 +248,7 @@ class ResolveSaveConflictAction extends Action {
248248

249249
return this.editorService.openEditor(
250250
{
251-
leftResource: resource.with({ scheme: CONFLICT_RESOLUTION_SCHEME }),
251+
leftResource: resourceToFileOnDisk(CONFLICT_RESOLUTION_SCHEME, resource),
252252
rightResource: resource,
253253
label: editorLabel,
254254
options: { pinned: true }

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
2323
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
2424
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
2525
import { once } from 'vs/base/common/functional';
26-
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
27-
import { toLocalResource } from 'vs/base/common/resources';
2826

2927
/**
3028
* Explorer viewlet id.
@@ -133,20 +131,27 @@ export const SortOrderConfiguration = {
133131

134132
export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified';
135133

134+
export function resourceToFileOnDisk(scheme: string, resource: URI): URI {
135+
return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) });
136+
}
137+
138+
export function fileOnDiskToResource(resource: URI): URI {
139+
return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null });
140+
}
141+
136142
export class FileOnDiskContentProvider implements ITextModelContentProvider {
137143
private fileWatcherDisposable: IDisposable | undefined;
138144

139145
constructor(
140146
@ITextFileService private readonly textFileService: ITextFileService,
141147
@IFileService private readonly fileService: IFileService,
142148
@IModeService private readonly modeService: IModeService,
143-
@IModelService private readonly modelService: IModelService,
144-
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
149+
@IModelService private readonly modelService: IModelService
145150
) {
146151
}
147152

148153
provideTextContent(resource: URI): Promise<ITextModel> {
149-
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
154+
const savedFileResource = fileOnDiskToResource(resource);
150155

151156
// Make sure our file from disk is resolved up to date
152157
return this.resolveEditorModel(resource).then(codeEditorModel => {
@@ -174,7 +179,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
174179
private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
175180
private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
176181
private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
177-
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
182+
const savedFileResource = fileOnDiskToResource(resource);
178183

179184
return this.textFileService.readStream(savedFileResource).then(content => {
180185
let codeEditorModel = this.modelService.getModel(resource);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as assert from 'assert';
7+
import { URI } from 'vs/base/common/uri';
8+
import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
9+
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
10+
import { FileOnDiskContentProvider, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files';
11+
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
12+
import { IFileService } from 'vs/platform/files/common/files';
13+
14+
class ServiceAccessor {
15+
constructor(
16+
@IFileService public fileService: TestFileService
17+
) {
18+
}
19+
}
20+
21+
suite('Files - FileOnDiskContentProvider', () => {
22+
23+
let instantiationService: IInstantiationService;
24+
let accessor: ServiceAccessor;
25+
26+
setup(() => {
27+
instantiationService = workbenchInstantiationService();
28+
accessor = instantiationService.createInstance(ServiceAccessor);
29+
});
30+
31+
test('provideTextContent', async () => {
32+
const provider = instantiationService.createInstance(FileOnDiskContentProvider);
33+
const uri = URI.parse('testFileOnDiskContentProvider://foo');
34+
35+
const content = await provider.provideTextContent(resourceToFileOnDisk('conflictResolution', uri));
36+
37+
assert.equal(snapshotToString(content.createSnapshot()), 'Hello Html');
38+
assert.equal(accessor.fileService.getLastReadFileUri().toString(), uri.toString());
39+
});
40+
});

src/vs/workbench/test/workbenchTestServices.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ export class TestFileService implements IFileService {
899899
readonly onError: Event<Error> = Event.None;
900900

901901
private content = 'Hello Html';
902+
private lastReadFileUri: URI;
902903

903904
constructor() {
904905
this._onFileChanges = new Emitter<FileChangesEvent>();
@@ -913,6 +914,10 @@ export class TestFileService implements IFileService {
913914
return this.content;
914915
}
915916

917+
public getLastReadFileUri(): URI {
918+
return this.lastReadFileUri;
919+
}
920+
916921
public get onFileChanges(): Event<FileChangesEvent> {
917922
return this._onFileChanges.event;
918923
}
@@ -952,6 +957,8 @@ export class TestFileService implements IFileService {
952957
}
953958

954959
readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {
960+
this.lastReadFileUri = resource;
961+
955962
return Promise.resolve({
956963
resource: resource,
957964
value: VSBuffer.fromString(this.content),
@@ -964,6 +971,8 @@ export class TestFileService implements IFileService {
964971
}
965972

966973
readFileStream(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileStreamContent> {
974+
this.lastReadFileUri = resource;
975+
967976
return Promise.resolve({
968977
resource: resource,
969978
value: {

0 commit comments

Comments
 (0)