diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index ba7e600b97c..d5faed937bd 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -94,6 +94,7 @@ import { ipcSharedProcessTunnelChannelName, ISharedProcessTunnelService } from ' import { SharedProcessTunnelService } from 'vs/platform/remote/node/sharedProcessTunnelService'; import { ipcSharedProcessWorkerChannelName, ISharedProcessWorkerConfiguration, ISharedProcessWorkerService } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService'; import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService'; +import { IUserConfigurationFileService, UserConfigurationFileServiceId } from 'vs/platform/configuration/common/userConfigurationFileService'; class SharedProcessMain extends Disposable { @@ -218,6 +219,9 @@ class SharedProcessMain extends Disposable { storageService.initialize() ]); + // User Configuration File + services.set(IUserConfigurationFileService, ProxyChannel.toService(mainProcessService.getChannel(UserConfigurationFileServiceId))); + // Request services.set(IRequestService, new SyncDescriptor(RequestService)); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index c4574912081..c284c8aed67 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -567,8 +567,10 @@ export class CodeApplication extends Disposable { const fileSystemProviderChannel = new DiskFileSystemProviderChannel(diskFileSystemProvider, this.logService); mainProcessElectronServer.registerChannel('localFilesystem', fileSystemProviderChannel); - // Configuration - mainProcessElectronServer.registerChannel(UserConfigurationFileServiceId, ProxyChannel.fromService(new UserConfigurationFileService(this.environmentMainService, this.fileService, this.logService))); + // User Configuration File + const userConfigurationFileService = new UserConfigurationFileService(this.environmentMainService, this.fileService, this.logService); + mainProcessElectronServer.registerChannel(UserConfigurationFileServiceId, ProxyChannel.fromService(userConfigurationFileService)); + sharedProcessClient.then(client => client.registerChannel(UserConfigurationFileServiceId, ProxyChannel.fromService(userConfigurationFileService))); // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); diff --git a/src/vs/platform/configuration/common/userConfigurationFileService.ts b/src/vs/platform/configuration/common/userConfigurationFileService.ts index c826c7fbd87..12245ff5e55 100644 --- a/src/vs/platform/configuration/common/userConfigurationFileService.ts +++ b/src/vs/platform/configuration/common/userConfigurationFileService.ts @@ -8,9 +8,8 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { JSONPath, parse, ParseError } from 'vs/base/common/json'; import { setProperty } from 'vs/base/common/jsonEdit'; import { Edit, FormattingOptions } from 'vs/base/common/jsonFormatter'; -import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; +import { FileOperationError, FileOperationResult, IFileService, IWriteFileOptions } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -31,6 +30,7 @@ export interface IUserConfigurationFileService { readonly _serviceBrand: undefined; updateSettings(value: IJSONValue, formattingOptions: FormattingOptions): Promise; + write(value: VSBuffer, options?: IWriteFileOptions): Promise; } export class UserConfigurationFileService implements IUserConfigurationFileService { @@ -48,12 +48,12 @@ export class UserConfigurationFileService implements IUserConfigurationFileServi } async updateSettings(value: IJSONValue, formattingOptions: FormattingOptions): Promise { - return this.queue.queue(() => this.doWrite(this.environmentService.settingsResource, value, formattingOptions)); // queue up writes to prevent race conditions + return this.queue.queue(() => this.doWrite(value, formattingOptions)); // queue up writes to prevent race conditions } - private async doWrite(resource: URI, jsonValue: IJSONValue, formattingOptions: FormattingOptions): Promise { - this.logService.trace(`${UserConfigurationFileServiceId}#write`, resource.toString(), jsonValue); - const { value, mtime, etag } = await this.fileService.readFile(resource, { atomic: true }); + private async doWrite(jsonValue: IJSONValue, formattingOptions: FormattingOptions): Promise { + this.logService.trace(`${UserConfigurationFileServiceId}#write`, this.environmentService.settingsResource.toString(), jsonValue); + const { value, mtime, etag } = await this.fileService.readFile(this.environmentService.settingsResource, { atomic: true }); let content = value.toString(); const parseErrors: ParseError[] = []; @@ -66,7 +66,7 @@ export class UserConfigurationFileService implements IUserConfigurationFileServi if (edit) { content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length); try { - await this.fileService.writeFile(resource, VSBuffer.fromString(content), { etag, mtime }); + await this.write(VSBuffer.fromString(content), { etag, mtime }); } catch (error) { if ((error).fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) { throw new Error(UserConfigurationErrorCode.ERROR_FILE_MODIFIED_SINCE); @@ -75,6 +75,10 @@ export class UserConfigurationFileService implements IUserConfigurationFileServi } } + async write(content: VSBuffer, options?: IWriteFileOptions): Promise { + await this.fileService.writeFile(this.environmentService.settingsResource, content, options); + } + private getEdits({ value, path }: IJSONValue, modelContent: string, formattingOptions: FormattingOptions): Edit[] { if (path.length) { return setProperty(modelContent, path, value, formattingOptions); diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index f0dc7fdef4a..6b15b6c5549 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -760,7 +760,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { try { if (oldContent) { // file exists already - await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), force ? undefined : oldContent); + await this.writeFileContent(newContent, oldContent, force); } else { // file does not exist await this.fileService.createFile(this.file, VSBuffer.fromString(newContent), { overwrite: force }); @@ -775,6 +775,10 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { } } + protected async writeFileContent(newContent: string, oldContent: IFileContent, force: boolean): Promise { + await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), force ? undefined : oldContent); + } + private onFileChanges(e: FileChangesEvent): void { if (!e.contains(this.file)) { return; diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 31da84131e4..bb7505765f2 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -12,9 +12,10 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; +import { IUserConfigurationFileService } from 'vs/platform/configuration/common/userConfigurationFileService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; +import { FileOperationError, FileOperationResult, IFileContent, IFileService } from 'vs/platform/files/common/files'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; @@ -62,6 +63,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IUserConfigurationFileService private readonly userConfigurationFileService: IUserConfigurationFileService, ) { super(environmentService.settingsResource, SyncResource.Settings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); } @@ -325,6 +327,10 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement return getIgnoredSettings(defaultIgnoredSettings, this.configurationService, content); } + protected override async writeFileContent(newContent: string, oldContent: IFileContent, force: boolean): Promise { + await this.userConfigurationFileService.write(VSBuffer.fromString(newContent), force ? undefined : { etag: oldContent.etag, mtime: oldContent.mtime }); + } + private validateContent(content: string): void { if (this.hasErrors(content)) { throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource);