From e039f3f75f154b078a5b101103876dedefa0fd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 16:40:26 +0100 Subject: [PATCH 01/18] fix: start editor factory --- src/lifecycle.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lifecycle.ts b/src/lifecycle.ts index bf3ad2d2..313de522 100644 --- a/src/lifecycle.ts +++ b/src/lifecycle.ts @@ -4,6 +4,7 @@ import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecyc import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation' import { Barrier, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async' import { Emitter } from 'vs/base/common/event' +import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor' const renderWorkbenchEmitter = new Emitter() export const onRenderWorkbench = renderWorkbenchEmitter.event @@ -52,6 +53,7 @@ export async function startup (instantiationService: IInstantiationService): Pro const lifecycleService = accessor.get(ILifecycleService) Registry.as(WorkbenchExtensions.Workbench).start(accessor) + Registry.as(EditorExtensions.EditorFactory).start(accessor) renderWorkbenchEmitter.fire(accessor) From 463d31cdfea2293544604e1c57202ef31379856e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:33:35 +0100 Subject: [PATCH 02/18] fix: no need to replace APP_SETTINGS_HOME anymore --- rollup/rollup.default-extensions.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/rollup/rollup.default-extensions.ts b/rollup/rollup.default-extensions.ts index eabe7a5f..885604f0 100644 --- a/rollup/rollup.default-extensions.ts +++ b/rollup/rollup.default-extensions.ts @@ -83,20 +83,6 @@ export default rollup.defineConfig([ extensionDirectoryPlugin({ include: `${DEFAULT_EXTENSIONS_PATH}/**/*`, transformManifest (manifest) { - if (manifest.name === 'configuration-editing') { - manifest = { - ...manifest, - contributes: { - ...manifest.contributes, - jsonValidation: manifest.contributes!.jsonValidation!.map(validation => { - return { - fileMatch: (validation.fileMatch as string).replaceAll('%APP_SETTINGS_HOME%', 'user:'), - url: validation.url - } - }) - } - } - } return { ...manifest, main: undefined From 35fcde2e7a59062b35f402a9b708a2fc676d7b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:35:33 +0100 Subject: [PATCH 03/18] feat: provide workbench contruction option when initializing services --- src/services.ts | 10 ++++++++-- src/workbench.ts | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/workbench.ts diff --git a/src/services.ts b/src/services.ts index 2d3acfca..3cc01f33 100644 --- a/src/services.ts +++ b/src/services.ts @@ -8,12 +8,14 @@ import { IEditorOverrideServices, StandaloneServices } from 'vs/editor/standalon import { GetLeadingNonServiceArgs, IInstantiationService, ServiceIdentifier, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation' import { IAction } from 'vs/base/common/actions' import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle' +import { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api' import getLayoutServiceOverride from './service-override/layout' import getEnvironmentServiceOverride from './service-override/environment' import getExtensionsServiceOverride from './service-override/extensions' import getFileServiceOverride from './service-override/files' import getQuickAccessOverride from './service-override/quickaccess' import { serviceInitializedBarrier, serviceInitializedEmitter, startup } from './lifecycle' +import { initialize as initializeWorkbench } from './workbench' let servicesInitialized = false StandaloneServices.withServices(() => { @@ -21,10 +23,13 @@ StandaloneServices.withServices(() => { return Disposable.None }) -export async function initialize (overrides: IEditorOverrideServices, container?: HTMLElement): Promise { +export async function initialize (overrides: IEditorOverrideServices, container: HTMLElement = document.body, configuration: IWorkbenchConstructionOptions = {}): Promise { if (servicesInitialized) { throw new Error('The services are already initialized.') } + + initializeWorkbench(container, configuration) + const instantiationService = StandaloneServices.initialize({ ...getLayoutServiceOverride(container), // Always override layout service to break cyclic dependency with ICodeEditorService ...getEnvironmentServiceOverride(), @@ -248,5 +253,6 @@ export { IColorTheme, StorageScope, StorageTarget, - Severity + Severity, + IWorkbenchConstructionOptions } diff --git a/src/workbench.ts b/src/workbench.ts new file mode 100644 index 00000000..abf354db --- /dev/null +++ b/src/workbench.ts @@ -0,0 +1,51 @@ +import './missing-services' +import { IAnyWorkspaceIdentifier, UNKNOWN_EMPTY_WINDOW_WORKSPACE } from 'vs/platform/workspace/common/workspace' +import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService' +import { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api' +import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window' +import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier as getWorkspaceIdentifierFromUri } from 'vs/workbench/services/workspaces/browser/workspaces' +import { URI } from 'vs/base/common/uri' +import { toLocalISOString } from 'vs/base/common/date' + +let workbenchContainer: HTMLElement = document.body +let workbenchConstructionOptions: IWorkbenchConstructionOptions = {} +let workspaceIdentifier: IAnyWorkspaceIdentifier = UNKNOWN_EMPTY_WINDOW_WORKSPACE +export const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: 'vscode-log' }) + +export function getWorkbenchContainer (): HTMLElement { + return workbenchContainer +} + +export function getWorkbenchConstructionOptions (): IWorkbenchConstructionOptions { + return workbenchConstructionOptions +} + +export function getWorkspaceIdentifier (): IAnyWorkspaceIdentifier { + return workspaceIdentifier +} + +function resolveWorkspace (configuration: IWorkbenchConstructionOptions): IAnyWorkspaceIdentifier { + let workspace: IWorkspace | undefined + if (configuration.workspaceProvider != null) { + workspace = configuration.workspaceProvider.workspace + } + + // Multi-root workspace + if (workspace != null && isWorkspaceToOpen(workspace)) { + return getWorkspaceIdentifierFromUri(workspace.workspaceUri) + } + + // Single-folder workspace + if (workspace != null && isFolderToOpen(workspace)) { + return getSingleFolderWorkspaceIdentifier(workspace.folderUri) + } + + // Empty window workspace + return UNKNOWN_EMPTY_WINDOW_WORKSPACE +} + +export function initialize (container: HTMLElement, options: IWorkbenchConstructionOptions): void { + workbenchContainer = container + workbenchConstructionOptions = options + workspaceIdentifier = resolveWorkspace(options) +} From f20bf794ba8a00f8f5260b7b480d9550001bb90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:38:48 +0100 Subject: [PATCH 04/18] feat: use workbench contruction option when initializing services --- rollup/rollup.config.ts | 9 ++++++++- src/service-override/configuration.ts | 27 ++++++++++++++++++++------- src/service-override/dialogs.ts | 4 ++-- src/service-override/environment.ts | 18 +++++++++++++----- src/service-override/files.ts | 3 ++- src/service-override/layout.ts | 20 ++++++++++++++------ src/service-override/notifications.ts | 4 ++-- src/service-override/output.ts | 19 ++++++++++++++----- src/service-override/storage.ts | 6 ++---- src/services.ts | 2 +- 10 files changed, 78 insertions(+), 34 deletions(-) diff --git a/rollup/rollup.config.ts b/rollup/rollup.config.ts index 8d979484..6213a161 100644 --- a/rollup/rollup.config.ts +++ b/rollup/rollup.config.ts @@ -859,6 +859,10 @@ export default (args: Record): rollup.RollupOptions[] => { types: './lifecycle.d.ts', default: './lifecycle.js' }, + './workbench': { + types: './workbench.d.ts', + default: './workbench.js' + }, './service-override/*': { types: './service-override/*.d.ts', default: './service-override/*.js' @@ -898,6 +902,9 @@ export default (args: Record): rollup.RollupOptions[] => { lifecycle: [ './lifecycle.d.ts' ], + workbench: [ + './workbench.d.ts' + ], l10n: [ './l10n.d.ts' ], @@ -988,7 +995,7 @@ export default (args: Record): rollup.RollupOptions[] => { const resolvedWithExtension = resolved.endsWith('.js') ? resolved : `${resolved}.js` const isNotExclusive = (resolved.startsWith(VSCODE_SRC_DIST_DIR) || path.dirname(resolved) === path.resolve(DIST_DIR_MAIN, 'service-override')) && !exclusiveModules.has(resolvedWithExtension) - const shouldBeShared = resolvedWithExtension === path.resolve(DIST_DIR_MAIN, 'assets.js') || resolvedWithExtension === path.resolve(DIST_DIR_MAIN, 'lifecycle.js') + const shouldBeShared = resolvedWithExtension === path.resolve(DIST_DIR_MAIN, 'assets.js') || resolvedWithExtension === path.resolve(DIST_DIR_MAIN, 'lifecycle.js') || resolvedWithExtension === path.resolve(DIST_DIR_MAIN, 'workbench.js') if (isNotExclusive || shouldBeShared) { // Those modules will be imported from external monaco-vscode-api diff --git a/src/service-override/configuration.ts b/src/service-override/configuration.ts index 1431739f..cd3bd596 100644 --- a/src/service-override/configuration.ts +++ b/src/service-override/configuration.ts @@ -36,6 +36,7 @@ import getFileServiceOverride, { initFile } from './files' import { memoizedConstructor, unsupported } from '../tools' import { registerServiceInitializePreParticipant } from '../lifecycle' import { getService } from '../services' +import { getWorkspaceIdentifier } from '../workbench' // 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' }) @@ -91,21 +92,25 @@ class MonacoWorkspaceEditingService extends AbstractWorkspaceEditingService { enterWorkspace = unsupported } -let _defaultWorkspace: URI | IAnyWorkspaceIdentifier = URI.file('/workspace') +/** + * @deprecated + */ +let _defaultWorkspace: URI | IAnyWorkspaceIdentifier | undefined registerServiceInitializePreParticipant(async (accessor) => { const workspaceService = accessor.get(IWorkspaceContextService) as WorkspaceService workspaceService.acquireInstantiationService(accessor.get(IInstantiationService)) - if (URI.isUri(_defaultWorkspace)) { - const configPath = _defaultWorkspace.with({ path: '/workspace.code-workspace' }) + const workspace = _defaultWorkspace ?? getWorkspaceIdentifier() + if (URI.isUri(workspace)) { + const configPath = workspace.with({ path: '/workspace.code-workspace' }) try { const fileService = accessor.get(IFileService) // Create the directory in the memory filesystem to prevent a warn log - await fileService.createFolder(_defaultWorkspace) + await fileService.createFolder(workspace) await fileService.writeFile(configPath, VSBuffer.fromString(JSON.stringify({ folders: [ { - path: _defaultWorkspace.path + path: workspace.path } ] }))) @@ -118,7 +123,7 @@ registerServiceInitializePreParticipant(async (accessor) => { configPath }) } else { - await workspaceService.initialize(_defaultWorkspace) + await workspaceService.initialize(workspace) } }) @@ -129,7 +134,13 @@ export async function reinitializeWorkspace (workspace: IAnyWorkspaceIdentifier) await workspaceService.initialize(workspace) } -export default function getServiceOverride (defaultWorkspace: URI | IAnyWorkspaceIdentifier): IEditorOverrideServices { +function getServiceOverride (): IEditorOverrideServices +/** + * @deprecated Provide workspace via the services `initialize` function `configuration.workspaceProvider` parameter + */ +function getServiceOverride (defaultWorkspace?: URI | IAnyWorkspaceIdentifier): IEditorOverrideServices + +function getServiceOverride (defaultWorkspace?: URI | IAnyWorkspaceIdentifier): IEditorOverrideServices { _defaultWorkspace = defaultWorkspace return { @@ -143,6 +154,8 @@ export default function getServiceOverride (defaultWorkspace: URI | IAnyWorkspac } } +export default getServiceOverride + export { defaultUserConfigurationFile, initUserConfiguration, diff --git a/src/service-override/dialogs.ts b/src/service-override/dialogs.ts index 99c99709..7cdb0c2a 100644 --- a/src/service-override/dialogs.ts +++ b/src/service-override/dialogs.ts @@ -60,10 +60,10 @@ class FileDialogService extends AbstractFileDialogService { } } -export default function getServiceOverride (container?: HTMLElement): IEditorOverrideServices { +export default function getServiceOverride (): IEditorOverrideServices { return { [IDialogService.toString()]: new SyncDescriptor(DialogService, undefined, true), [IFileDialogService.toString()]: new SyncDescriptor(FileDialogService, undefined, true), - ...getLayoutServiceOverride(container) + ...getLayoutServiceOverride() } } diff --git a/src/service-override/environment.ts b/src/service-override/environment.ts index e367a854..5237bbe7 100644 --- a/src/service-override/environment.ts +++ b/src/service-override/environment.ts @@ -1,24 +1,32 @@ import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices' import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' import { BrowserWorkbenchEnvironmentService, IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService' -import { URI } from 'vs/base/common/uri' import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IProductService } from 'vs/platform/product/common/productService' import { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api' +import { getWorkbenchConstructionOptions, getWorkspaceIdentifier, logsPath } from '../workbench' class InjectedBrowserWorkbenchEnvironmentService extends BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { - constructor (options: IWorkbenchConstructionOptions, @IProductService productService: IProductService) { - super('default', URI.from({ scheme: 'logs', path: '/' }), options, productService) + constructor (workspaceId: string = getWorkspaceIdentifier().id, options: IWorkbenchConstructionOptions = getWorkbenchConstructionOptions(), @IProductService productService: IProductService) { + super(workspaceId, logsPath, options, productService) } } -export default function getServiceOverride (options: IWorkbenchConstructionOptions = {}): IEditorOverrideServices { +/** + * @deprecated Provide construction option via the services `initialize` function `configuration` parameter + */ +function getServiceOverride (options: IWorkbenchConstructionOptions): IEditorOverrideServices +function getServiceOverride (): IEditorOverrideServices + +function getServiceOverride (options?: IWorkbenchConstructionOptions): IEditorOverrideServices { return { [IEnvironmentService.toString()]: new SyncDescriptor(InjectedBrowserWorkbenchEnvironmentService, [], true), - [IBrowserWorkbenchEnvironmentService.toString()]: new SyncDescriptor(InjectedBrowserWorkbenchEnvironmentService, [options], true) + [IBrowserWorkbenchEnvironmentService.toString()]: new SyncDescriptor(InjectedBrowserWorkbenchEnvironmentService, [undefined, options], true) } } +export default getServiceOverride + export { IWorkbenchConstructionOptions } diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 49ed7df2..d92f8357 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -15,6 +15,7 @@ import 'vs/workbench/contrib/files/browser/files.contribution.js?include=registe import { Schemas } from 'vs/base/common/network' import { IndexedDBFileSystemProvider } from 'vs/platform/files/browser/indexedDBFileSystemProvider' import { IndexedDB } from 'vs/base/browser/indexedDB' +import { logsPath } from '../workbench' abstract class RegisteredFile { private ctime: number @@ -468,7 +469,7 @@ void userDataFileSystemProvider.mkdir(URI.from({ scheme: Schemas.vscodeUserData, const providers: Record = { extension: extensionFileSystemProvider, - logs: new InMemoryFileSystemProvider(), + [logsPath.scheme]: new InMemoryFileSystemProvider(), [Schemas.vscodeUserData]: userDataFileSystemProvider, [Schemas.tmp]: new InMemoryFileSystemProvider(), file: fileSystemProvider diff --git a/src/service-override/layout.ts b/src/service-override/layout.ts index d9dbd6b0..a90031b9 100644 --- a/src/service-override/layout.ts +++ b/src/service-override/layout.ts @@ -16,6 +16,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IStatusbarService } from 'vs/workbench/services/statusbar/browser/statusbar' import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation' import { onRenderWorkbench } from '../lifecycle' +import { getWorkbenchContainer } from '../workbench' export class LayoutService implements ILayoutService, IWorkbenchLayoutService { declare readonly _serviceBrand: undefined @@ -26,7 +27,7 @@ export class LayoutService implements ILayoutService, IWorkbenchLayoutService { private viewDescriptorService!: IViewDescriptorService constructor ( - public container: HTMLElement + public container: HTMLElement = getWorkbenchContainer() ) { window.addEventListener('resize', () => this.layout()) this.layout() @@ -280,9 +281,7 @@ onRenderWorkbench((accessor) => { if (layoutService instanceof LayoutService) { layoutService.init(accessor) } -}) -export default function getServiceOverride (container: HTMLElement = document.body): IEditorOverrideServices { const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac' const workbenchClasses = coalesce([ 'monaco-workbench', @@ -291,12 +290,21 @@ export default function getServiceOverride (container: HTMLElement = document.bo isChrome ? 'chromium' : isFirefox ? 'firefox' : isSafari ? 'safari' : undefined ]) - container.classList.add(...workbenchClasses) + layoutService.container.classList.add(...workbenchClasses) document.body.classList.add(platformClass) document.body.classList.add('web') +}) +function getServiceOverride (): IEditorOverrideServices +/** + * @deprecated Provide container via the services `initialize` function + */ +function getServiceOverride (container?: HTMLElement): IEditorOverrideServices + +function getServiceOverride (container?: HTMLElement): IEditorOverrideServices { return { - [ILayoutService.toString()]: new SyncDescriptor(LayoutService, [container], true), - [IWorkbenchLayoutService.toString()]: new SyncDescriptor(LayoutService, [container], true) + [ILayoutService.toString()]: new SyncDescriptor(LayoutService, [container], true) } } + +export default getServiceOverride diff --git a/src/service-override/notifications.ts b/src/service-override/notifications.ts index b0a38fad..ea7a96b4 100644 --- a/src/service-override/notifications.ts +++ b/src/service-override/notifications.ts @@ -31,9 +31,9 @@ onRenderWorkbench(async (accessor) => { }) }) -export default function getServiceOverride (container?: HTMLElement): IEditorOverrideServices { +export default function getServiceOverride (): IEditorOverrideServices { return { [INotificationService.toString()]: new SyncDescriptor(NotificationService, undefined, true), - ...getLayoutServiceOverride(container) + ...getLayoutServiceOverride() } } diff --git a/src/service-override/output.ts b/src/service-override/output.ts index 188b6292..f2a9a621 100644 --- a/src/service-override/output.ts +++ b/src/service-override/output.ts @@ -1,19 +1,28 @@ import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices' import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' -import { URI } from 'vs/base/common/uri' import { IFileService } from 'vs/platform/files/common/files' -import { ILoggerService, LogLevel } from 'vs/platform/log/common/log' +import { ILoggerService, LogLevel, getLogLevel } from 'vs/platform/log/common/log' import { FileLoggerService } from 'vs/platform/log/common/fileLog' import 'vs/workbench/contrib/output/browser/output.contribution' +import { IEnvironmentService } from 'vs/platform/environment/common/environment' +import { logsPath } from '../workbench' class _FileLoggerService extends FileLoggerService { - constructor (logLevel: LogLevel, @IFileService fileService: IFileService) { - super(logLevel, URI.from({ scheme: 'logs', path: '/' }), fileService) + constructor (logLevel: LogLevel | undefined, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService) { + super(logLevel ?? getLogLevel(environmentService), logsPath, fileService) } } -export default function getServiceOverride (logLevel: LogLevel = LogLevel.Info): IEditorOverrideServices { +function getServiceOverride (): IEditorOverrideServices +/** + * @deprecated Provide logLevel via the services `initialize` function `configuration.developmentOptions.logLevel` parameter + */ +function getServiceOverride (logLevel?: LogLevel): IEditorOverrideServices + +function getServiceOverride (logLevel?: LogLevel): IEditorOverrideServices { return { [ILoggerService.toString()]: new SyncDescriptor(_FileLoggerService, [logLevel], true) } } + +export default getServiceOverride diff --git a/src/service-override/storage.ts b/src/service-override/storage.ts index 600a708b..e39201d8 100644 --- a/src/service-override/storage.ts +++ b/src/service-override/storage.ts @@ -9,8 +9,8 @@ import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace' import { BrowserStorageService } from 'vs/workbench/services/storage/browser/storageService' import { ILogService } from 'vs/platform/log/common/log' import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile' -import { generateUuid } from 'vs/base/common/uuid' import { registerServiceInitializePreParticipant } from '../lifecycle' +import { getWorkspaceIdentifier } from '../workbench' export enum StorageScope { APPLICATION = VSStorageScope.APPLICATION, @@ -138,9 +138,7 @@ class InjectedBrowserStorageService extends BrowserStorageService { @IUserDataProfileService userDataProfileService: IUserDataProfileService, @ILogService logService: ILogService ) { - super({ - id: generateUuid() - }, userDataProfileService, logService) + super(getWorkspaceIdentifier(), userDataProfileService, logService) } } diff --git a/src/services.ts b/src/services.ts index 3cc01f33..51cd2152 100644 --- a/src/services.ts +++ b/src/services.ts @@ -31,7 +31,7 @@ export async function initialize (overrides: IEditorOverrideServices, container: initializeWorkbench(container, configuration) const instantiationService = StandaloneServices.initialize({ - ...getLayoutServiceOverride(container), // Always override layout service to break cyclic dependency with ICodeEditorService + ...getLayoutServiceOverride(), // Always override layout service to break cyclic dependency with ICodeEditorService ...getEnvironmentServiceOverride(), ...getExtensionsServiceOverride(), ...getFileServiceOverride(), From 0ea4e7a993ca07aa497e5bfdfb626797aeb261f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:40:46 +0100 Subject: [PATCH 05/18] feat: flush storage service on focus change like in vscode --- src/service-override/storage.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/service-override/storage.ts b/src/service-override/storage.ts index e39201d8..c57bc676 100644 --- a/src/service-override/storage.ts +++ b/src/service-override/storage.ts @@ -11,6 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log' import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile' import { registerServiceInitializePreParticipant } from '../lifecycle' import { getWorkspaceIdentifier } from '../workbench' +import { IHostService } from '../services' export enum StorageScope { APPLICATION = VSStorageScope.APPLICATION, @@ -130,7 +131,17 @@ class ExternalStorageService extends AbstractStorageService { } registerServiceInitializePreParticipant(async (accessor) => { - await (accessor.get(IStorageService) as ExternalStorageService).initialize() + const storageService = accessor.get(IStorageService) + const hostService = accessor.get(IHostService) + if (storageService instanceof AbstractStorageService) { + await storageService.initialize() + + hostService.onDidChangeFocus(focus => { + if (!focus) { + void storageService.flush() + } + }) + } }) class InjectedBrowserStorageService extends BrowserStorageService { From bdbeccc240e0dddca3bf360c9271e8b2d5864b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:41:42 +0100 Subject: [PATCH 06/18] feat: add a way to register IndexedDB providers like in for user data and logs --- src/service-override/files.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index d92f8357..db32ee53 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -13,8 +13,9 @@ import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystem import * as path from 'vs/base/common/path' import 'vs/workbench/contrib/files/browser/files.contribution.js?include=registerConfiguration' import { Schemas } from 'vs/base/common/network' -import { IndexedDBFileSystemProvider } from 'vs/platform/files/browser/indexedDBFileSystemProvider' +import { IndexedDBFileSystemProvider, IndexedDBFileSystemProviderErrorData, IndexedDBFileSystemProviderErrorDataClassification } from 'vs/platform/files/browser/indexedDBFileSystemProvider' import { IndexedDB } from 'vs/base/browser/indexedDB' +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' import { logsPath } from '../workbench' abstract class RegisteredFile { @@ -476,7 +477,7 @@ const providers: Record = { } class MemoryFileService extends FileService { - constructor (@ILogService logService: ILogService) { + constructor (@ILogService logService: ILogService, @ITelemetryService telemetryService: ITelemetryService) { super(logService) for (const [scheme, provider] of Object.entries(providers)) { @@ -487,6 +488,10 @@ class MemoryFileService extends FileService { disposable = this.registerProvider(scheme, fileSystemProvider) }) } + + if (provider instanceof IndexedDBFileSystemProvider) { + this._register(provider.onReportError(e => telemetryService.publicLog2('indexedDBFileSystemProviderError', e))) + } } } } @@ -527,6 +532,21 @@ export async function initFile (scheme: string, file: URI, content: Uint8Array | }) } +/** + * Can be used to replace memory providers by indexeddb providers before the fileService is initialized + */ +export async function createIndexedDBProviders (): Promise { + const userDataStore = 'vscode-userdata-store' + const logsStore = 'vscode-logs-store' + const indexedDB = await IndexedDB.create('vscode-web-db', 3, [userDataStore, logsStore]) + + // Logger + providers[logsPath.scheme] = new IndexedDBFileSystemProvider(logsPath.scheme, indexedDB, logsStore, false) + + const userDataProvider = new IndexedDBFileSystemProvider(Schemas.vscodeUserData, indexedDB, userDataStore, true) + providers[Schemas.vscodeUserData] = userDataProvider +} + /** * Register a file system overlay * From 27cedfb05036e6e66208e2ebe5dd980869a63bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:42:27 +0100 Subject: [PATCH 07/18] feat: do not overwrite file when initializing it --- src/service-override/files.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index db32ee53..effaa170 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -522,6 +522,14 @@ export async function initFile (scheme: string, file: URI, content: Uint8Array | if (provider == null || provider.writeFile == null) { throw new Error(`${scheme} provider doesn't exist or doesn't support writing files`) } + if (!(options?.overwrite ?? false)) { + try { + await provider.stat(file) + // The file already exists, do nothing + return + } catch (error) { + } + } await provider.writeFile(file, content instanceof Uint8Array ? content : encoder.encode(content), { atomic: false, From 4989be956e00b66cce7790aef7e7810fffe3e974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:43:12 +0100 Subject: [PATCH 08/18] fix: create view parts later so that editor serializer registry is ready --- src/service-override/views.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service-override/views.ts b/src/service-override/views.ts index 9c4c827b..46bf6fbf 100644 --- a/src/service-override/views.ts +++ b/src/service-override/views.ts @@ -100,7 +100,7 @@ import getQuickAccessOverride from './quickaccess' import getKeybindingsOverride from './keybindings' import { changeUrlDomain } from './tools/url' import { registerAssets } from '../assets' -import { registerServiceInitializePostParticipant } from '../lifecycle' +import { onRenderWorkbench } from '../lifecycle' import { withReadyServices } from '../services' function createPart (id: string, role: string, classes: string[]): HTMLElement { @@ -621,7 +621,7 @@ registerAssets({ }) let restoreEditorView = false -registerServiceInitializePostParticipant(async (accessor) => { +onRenderWorkbench(async (accessor) => { const paneCompositePartService = accessor.get(IPaneCompositePartService) const viewDescriptorService = accessor.get(IViewDescriptorService) From dc9e96ed9a2614f2c340adae5fb56ad15b189137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:43:48 +0100 Subject: [PATCH 09/18] clean: remove debug code --- src/service-override/views.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/service-override/views.ts b/src/service-override/views.ts index 46bf6fbf..f01b9d68 100644 --- a/src/service-override/views.ts +++ b/src/service-override/views.ts @@ -630,8 +630,6 @@ onRenderWorkbench(async (accessor) => { const withBannerPart = accessor.get(IBannerService) instanceof Part const withTitlePart = accessor.get(ITitleService) instanceof Part - paneCompositePartService.getPaneComposites(ViewContainerLocation.Panel) - const layoutService = accessor.get(ILayoutService) as LayoutService const invisibleContainer = document.createElement('div') From 4b43ecc1d96947f084ddd5038a433e6072024041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:44:01 +0100 Subject: [PATCH 10/18] feat: add a method to registe reditor serializer --- src/service-override/views.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/service-override/views.ts b/src/service-override/views.ts index f01b9d68..467c1772 100644 --- a/src/service-override/views.ts +++ b/src/service-override/views.ts @@ -54,7 +54,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService' import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService' import { EditorInput, IEditorCloseHandler } from 'vs/workbench/common/editor/editorInput' -import { EditorExtensions, EditorInputCapabilities, IEditorOpenContext, Verbosity } from 'vs/workbench/common/editor' +import { EditorExtensions, EditorInputCapabilities, IEditorFactoryRegistry, IEditorOpenContext, IEditorSerializer, Verbosity } from 'vs/workbench/common/editor' import { IEditorOptions } from 'vs/platform/editor/common/editor' import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService' import { ITextEditorService, TextEditorService } from 'vs/workbench/services/textfile/common/textEditorService' @@ -370,6 +370,12 @@ function registerEditor (globPattern: string, editorInfo: RegisteredEditorInfo, }) } +function registerEditorSerializer (editorTypeId: string, ctor: { + new (...Services: Services): IEditorSerializer +}): IDisposable { + return Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(editorTypeId, ctor) +} + interface CustomViewOption { readonly id: string name: string @@ -724,6 +730,8 @@ export { AbstractTextResourceEditorInput, EditorInput, registerEditor, + IEditorSerializer, + registerEditorSerializer, RegisteredEditorInfo, RegisteredEditorOptions, EditorInputFactoryObject, From 23b61a2e3c224f854b576b59acd933d1779357c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:44:34 +0100 Subject: [PATCH 11/18] fix(demo): fix eslint error --- demo/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 0fafef83..1e8f6427 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -59,7 +59,7 @@ export default defineConfig({ // These 2 lines prevent vite from reloading the whole page when starting a worker (so 2 times in a row after cleaning the vite cache - for the editor then the textmate workers) // it's mainly empirical and probably not the best way, fix me if you find a better way 'monaco-editor/esm/vs/nls.js', 'monaco-editor/esm/vs/editor/editor.worker.js', 'vscode-textmate', 'vscode-oniguruma', '@vscode/vscode-languagedetection', - ...(await glob('monaco-editor/esm/vs/**/common/**/*.js', { cwd: path.resolve(__dirname, '../node_modules') })), + ...(await glob('monaco-editor/esm/vs/**/common/**/*.js', { cwd: path.resolve(__dirname, '../node_modules') })) ], exclude: [], esbuildOptions: { From e234b78a9e30b2a52b50db2eef985644344d3ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:45:28 +0100 Subject: [PATCH 12/18] refactor: put default settings/keybinding in decidated files --- demo/src/setup.ts | 33 ++++---------------------------- demo/src/user/configuration.json | 22 +++++++++++++++++++++ demo/src/user/keybindings.json | 7 +++++++ 3 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 demo/src/user/configuration.json create mode 100644 demo/src/user/keybindings.json diff --git a/demo/src/setup.ts b/demo/src/setup.ts index 6b268ec0..b0e3c100 100644 --- a/demo/src/setup.ts +++ b/demo/src/setup.ts @@ -44,6 +44,8 @@ import * as monaco from 'monaco-editor' import { TerminalBackend } from './features/terminal' import { openNewCodeEditor } from './features/editor' import { toCrossOriginWorker, toWorkerConfig } from './tools/workers' +import defaultConfiguration from './user/configuration.json?raw' +import defaultKeybindings from './user/keybindings.json?raw' // Workers export type WorkerLoader = () => Worker @@ -70,35 +72,8 @@ const remotePath = remoteAuthority != null ? params.get('remotePath') ?? undefin // Set configuration before initializing service so it's directly available (especially for the theme, to prevent a flicker) await Promise.all([ - initUserConfiguration(`{ - "workbench.colorTheme": "Default Dark+", - "workbench.iconTheme": "vs-seti", - "editor.autoClosingBrackets": "languageDefined", - "editor.autoClosingQuotes": "languageDefined", - "editor.scrollBeyondLastLine": true, - "editor.mouseWheelZoom": true, - "editor.wordBasedSuggestions": false, - "editor.acceptSuggestionOnEnter": "on", - "editor.foldingHighlight": false, - "editor.semanticHighlighting.enabled": true, - "editor.bracketPairColorization.enabled": false, - "editor.fontSize": 12, - "audioCues.lineHasError": "on", - "audioCues.onDebugBreak": "on", - "files.autoSave": "afterDelay", - "files.autoSaveDelay": 1000, - "debug.toolBarLocation": "docked", - "editor.experimental.asyncTokenization": true, - "terminal.integrated.tabs.title": "\${sequence}", - "typescript.tsserver.log": "normal" - }`), - initUserKeybindings(`[ - { - "key": "ctrl+d", - "command": "editor.action.deleteLines", - "when": "editorTextFocus" - } - ]`) + initUserConfiguration(defaultConfiguration), + initUserKeybindings(defaultKeybindings) ]) // Override services diff --git a/demo/src/user/configuration.json b/demo/src/user/configuration.json new file mode 100644 index 00000000..aabf8638 --- /dev/null +++ b/demo/src/user/configuration.json @@ -0,0 +1,22 @@ +{ + "workbench.colorTheme": "Default Dark+", + "workbench.iconTheme": "vs-seti", + "editor.autoClosingBrackets": "languageDefined", + "editor.autoClosingQuotes": "languageDefined", + "editor.scrollBeyondLastLine": true, + "editor.mouseWheelZoom": true, + "editor.wordBasedSuggestions": false, + "editor.acceptSuggestionOnEnter": "on", + "editor.foldingHighlight": false, + "editor.semanticHighlighting.enabled": true, + "editor.bracketPairColorization.enabled": false, + "editor.fontSize": 12, + "audioCues.lineHasError": "on", + "audioCues.onDebugBreak": "on", + "files.autoSave": "afterDelay", + "files.autoSaveDelay": 1000, + "debug.toolBarLocation": "docked", + "editor.experimental.asyncTokenization": true, + "terminal.integrated.tabs.title": "${sequence}", + "typescript.tsserver.log": "normal" +} \ No newline at end of file diff --git a/demo/src/user/keybindings.json b/demo/src/user/keybindings.json new file mode 100644 index 00000000..0549e1ea --- /dev/null +++ b/demo/src/user/keybindings.json @@ -0,0 +1,7 @@ +[ + { + "key": "ctrl+d", + "command": "editor.action.deleteLines", + "when": "editorTextFocus" + } +] \ No newline at end of file From 61ec89896fa5830e0bf73a525c2c5a6d912311f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:46:09 +0100 Subject: [PATCH 13/18] fix(demo): update service initialization according to api changes --- demo/src/setup.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/demo/src/setup.ts b/demo/src/setup.ts index b0e3c100..948e2043 100644 --- a/demo/src/setup.ts +++ b/demo/src/setup.ts @@ -82,9 +82,7 @@ await initializeMonacoService({ ...getModelServiceOverride(), ...getNotificationServiceOverride(), ...getDialogsServiceOverride(), - ...getConfigurationServiceOverride(remotePath == null - ? monaco.Uri.file('/tmp') - : { id: 'remote-workspace', uri: monaco.Uri.from({ scheme: 'vscode-remote', path: remotePath, authority: remoteAuthority }) }), + ...getConfigurationServiceOverride(), ...getKeybindingsServiceOverride(), ...getTextmateServiceOverride(), ...getThemeServiceOverride(), @@ -92,7 +90,7 @@ await initializeMonacoService({ ...getAudioCueServiceOverride(), ...getDebugServiceOverride(), ...getPreferencesServiceOverride(), - ...getViewsServiceOverride(openNewCodeEditor), + ...getViewsServiceOverride(openNewCodeEditor, undefined, true), ...getBannerServiceOverride(), ...getStatusBarServiceOverride(), ...getTitleBarServiceOverride(), @@ -110,11 +108,20 @@ await initializeMonacoService({ ...getStorageServiceOverride(), ...getRemoteAgentServiceOverride(connectionToken), ...getLifecycleServiceOverride(), - ...getEnvironmentServiceOverride({ - remoteAuthority, - enableWorkspaceTrust: true - }), + ...getEnvironmentServiceOverride(), ...getWorkspaceTrustOverride() +}, document.body, { + remoteAuthority, + enableWorkspaceTrust: true, + workspaceProvider: { + trusted: true, + async open () { + return false + }, + workspace: { + folderUri: remotePath == null ? monaco.Uri.file('/tmp') : monaco.Uri.from({ scheme: 'vscode-remote', path: remotePath, authority: remoteAuthority }) + } + } }) StandaloneServices.get(ILogService).setLevel(LogLevel.Off) From 89dce94e681d2aff076f2d8a7af713631064b620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:46:50 +0100 Subject: [PATCH 14/18] feat(demo): use IndexedDB fs providers --- demo/src/setup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/demo/src/setup.ts b/demo/src/setup.ts index 948e2043..dd927a2d 100644 --- a/demo/src/setup.ts +++ b/demo/src/setup.ts @@ -38,6 +38,7 @@ import getWorkspaceTrustOverride from '@codingame/monaco-vscode-workspace-trust- import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker' import TextMateWorker from '@codingame/monaco-vscode-textmate-service-override/worker?worker' import OutputLinkComputerWorker from '@codingame/monaco-vscode-output-service-override/worker?worker' +import { createIndexedDBProviders } from '@codingame/monaco-vscode-files-service-override' import ExtensionHostWorker from 'vscode/workers/extensionHost.worker?worker' import LanguageDetectionWorker from '@codingame/monaco-vscode-language-detection-worker-service-override/worker?worker' import * as monaco from 'monaco-editor' @@ -47,6 +48,8 @@ import { toCrossOriginWorker, toWorkerConfig } from './tools/workers' import defaultConfiguration from './user/configuration.json?raw' import defaultKeybindings from './user/keybindings.json?raw' +await createIndexedDBProviders() + // Workers export type WorkerLoader = () => Worker const workerLoaders: Partial> = { From 5a46cbf404cb82260da09b12ea0e8b2a55bc93cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:47:24 +0100 Subject: [PATCH 15/18] feat(demo): add reset settings/keybindings buttons --- demo/index.html | 2 ++ demo/src/main.ts | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/demo/index.html b/demo/index.html index 6badb7b6..babbe826 100644 --- a/demo/index.html +++ b/demo/index.html @@ -55,9 +55,11 @@

Editor

Settings

+

Keybindings

+
diff --git a/demo/src/main.ts b/demo/src/main.ts index 513619e5..9a9ed573 100644 --- a/demo/src/main.ts +++ b/demo/src/main.ts @@ -5,8 +5,8 @@ import { registerFileSystemOverlay, HTMLFileSystemProvider } from '@codingame/mo import * as vscode from 'vscode' import { ILogService, StandaloneServices, IPreferencesService, IEditorService, IDialogService, getService, createInstance } from 'vscode/services' import { Parts, isPartVisibile, setPartVisibility } from '@codingame/monaco-vscode-views-service-override' -import { defaultUserConfigurationFile } from '@codingame/monaco-vscode-configuration-service-override' -import { defaultUserKeybindindsFile } from '@codingame/monaco-vscode-keybindings-service-override' +import { defaultUserConfigurationFile, updateUserConfiguration } from '@codingame/monaco-vscode-configuration-service-override' +import { defaultUserKeybindindsFile, updateUserKeybindings } from '@codingame/monaco-vscode-keybindings-service-override' import { clearStorage, remoteAuthority } from './setup' import { CustomEditorInput } from './features/customView' import './features/debugger' @@ -57,6 +57,8 @@ import '@codingame/monaco-vscode-configuration-editing-default-extension' import '@codingame/monaco-vscode-markdown-math-default-extension' import '@codingame/monaco-vscode-npm-default-extension' import '@codingame/monaco-vscode-media-preview-default-extension' +import defaultConfiguration from './user/configuration.json?raw' +import defaultKeybindings from './user/keybindings.json?raw' if (remoteAuthority != null) { import('./features/remoteExtension') @@ -145,6 +147,14 @@ document.querySelector('#settingsui')!.addEventListener('click', async () => { window.scrollTo({ top: 0, behavior: 'smooth' }) }) +document.querySelector('#resetsettings')!.addEventListener('click', async () => { + await updateUserConfiguration(defaultConfiguration) +}) + +document.querySelector('#resetkeybindings')!.addEventListener('click', async () => { + await updateUserKeybindings(defaultKeybindings) +}) + document.querySelector('#keybindingsui')!.addEventListener('click', async () => { await StandaloneServices.get(IPreferencesService).openGlobalKeybindingSettings(false) window.scrollTo({ top: 0, behavior: 'smooth' }) From d6510979dcfcdac61e98170e0dd1b5f7d3a31f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 15 Nov 2023 23:48:42 +0100 Subject: [PATCH 16/18] feat(demo): register custom editor serializer --- demo/src/features/customView.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/demo/src/features/customView.ts b/demo/src/features/customView.ts index 8e5a4d49..8c1e8836 100644 --- a/demo/src/features/customView.ts +++ b/demo/src/features/customView.ts @@ -1,5 +1,5 @@ -import { IDialogService, EditorInput, ITelemetryService, IThemeService, IStorageService, createInstance } from 'vscode/services' -import { registerCustomView, registerEditorPane, registerEditor, ViewContainerLocation, SimpleEditorPane, SimpleEditorInput, RegisteredEditorPriority, IEditorCloseHandler, ConfirmResult } from '@codingame/monaco-vscode-views-service-override' +import { IDialogService, EditorInput, ITelemetryService, IThemeService, IStorageService, createInstance, IInstantiationService } from 'vscode/services' +import { registerCustomView, registerEditorPane, registerEditor, registerEditorSerializer, ViewContainerLocation, SimpleEditorPane, SimpleEditorInput, RegisteredEditorPriority, IEditorCloseHandler, ConfirmResult, IEditorSerializer } from '@codingame/monaco-vscode-views-service-override' import * as monaco from 'monaco-editor' registerCustomView({ @@ -114,4 +114,26 @@ registerEditor('*.customeditor', { } }) +interface ISerializedCustomEditorInput { + resourceJSON?: monaco.UriComponents +} +registerEditorSerializer(CustomEditorPane.ID, class implements IEditorSerializer { + canSerialize (): boolean { + return true + } + + serialize (editor: CustomEditorInput): string | undefined { + const serializedFileEditorInput: ISerializedCustomEditorInput = { + resourceJSON: editor.resource?.toJSON() + } + + return JSON.stringify(serializedFileEditorInput) + } + + deserialize (instantiationService: IInstantiationService, serializedEditor: string): EditorInput | undefined { + const serializedFileEditorInput: ISerializedCustomEditorInput = JSON.parse(serializedEditor) + return instantiationService.createInstance(CustomEditorInput, monaco.Uri.revive(serializedFileEditorInput.resourceJSON)) + } +}) + export { CustomEditorInput } From cc5beca8aa8a9c86b5c22a3f814557efc11ad068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Thu, 16 Nov 2023 00:26:30 +0100 Subject: [PATCH 17/18] feat: add ability to clean user data --- demo/index.html | 2 +- demo/src/setup.ts | 3 ++- src/service-override/files.ts | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/demo/index.html b/demo/index.html index babbe826..7bd97242 100644 --- a/demo/index.html +++ b/demo/index.html @@ -22,7 +22,7 @@

Editor

- +
diff --git a/demo/src/setup.ts b/demo/src/setup.ts index dd927a2d..80ba55dd 100644 --- a/demo/src/setup.ts +++ b/demo/src/setup.ts @@ -48,7 +48,7 @@ import { toCrossOriginWorker, toWorkerConfig } from './tools/workers' import defaultConfiguration from './user/configuration.json?raw' import defaultKeybindings from './user/keybindings.json?raw' -await createIndexedDBProviders() +const userDataProvider = await createIndexedDBProviders() // Workers export type WorkerLoader = () => Worker @@ -129,6 +129,7 @@ await initializeMonacoService({ StandaloneServices.get(ILogService).setLevel(LogLevel.Off) export async function clearStorage (): Promise { + await userDataProvider.reset() await (await getService(IStorageService) as BrowserStorageService).clear() } diff --git a/src/service-override/files.ts b/src/service-override/files.ts index effaa170..c4629f27 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -543,7 +543,7 @@ export async function initFile (scheme: string, file: URI, content: Uint8Array | /** * Can be used to replace memory providers by indexeddb providers before the fileService is initialized */ -export async function createIndexedDBProviders (): Promise { +export async function createIndexedDBProviders (): Promise { const userDataStore = 'vscode-userdata-store' const logsStore = 'vscode-logs-store' const indexedDB = await IndexedDB.create('vscode-web-db', 3, [userDataStore, logsStore]) @@ -553,6 +553,8 @@ export async function createIndexedDBProviders (): Promise { const userDataProvider = new IndexedDBFileSystemProvider(Schemas.vscodeUserData, indexedDB, userDataStore, true) providers[Schemas.vscodeUserData] = userDataProvider + + return userDataProvider } /** From 7a827207fcbd2a92130acedef50de2c27c9fe604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Thu, 16 Nov 2023 15:31:33 +0100 Subject: [PATCH 18/18] fix: properly update keybindings --- scripts/vscode.patch | 10 ++++++++-- src/monaco.ts | 22 ++++++++++++++++++++-- src/service-override/keybindings.ts | 20 +++++++++++++------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/scripts/vscode.patch b/scripts/vscode.patch index 529b78e3..e93fba06 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -397,7 +397,7 @@ index 23d547570e9..31dfb4fd8d4 100644 export * from 'vs/editor/editor.api'; diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts -index 01f2c6987ac..a4853357178 100644 +index 01f2c6987ac..e3bf9b1e9e4 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -89,8 +89,6 @@ import { DefaultConfiguration } from 'vs/platform/configuration/common/configura @@ -409,7 +409,13 @@ index 01f2c6987ac..a4853357178 100644 import { ExtensionKind, IEnvironmentService, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; class SimpleModel implements IResolvedTextEditorModel { -@@ -517,10 +515,14 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { +@@ -512,15 +510,19 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { + }); + } + +- private updateResolver(): void { ++ protected updateResolver(): void { + this._cachedResolver = null; this._onDidUpdateKeybindings.fire(); } diff --git a/src/monaco.ts b/src/monaco.ts index 78e2ea81..249a32b2 100644 --- a/src/monaco.ts +++ b/src/monaco.ts @@ -43,6 +43,8 @@ import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/commo import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver' import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem' import { Keybinding, ResolvedKeybinding } from 'vs/base/common/keybindings' +import { Event } from 'vs/base/common/event' +import { Emitter } from 'monaco-editor' import { createInjectedClass } from './tools/injection' // Selectively comes from vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts import 'vs/workbench/contrib/codeEditor/browser/workbenchReferenceSearch' @@ -182,8 +184,13 @@ export async function createModelReference (resource: URI, content?: string): Pr return (await StandaloneServices.get(ITextModelService).createModelReference(resource)) as IReference } +export interface KeybindingProvider { + provideKeybindings (): ResolvedKeybindingItem[] + onDidChangeKeybindings: Event +} + export interface DynamicKeybindingService extends IKeybindingService { - registerKeybindingProvider (provider: () => ResolvedKeybindingItem[]): IDisposable + registerKeybindingProvider (provider: KeybindingProvider): IDisposable _getResolver(): KeybindingResolver } @@ -195,6 +202,7 @@ function isDynamicKeybindingService (keybindingService: IKeybindingService) { // This class use useful so editor.addAction and editor.addCommand still work // Monaco do an `instanceof` on the KeybindingService so we need it to extends `StandaloneKeybindingService` class DelegateStandaloneKeybindingService extends StandaloneKeybindingService { + private _onDidChangeKeybindings = new Emitter() constructor ( private delegate: DynamicKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, @@ -206,13 +214,23 @@ class DelegateStandaloneKeybindingService extends StandaloneKeybindingService { ) { super(contextKeyService, commandService, telemetryService, notificationService, logService, codeEditorService) - this._register(delegate.registerKeybindingProvider(() => this.getUserKeybindingItems())) + this._register(delegate.registerKeybindingProvider({ + provideKeybindings: () => { + return this.getUserKeybindingItems() + }, + onDidChangeKeybindings: this._onDidChangeKeybindings.event + })) } protected override _getResolver (): KeybindingResolver { return this.delegate._getResolver() } + protected override updateResolver (): void { + super.updateResolver() + this._onDidChangeKeybindings.fire() + } + override resolveKeyboardEvent (keyboardEvent: IKeyboardEvent): ResolvedKeybinding { return this.delegate.resolveKeyboardEvent(keyboardEvent) } diff --git a/src/service-override/keybindings.ts b/src/service-override/keybindings.ts index c766e06b..a900aa2a 100644 --- a/src/service-override/keybindings.ts +++ b/src/service-override/keybindings.ts @@ -9,7 +9,6 @@ import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/b 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' import { toDisposable } from 'vs/base/common/lifecycle' import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver' import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey' @@ -24,7 +23,7 @@ import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys' import { Schemas } from 'vs/base/common/network' import { URI } from 'vs/base/common/uri' import getFileServiceOverride, { initFile } from './files' -import { DynamicKeybindingService } from '../monaco' +import { DisposableStore, DynamicKeybindingService, KeybindingProvider } from '../monaco' import { onRenderWorkbench } from '../lifecycle' import { IInstantiationService } from '../services' import 'vs/workbench/browser/workbench.contribution' @@ -50,7 +49,7 @@ async function updateUserKeybindings (keybindingsJson: string): Promise { } class DynamicWorkbenchKeybindingService extends WorkbenchKeybindingService implements DynamicKeybindingService { - private keybindingProviders: (() => ResolvedKeybindingItem[])[] = [] + private keybindingProviders: KeybindingProvider[] = [] constructor ( private shouldUseGlobalKeybindings: () => boolean, @@ -69,17 +68,24 @@ class DynamicWorkbenchKeybindingService extends WorkbenchKeybindingService imple super(contextKeyService, commandService, telemetryService, notificationService, userDataProfileService, hostService, extensionService, fileService, uriIdentityService, logService, keyboardLayoutService) } - public registerKeybindingProvider (provider: () => ResolvedKeybindingItem[]) { + public registerKeybindingProvider (provider: KeybindingProvider) { this.keybindingProviders.push(provider) this.updateResolver() - return toDisposable(() => { + const store = new DisposableStore() + store.add(provider.onDidChangeKeybindings(() => { + this.updateResolver() + })) + + store.add(toDisposable(() => { const idx = this.keybindingProviders.indexOf(provider) if (idx >= 0) { this.keybindingProviders.splice(idx, 1) this.updateResolver() } - }) + })) + + return store } public override _getResolver (): KeybindingResolver { @@ -94,7 +100,7 @@ class DynamicWorkbenchKeybindingService extends WorkbenchKeybindingService imple } protected override getUserKeybindingItems () { - return [...super.getUserKeybindingItems(), ...this.keybindingProviders.flatMap(provider => provider())] + return [...super.getUserKeybindingItems(), ...this.keybindingProviders.flatMap(provider => provider.provideKeybindings())] } }