From 9979c970d6383af05aa6b0c0f1b495df3951fabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Thu, 9 Nov 2023 14:56:47 +0100 Subject: [PATCH] feat: add a way to initialize configuration and keybindings before the services are initialized --- src/service-override/configuration.ts | 22 +++++++++++++++++++--- src/service-override/files.ts | 25 ++++++++++++++++++++++++- src/service-override/keybindings.ts | 26 ++++++++++++++++++++++---- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/service-override/configuration.ts b/src/service-override/configuration.ts index bf07e07d..37378ece 100644 --- a/src/service-override/configuration.ts +++ b/src/service-override/configuration.ts @@ -8,7 +8,7 @@ import { ConfigurationScope } from 'vscode/src/vs/platform/configuration/common/ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationNode, IConfigurationDefaults } from 'vs/platform/configuration/common/configurationRegistry' import { Registry } from 'vs/platform/registry/common/platform' import { VSBuffer } from 'vs/base/common/buffer' -import { IFileService } from 'vs/platform/files/common/files' +import { IFileService, IFileWriteOptions } from 'vs/platform/files/common/files' import { ILogService } from 'vs/platform/log/common/log' import { IColorCustomizations, IThemeScopedColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService' import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile' @@ -32,12 +32,26 @@ import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspace import { URI } from 'vs/base/common/uri' import 'vs/workbench/api/common/configurationExtensionPoint' import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService' -import getFileServiceOverride from './files' +import getFileServiceOverride, { initFile } from './files' import { memoizedConstructor, unsupported } from '../tools' import { registerServiceInitializePreParticipant } from '../lifecycle' +import { getService } from '../services' +// This is the default value, but can be overriden by overriding the Environment or UserDataProfileService service +const defaultUserConfigurationFile = URI.from({ scheme: Schemas.vscodeUserData, path: '/User/settings.json' }) + +/** + * Should be called only BEFORE the service are initialized to initialize the file on the filesystem before the configuration service initializes + */ +async function initUserConfiguration (configurationJson: string, options?: Partial, file: URI = defaultUserConfigurationFile): Promise { + await initFile(defaultUserConfigurationFile.scheme, file, configurationJson, options) +} + +/** + * Can be called at any time after the services are initialized to update the user configuration + */ async function updateUserConfiguration (configurationJson: string): Promise { - const userDataProfilesService = StandaloneServices.get(IUserDataProfilesService) + const userDataProfilesService = await getService(IUserDataProfilesService) await StandaloneServices.get(IFileService).writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(configurationJson)) } @@ -125,6 +139,8 @@ export default function getServiceOverride (defaultWorkspace: URI | IAnyWorkspac } export { + defaultUserConfigurationFile, + initUserConfiguration, updateUserConfiguration, getUserConfiguration, onUserConfigurationChange, diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 842fc50b..49ed7df2 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -460,11 +460,16 @@ const fileSystemProvider = new OverlayFileSystemProvider() fileSystemProvider.register(0, new MkdirpOnWriteInMemoryFileSystemProvider()) const extensionFileSystemProvider = new RegisteredFileSystemProvider(true) +const userDataFileSystemProvider = new InMemoryFileSystemProvider() + +// Initialize /User/ folder to be able to write configuration and keybindings in it before the fileService is initialized +// The `mkdirp` logic is inside the service, and the provider will just fail if asked to write a file in a non-existent directory +void userDataFileSystemProvider.mkdir(URI.from({ scheme: Schemas.vscodeUserData, path: '/User/' })) const providers: Record = { extension: extensionFileSystemProvider, logs: new InMemoryFileSystemProvider(), - [Schemas.vscodeUserData]: new InMemoryFileSystemProvider(), + [Schemas.vscodeUserData]: userDataFileSystemProvider, [Schemas.tmp]: new InMemoryFileSystemProvider(), file: fileSystemProvider } @@ -503,6 +508,24 @@ export function registerExtensionFile (extensionLocation: URI, filePath: string, return extensionFileSystemProvider.registerFile(new RegisteredReadOnlyFile(joinPath(extensionLocation, filePath), getContent)) } +/** + * Can be used to create a file before the fileService is initialized + */ +export async function initFile (scheme: string, file: URI, content: Uint8Array | string, options?: Partial): Promise { + const provider = providers[scheme] + if (provider == null || provider.writeFile == null) { + throw new Error(`${scheme} provider doesn't exist or doesn't support writing files`) + } + + await provider.writeFile(file, content instanceof Uint8Array ? content : encoder.encode(content), { + atomic: false, + create: true, + overwrite: false, + unlock: false, + ...options + }) +} + /** * Register a file system overlay * diff --git a/src/service-override/keybindings.ts b/src/service-override/keybindings.ts index 9268d68f..c766e06b 100644 --- a/src/service-override/keybindings.ts +++ b/src/service-override/keybindings.ts @@ -6,7 +6,7 @@ import { VSBuffer } from 'vs/base/common/buffer' import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile' import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout' import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/browser/keyboardLayoutService' -import { IFileService } from 'vs/platform/files/common/files' +import { IFileService, IFileWriteOptions } from 'vs/platform/files/common/files' import { ICommandService } from 'vs/platform/commands/common/commands' import { CommandService } from 'vs/workbench/services/commands/common/commandService' import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem' @@ -21,12 +21,28 @@ import { IHostService } from 'vs/workbench/services/host/browser/host' import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions' import { ILogService } from 'vs/platform/log/common/log' import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys' -import getFileServiceOverride from './files' +import { Schemas } from 'vs/base/common/network' +import { URI } from 'vs/base/common/uri' +import getFileServiceOverride, { initFile } from './files' import { DynamicKeybindingService } from '../monaco' -import 'vs/workbench/browser/workbench.contribution' -import 'vs/workbench/contrib/keybindings/browser/keybindings.contribution' import { onRenderWorkbench } from '../lifecycle' import { IInstantiationService } from '../services' +import 'vs/workbench/browser/workbench.contribution' +import 'vs/workbench/contrib/keybindings/browser/keybindings.contribution' + +// This is the default value, but can be overriden by overriding the Environment or UserDataProfileService service +const defaultUserKeybindindsFile = URI.from({ scheme: Schemas.vscodeUserData, path: '/User/keybindings.json' }) + +/** + * Should be called only BEFORE the service are initialized to initialize the file on the filesystem before the keybindings service initializes + */ +async function initUserKeybindings (configurationJson: string, options?: Partial, file: URI = defaultUserKeybindindsFile): Promise { + await initFile(defaultUserKeybindindsFile.scheme, file, configurationJson, options) +} + +/** + * Can be called at any time after the services are initialized to update the user configuration + */ async function updateUserKeybindings (keybindingsJson: string): Promise { const userDataProfilesService: IUserDataProfilesService = StandaloneServices.get(IUserDataProfilesService) @@ -100,6 +116,8 @@ export default function getServiceOverride ({ shouldUseGlobalKeybindings = () => } export { + defaultUserKeybindindsFile, + initUserKeybindings, updateUserKeybindings, IUserFriendlyKeybinding }