diff --git a/demo/index.html b/demo/index.html
index 6badb7b6..7bd97242 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -22,7 +22,7 @@
Editor
-
+
@@ -55,9 +55,11 @@ Editor
Settings
+
Keybindings
+
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 }
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' })
diff --git a/demo/src/setup.ts b/demo/src/setup.ts
index 6b268ec0..80ba55dd 100644
--- a/demo/src/setup.ts
+++ b/demo/src/setup.ts
@@ -38,12 +38,17 @@ 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'
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'
+
+const userDataProvider = await createIndexedDBProviders()
// Workers
export type WorkerLoader = () => Worker
@@ -70,35 +75,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
@@ -107,9 +85,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(),
@@ -117,7 +93,7 @@ await initializeMonacoService({
...getAudioCueServiceOverride(),
...getDebugServiceOverride(),
...getPreferencesServiceOverride(),
- ...getViewsServiceOverride(openNewCodeEditor),
+ ...getViewsServiceOverride(openNewCodeEditor, undefined, true),
...getBannerServiceOverride(),
...getStatusBarServiceOverride(),
...getTitleBarServiceOverride(),
@@ -135,15 +111,25 @@ 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)
export async function clearStorage (): Promise {
+ await userDataProvider.reset()
await (await getService(IStorageService) as BrowserStorageService).clear()
}
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
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: {
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/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
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/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)
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/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..c4629f27 100644
--- a/src/service-override/files.ts
+++ b/src/service-override/files.ts
@@ -13,8 +13,10 @@ 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 {
private ctime: number
@@ -468,14 +470,14 @@ 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
}
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)) {
@@ -486,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)))
+ }
}
}
}
@@ -516,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,
@@ -526,6 +540,23 @@ 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
+
+ return userDataProvider
+}
+
/**
* Register a file system overlay
*
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())]
}
}
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..c57bc676 100644
--- a/src/service-override/storage.ts
+++ b/src/service-override/storage.ts
@@ -9,8 +9,9 @@ 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'
+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 {
@@ -138,9 +149,7 @@ class InjectedBrowserStorageService extends BrowserStorageService {
@IUserDataProfileService userDataProfileService: IUserDataProfileService,
@ILogService logService: ILogService
) {
- super({
- id: generateUuid()
- }, userDataProfileService, logService)
+ super(getWorkspaceIdentifier(), userDataProfileService, logService)
}
}
diff --git a/src/service-override/views.ts b/src/service-override/views.ts
index 9c4c827b..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'
@@ -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 {
@@ -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
@@ -621,7 +627,7 @@ registerAssets({
})
let restoreEditorView = false
-registerServiceInitializePostParticipant(async (accessor) => {
+onRenderWorkbench(async (accessor) => {
const paneCompositePartService = accessor.get(IPaneCompositePartService)
const viewDescriptorService = accessor.get(IViewDescriptorService)
@@ -630,8 +636,6 @@ registerServiceInitializePostParticipant(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')
@@ -726,6 +730,8 @@ export {
AbstractTextResourceEditorInput,
EditorInput,
registerEditor,
+ IEditorSerializer,
+ registerEditorSerializer,
RegisteredEditorInfo,
RegisteredEditorOptions,
EditorInputFactoryObject,
diff --git a/src/services.ts b/src/services.ts
index 2d3acfca..51cd2152 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,12 +23,15 @@ 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
+ ...getLayoutServiceOverride(), // Always override layout service to break cyclic dependency with ICodeEditorService
...getEnvironmentServiceOverride(),
...getExtensionsServiceOverride(),
...getFileServiceOverride(),
@@ -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)
+}