From 43e515e730fc242be47830da7b4ba2da04fe1b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:48:03 +0200 Subject: [PATCH 01/14] feat: export some function that may be useful --- src/monaco.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/monaco.ts b/src/monaco.ts index b76af9fe..59b7255a 100644 --- a/src/monaco.ts +++ b/src/monaco.ts @@ -43,6 +43,8 @@ import { Event } from 'vs/base/common/event' import { ResourceContextKey } from 'vs/workbench/common/contextkeys' import { createInjectedClass } from './tools/injection' import { getService } from './services' +export { registerEditorAction, registerEditorContribution, registerDiffEditorContribution, registerMultiEditorAction, EditorAction, EditorCommand } from 'vs/editor/browser/editorExtensions' +export { IEditorContribution, IDiffEditorContribution } from 'vs/editor/common/editorCommon' function computeConfiguration (configuration: IEditorConfiguration, overrides?: Readonly): IEditorOptions { const editorConfiguration: IEditorOptions = isObject(configuration.editor) ? deepClone(configuration.editor) : Object.create(null) From 7cb965cb19d263a99a9b75dcb98d4090c6b8b273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:48:38 +0200 Subject: [PATCH 02/14] refactor: rename class --- src/service-override/files.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 79487c23..8207214b 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -513,8 +513,8 @@ class DelegateFileSystemProvider implements IFileSystemProvider { } } -const fileSystemProvider = new OverlayFileSystemProvider() -fileSystemProvider.register(0, new MkdirpOnWriteInMemoryFileSystemProvider()) +const overlayFileSystemProvider = new OverlayFileSystemProvider() +overlayFileSystemProvider.register(0, new MkdirpOnWriteInMemoryFileSystemProvider()) const extensionFileSystemProvider = new RegisteredFileSystemProvider(true) const userDataFileSystemProvider = new InMemoryFileSystemProvider() @@ -536,10 +536,10 @@ const providers: Record = { [logsPath.scheme]: new InMemoryFileSystemProvider(), [Schemas.vscodeUserData]: userDataFileSystemProvider, [Schemas.tmp]: new InMemoryFileSystemProvider(), - [Schemas.file]: fileSystemProvider + [Schemas.file]: overlayFileSystemProvider } -class MemoryFileService extends FileService { +class FileServiceOverride extends FileService { constructor (logService: ILogService, @ITelemetryService telemetryService: ITelemetryService) { super(logService) @@ -568,7 +568,7 @@ registerServiceInitializePreParticipant(async (accessor) => { export default function getServiceOverride (): IEditorOverrideServices { return { - [IFileService.toString()]: new SyncDescriptor(MemoryFileService, [fileLogger], true), + [IFileService.toString()]: new SyncDescriptor(FileServiceOverride, [fileLogger], true), [ITextFileService.toString()]: new SyncDescriptor(BrowserTextFileService, [], true), [IFilesConfigurationService.toString()]: new SyncDescriptor(FilesConfigurationService, [], true), [IElevatedFileService.toString()]: new SyncDescriptor(BrowserElevatedFileService, [], true) From 1cadd7c668acfa1a9175d229d61a89f915b93fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:49:24 +0200 Subject: [PATCH 03/14] fix: wrong used variable --- src/service-override/files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 8207214b..1bf6c427 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -548,7 +548,7 @@ class FileServiceOverride extends FileService { if (provider instanceof OverlayFileSystemProvider) { provider.onDidChangeOverlays(() => { disposable.dispose() - disposable = this.registerProvider(scheme, fileSystemProvider) + disposable = this.registerProvider(scheme, provider) }) } From df273323fab6cc5989592ab5420ef8e6af59c509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:50:04 +0200 Subject: [PATCH 04/14] feat: export OverlayFileSystemProvider --- src/service-override/files.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 1bf6c427..4f0010bb 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -305,6 +305,9 @@ class OverlayFileSystemProvider implements IFileSystemProviderWithFileReadWriteC capabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.PathCaseSensitive private async readFromDelegates (caller: (delegate: IFileSystemProviderWithFileReadWriteCapability) => Promise) { + if (this.delegates.length === 0) { + throw createFileSystemProviderError('No delegate', FileSystemProviderErrorCode.Unavailable) + } let firstError: unknown | undefined for (const delegate of this.delegates) { try { @@ -325,6 +328,9 @@ class OverlayFileSystemProvider implements IFileSystemProviderWithFileReadWriteC } private async writeToDelegates (caller: (delegate: IFileSystemProviderWithFileReadWriteCapability) => Promise): Promise { + if (this.delegates.length === 0) { + throw createFileSystemProviderError('No delegate', FileSystemProviderErrorCode.Unavailable) + } for (const provider of this.delegates) { if ((provider.capabilities & FileSystemProviderCapabilities.Readonly) > 0) { continue @@ -669,5 +675,6 @@ export { RegisteredFile, RegisteredReadOnlyFile, RegisteredMemoryFile, - DelegateFileSystemProvider + DelegateFileSystemProvider, + OverlayFileSystemProvider } From cb6acd22f442761f15c6935d323f6b4ade680f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:50:47 +0200 Subject: [PATCH 05/14] feat: add new EmptyFileSystemProvider --- src/service-override/files.ts | 40 ++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 4f0010bb..218ded8f 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -519,6 +519,43 @@ class DelegateFileSystemProvider implements IFileSystemProvider { } } +class EmptyFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability { + async readFile (): Promise { + throw createFileSystemProviderError('Not found', FileSystemProviderErrorCode.FileNotFound) + } + + async writeFile (): Promise { + throw createFileSystemProviderError('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } + + capabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.PathCaseSensitive + onDidChangeCapabilities = Event.None + onDidChangeFile = Event.None + watch (): IDisposable { + return Disposable.None + } + + async stat (): Promise { + throw createFileSystemProviderError('Not found', FileSystemProviderErrorCode.FileNotFound) + } + + async mkdir (): Promise { + throw createFileSystemProviderError('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } + + async readdir (): Promise<[string, FileType][]> { + throw createFileSystemProviderError('Not found', FileSystemProviderErrorCode.FileNotFound) + } + + async delete (): Promise { + throw createFileSystemProviderError('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } + + async rename (): Promise { + throw createFileSystemProviderError('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } +} + const overlayFileSystemProvider = new OverlayFileSystemProvider() overlayFileSystemProvider.register(0, new MkdirpOnWriteInMemoryFileSystemProvider()) @@ -676,5 +713,6 @@ export { RegisteredReadOnlyFile, RegisteredMemoryFile, DelegateFileSystemProvider, - OverlayFileSystemProvider + OverlayFileSystemProvider, + EmptyFileSystemProvider } From 68ae5f3fae6c1571a849a7e154bdc4d7de10f190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:51:20 +0200 Subject: [PATCH 06/14] fix: use the replaced overlay filesystem --- src/service-override/files.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 218ded8f..722c51b2 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -687,7 +687,11 @@ export async function createIndexedDBProviders (): Promise Date: Wed, 17 Apr 2024 16:51:57 +0200 Subject: [PATCH 07/14] feat: add a function to replace the filesystem by the HTMLFileSystemProvider --- src/service-override/files.ts | 62 ++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/service-override/files.ts b/src/service-override/files.ts index 722c51b2..29a5cb8c 100644 --- a/src/service-override/files.ts +++ b/src/service-override/files.ts @@ -1,7 +1,7 @@ -import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices' +import { IEditorOverrideServices, StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices' import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' import { FileService } from 'vs/platform/files/common/fileService' -import { ILogService } from 'vs/platform/log/common/log' +import { ILogService, LogLevel } from 'vs/platform/log/common/log' import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider' import { URI } from 'vs/base/common/uri' import { FileChangeType, FilePermission, FileSystemProviderCapabilities, FileType, IFileSystemProvider, toFileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError, FileSystemProviderErrorCode, IFileChange, IFileDeleteOptions, IFileOverwriteOptions, IFileService, IFileSystemProviderWithFileReadWriteCapability, IFileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files' @@ -658,13 +658,15 @@ export async function initFile (file: URI, content: Uint8Array | string, options }) } +let indexedDB: IndexedDB | undefined +const userDataStore = 'vscode-userdata-store' +const logsStore = 'vscode-logs-store' +const handlesStore = 'vscode-filehandles-store' /** * 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]) + indexedDB = await IndexedDB.create('vscode-web-db', 3, [userDataStore, logsStore, handlesStore]) // Logger registerCustomProvider(logsPath.scheme, new IndexedDBFileSystemProvider(logsPath.scheme, indexedDB, logsStore, false)) @@ -675,6 +677,56 @@ export async function createIndexedDBProviders (): Promise Date: Wed, 17 Apr 2024 16:53:34 +0200 Subject: [PATCH 08/14] feat: add a way to use web versions of dialogs and search services --- src/service-override/dialogs.ts | 15 ++++++++++++--- src/service-override/search.ts | 13 +++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/service-override/dialogs.ts b/src/service-override/dialogs.ts index 04147512..83be7666 100644 --- a/src/service-override/dialogs.ts +++ b/src/service-override/dialogs.ts @@ -3,12 +3,13 @@ import { DialogService } from 'vs/workbench/services/dialogs/common/dialogServic import { IDialogService, IFileDialogService, IOpenDialogOptions, IPickAndOpenOptions, ISaveDialogOptions } from 'vs/platform/dialogs/common/dialogs' import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService' +import { FileDialogService } from 'vs/workbench/services/dialogs/browser/fileDialogService' import { URI } from 'vs/base/common/uri' import { unsupported } from '../tools' import 'vs/workbench/browser/parts/dialogs/dialog.web.contribution' import 'vs/workbench/contrib/welcomeDialog/browser/welcomeDialog.contribution' -class FileDialogService extends AbstractFileDialogService { +class DialogServiceOverride extends AbstractFileDialogService { override pickWorkspaceAndOpen = unsupported async pickFileFolderAndOpen (options: IPickAndOpenOptions): Promise { @@ -60,9 +61,17 @@ class FileDialogService extends AbstractFileDialogService { } } -export default function getServiceOverride (): IEditorOverrideServices { +interface DialogServiceOverrideProps { + /** + * Is an `HTMLFileSystemProvider` is used as only provider for the `file` scheme directly (without overlay) + * Enable this option to enable browser file dialogs + */ + useHtmlFileSystemProvider: boolean +} + +export default function getServiceOverride ({ useHtmlFileSystemProvider }: DialogServiceOverrideProps): IEditorOverrideServices { return { [IDialogService.toString()]: new SyncDescriptor(DialogService, undefined, true), - [IFileDialogService.toString()]: new SyncDescriptor(FileDialogService, undefined, true) + [IFileDialogService.toString()]: useHtmlFileSystemProvider ? new SyncDescriptor(FileDialogService, undefined, true) : new SyncDescriptor(DialogServiceOverride, undefined, true) } } diff --git a/src/service-override/search.ts b/src/service-override/search.ts index 6b45f39b..423d581b 100644 --- a/src/service-override/search.ts +++ b/src/service-override/search.ts @@ -6,12 +6,21 @@ import { ISearchViewModelWorkbenchService, SearchViewModelWorkbenchService } fro import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService' import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace' import { ReplaceService } from 'vs/workbench/contrib/search/browser/replaceService' +import { RemoteSearchService } from 'vs/workbench/services/search/browser/searchService' import 'vs/workbench/contrib/search/browser/search.contribution' import 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution' -export default function getServiceOverride (): IEditorOverrideServices { +interface SearchServiceOverrideProps { + /** + * Is an `HTMLFileSystemProvider` is used as only provider for the `file` scheme directly (without overlay) + * Enable this option to enable searching local filesystem + */ + useHtmlFileSystemProvider: boolean +} + +export default function getServiceOverride ({ useHtmlFileSystemProvider }: SearchServiceOverrideProps): IEditorOverrideServices { return { - [ISearchService.toString()]: new SyncDescriptor(SearchService, [], true), + [ISearchService.toString()]: useHtmlFileSystemProvider ? new SyncDescriptor(RemoteSearchService, [], true) : new SyncDescriptor(SearchService, [], true), [ISearchViewModelWorkbenchService.toString()]: new SyncDescriptor(SearchViewModelWorkbenchService, [], true), [ISearchHistoryService.toString()]: new SyncDescriptor(SearchHistoryService, [], true), [IReplaceService.toString()]: new SyncDescriptor(ReplaceService, [], true) From e35b11eaa856601f1032976bf04c9f26170503c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 16:53:58 +0200 Subject: [PATCH 09/14] feat: add localFileSearch worker --- rollup/rollup.config.ts | 3 ++- src/workers/localFileSearch.worker.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/workers/localFileSearch.worker.ts diff --git a/rollup/rollup.config.ts b/rollup/rollup.config.ts index 8d13d0ff..a676a231 100644 --- a/rollup/rollup.config.ts +++ b/rollup/rollup.config.ts @@ -362,7 +362,8 @@ const workerGroups: Record = { languageDetection: 'service-override:language-detection-worker', outputLinkComputer: 'service-override:output', textmate: 'service-override:textmate', - notebook: 'service-override:notebook' + notebook: 'service-override:notebook', + localFileSearch: 'service-override:search' } const externals = Object.keys({ ...pkg.dependencies }) diff --git a/src/workers/localFileSearch.worker.ts b/src/workers/localFileSearch.worker.ts new file mode 100644 index 00000000..8a6eb95b --- /dev/null +++ b/src/workers/localFileSearch.worker.ts @@ -0,0 +1,10 @@ +import { create } from 'vs/workbench/services/search/worker/localFileSearch' +import { SimpleWorkerServer } from 'vs/base/common/worker/simpleWorker' + +const simpleWorker = new SimpleWorkerServer((msg) => { + globalThis.postMessage(msg) +}, create) + +globalThis.onmessage = (e: MessageEvent) => { + simpleWorker.onmessage(e.data) +} From 06a118e90f9afe4497f04f5ac133566b188b3727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 17:01:15 +0200 Subject: [PATCH 10/14] refactor(demo): use registered file instead of initFIle --- demo/src/setup.common.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/demo/src/setup.common.ts b/demo/src/setup.common.ts index 7ffa4b52..4bbd9ceb 100644 --- a/demo/src/setup.common.ts +++ b/demo/src/setup.common.ts @@ -1,6 +1,6 @@ import getConfigurationServiceOverride, { IStoredWorkspace, initUserConfiguration } from '@codingame/monaco-vscode-configuration-service-override' import getKeybindingsServiceOverride, { initUserKeybindings } from '@codingame/monaco-vscode-keybindings-service-override' -import { RegisteredFileSystemProvider, RegisteredMemoryFile, RegisteredReadOnlyFile, createIndexedDBProviders, initFile, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override' +import { RegisteredFileSystemProvider, RegisteredMemoryFile, RegisteredReadOnlyFile, createIndexedDBProviders, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override' import * as monaco from 'monaco-editor' import { IWorkbenchConstructionOptions, LogLevel, IEditorOverrideServices } from 'vscode/services' import * as vscode from 'vscode' @@ -147,6 +147,20 @@ h1 { }` )) +// Use a workspace file to be able to add another folder later (for the "Attach filesystem" button) +fileSystemProvider.registerFile(new RegisteredMemoryFile(workspaceFile, JSON.stringify({ + folders: [{ + path: '/tmp' + }] +}, null, 2))) + +fileSystemProvider.registerFile(new RegisteredMemoryFile(monaco.Uri.file('/tmp/.vscode/extensions.json'), JSON.stringify({ + "recommendations": [ + "vscodevim.vim" + ] +}, null, 2))) + + registerFileSystemOverlay(1, fileSystemProvider) export const userDataProvider = await createIndexedDBProviders() @@ -186,17 +200,6 @@ export const workspaceFile = monaco.Uri.file('/workspace.code-workspace') await Promise.all([ initUserConfiguration(defaultConfiguration), initUserKeybindings(defaultKeybindings), - // Use a workspace file to be able to add another folder later (for the "Attach filesystem" button) - initFile(workspaceFile, JSON.stringify({ - folders: [{ - path: '/tmp' - }] - })), - initFile(monaco.Uri.file('/tmp/.vscode/extensions.json'), `{ - "recommendations": [ - "vscodevim.vim" - ] -}`) ]) export const constructOptions: IWorkbenchConstructionOptions = { From e1fba763a3553c11ef5c739d931504912bc21299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 17:02:34 +0200 Subject: [PATCH 11/14] feat(demo): Add search param that replaces the filesystem by the html filesystem --- demo/src/main.common.ts | 20 ++-- demo/src/setup.common.ts | 199 +++++++++++++++++++----------------- demo/src/setup.views.ts | 2 +- demo/src/setup.workbench.ts | 2 +- 4 files changed, 115 insertions(+), 108 deletions(-) diff --git a/demo/src/main.common.ts b/demo/src/main.common.ts index 4f8831a0..0c579f83 100644 --- a/demo/src/main.common.ts +++ b/demo/src/main.common.ts @@ -1,7 +1,5 @@ import './style.css' import * as monaco from 'monaco-editor' -import { registerFileSystemOverlay, HTMLFileSystemProvider } from '@codingame/monaco-vscode-files-service-override' -import { ILogService, StandaloneServices } from 'vscode/services' import './setup.common' import './features/output' import './features/debugger' @@ -96,15 +94,13 @@ void getApi().then(async vscode => { window.location.href = url.toString() }) - document.querySelector('#filesystem')!.addEventListener('click', async () => { - const dirHandle = await window.showDirectoryPicker() - - const htmlFileSystemProvider = new HTMLFileSystemProvider(undefined, 'unused', StandaloneServices.get(ILogService)) - await htmlFileSystemProvider.registerDirectoryHandle(dirHandle) - registerFileSystemOverlay(1, htmlFileSystemProvider) - - vscode.workspace.updateWorkspaceFolders(0, 0, { - uri: vscode.Uri.file(dirHandle.name) - }) + document.querySelector('#toggleHTMLFileSystemProvider')!.addEventListener('click', async () => { + const url = new URL(window.location.href) + if (url.searchParams.has('htmlFileSystemProvider')) { + url.searchParams.delete('htmlFileSystemProvider') + } else { + url.searchParams.set('htmlFileSystemProvider', 'true') + } + window.location.href = url.toString() }) }) diff --git a/demo/src/setup.common.ts b/demo/src/setup.common.ts index 4bbd9ceb..5ff3a9ba 100644 --- a/demo/src/setup.common.ts +++ b/demo/src/setup.common.ts @@ -1,6 +1,6 @@ import getConfigurationServiceOverride, { IStoredWorkspace, initUserConfiguration } from '@codingame/monaco-vscode-configuration-service-override' import getKeybindingsServiceOverride, { initUserKeybindings } from '@codingame/monaco-vscode-keybindings-service-override' -import { RegisteredFileSystemProvider, RegisteredMemoryFile, RegisteredReadOnlyFile, createIndexedDBProviders, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override' +import { RegisteredFileSystemProvider, RegisteredMemoryFile, RegisteredReadOnlyFile, createIndexedDBProviders, registerHTMLFileSystemProvider, registerFileSystemOverlay, initFile } from '@codingame/monaco-vscode-files-service-override' import * as monaco from 'monaco-editor' import { IWorkbenchConstructionOptions, LogLevel, IEditorOverrideServices } from 'vscode/services' import * as vscode from 'vscode' @@ -66,104 +66,126 @@ import { TerminalBackend } from './features/terminal' import { workerConfig } from './tools/extHostWorker' import 'vscode/localExtensionHost' -const fileSystemProvider = new RegisteredFileSystemProvider(false) +const url = new URL(document.location.href) +const params = url.searchParams +export const remoteAuthority = params.get('remoteAuthority') ?? undefined +export const connectionToken = params.get('connectionToken') ?? undefined +export const remotePath = remoteAuthority != null ? params.get('remotePath') ?? undefined : undefined +export const resetLayout = params.has('resetLayout') +export const useHtmlFileSystemProvider = params.has('htmlFileSystemProvider') +params.delete('resetLayout') -fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.js'), `// import anotherfile -let variable = 1 -function inc () { - variable++ -} +window.history.replaceState({}, document.title, url.href) + +export let workspaceFile = monaco.Uri.file('/workspace.code-workspace') -while (variable < 5000) { - inc() - console.log('Hello world', variable); -}` -)) +export const userDataProvider = await createIndexedDBProviders() -fileSystemProvider.registerFile(new RegisteredReadOnlyFile(vscode.Uri.file('/tmp/test_readonly.js'), async () => 'This is a readonly static file')) +if (useHtmlFileSystemProvider) { + workspaceFile = monaco.Uri.from({ scheme: 'tmp', path: '/test.code-workspace' }) + await initFile(workspaceFile, JSON.stringify({ + folders: [] + }, null, 2)) -fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/jsconfig.json'), `{ - "compilerOptions": { - "target": "es2020", - "module": "esnext", - "lib": [ - "es2021", - "DOM" - ] + registerHTMLFileSystemProvider() +} else { + const fileSystemProvider = new RegisteredFileSystemProvider(false) + + fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.js'), `// import anotherfile + let variable = 1 + function inc () { + variable++ } -}` -)) -fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/index.html'), ` - - - - - monaco-vscode-api demo - - - - + while (variable < 5000) { + inc() + console.log('Hello world', variable); + }` + )) + + fileSystemProvider.registerFile(new RegisteredReadOnlyFile(vscode.Uri.file('/tmp/test_readonly.js'), async () => 'This is a readonly static file')) + + fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/jsconfig.json'), `{ + "compilerOptions": { + "target": "es2020", + "module": "esnext", + "lib": [ + "es2021", + "DOM" + ] + } + }` + )) -

Hello, world!

- -` -)) + fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/index.html'), ` + + + + + monaco-vscode-api demo + + + + -fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.md'), ` -***Hello World*** +

Hello, world!

+ + ` + )) -Math block: -$$ -\\displaystyle -\\left( \\sum_{k=1}^n a_k b_k \\right)^2 -\\leq -\\left( \\sum_{k=1}^n a_k^2 \\right) -\\left( \\sum_{k=1}^n b_k^2 \\right) -$$ + fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.md'), ` + ***Hello World*** -# Easy Math + Math block: + $$ + \\displaystyle + \\left( \\sum_{k=1}^n a_k b_k \\right)^2 + \\leq + \\left( \\sum_{k=1}^n a_k^2 \\right) + \\left( \\sum_{k=1}^n b_k^2 \\right) + $$ -2 + 2 = 4 // this test will pass -2 + 2 = 5 // this test will fail + # Easy Math -# Harder Math + 2 + 2 = 4 // this test will pass + 2 + 2 = 5 // this test will fail -230230 + 5819123 = 6049353 -` -)) + # Harder Math -fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.customeditor'), ` -Custom Editor!` -)) + 230230 + 5819123 = 6049353 + ` + )) -fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.css'), ` -h1 { - color: DeepSkyBlue; -}` -)) + fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.customeditor'), ` + Custom Editor!` + )) -// Use a workspace file to be able to add another folder later (for the "Attach filesystem" button) -fileSystemProvider.registerFile(new RegisteredMemoryFile(workspaceFile, JSON.stringify({ - folders: [{ - path: '/tmp' - }] -}, null, 2))) + fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/test.css'), ` + h1 { + color: DeepSkyBlue; + }` + )) -fileSystemProvider.registerFile(new RegisteredMemoryFile(monaco.Uri.file('/tmp/.vscode/extensions.json'), JSON.stringify({ - "recommendations": [ - "vscodevim.vim" - ] -}, null, 2))) + // Use a workspace file to be able to add another folder later (for the "Attach filesystem" button) + fileSystemProvider.registerFile(new RegisteredMemoryFile(workspaceFile, JSON.stringify({ + folders: [{ + path: '/tmp' + }] + }, null, 2))) + fileSystemProvider.registerFile(new RegisteredMemoryFile(monaco.Uri.file('/tmp/.vscode/extensions.json'), JSON.stringify({ + "recommendations": [ + "vscodevim.vim" + ] + }, null, 2))) -registerFileSystemOverlay(1, fileSystemProvider) -export const userDataProvider = await createIndexedDBProviders() + registerFileSystemOverlay(1, fileSystemProvider) +} // Workers export type WorkerLoader = () => Worker @@ -185,18 +207,7 @@ window.MonacoEnvironment = { } } -const url = new URL(document.location.href) -const params = url.searchParams -export const remoteAuthority = params.get('remoteAuthority') ?? undefined -export const connectionToken = params.get('connectionToken') ?? undefined -export const remotePath = remoteAuthority != null ? params.get('remotePath') ?? undefined : undefined -export const resetLayout = params.has('resetLayout') -params.delete('resetLayout') - -window.history.replaceState({}, document.title, url.href) - // Set configuration before initializing service so it's directly available (especially for the theme, to prevent a flicker) -export const workspaceFile = monaco.Uri.file('/workspace.code-workspace') await Promise.all([ initUserConfiguration(defaultConfiguration), initUserKeybindings(defaultKeybindings), @@ -232,14 +243,14 @@ export const constructOptions: IWorkbenchConstructionOptions = { 'window.title': 'Monaco-Vscode-Api${separator}${dirty}${activeEditorShort}' }, defaultLayout: { - editors: [{ + editors: useHtmlFileSystemProvider ? undefined : [{ uri: monaco.Uri.file('/tmp/test.js'), viewColumn: 1 }, { uri: monaco.Uri.file('/tmp/test.md'), viewColumn: 2 }], - layout: { + layout: useHtmlFileSystemProvider ? undefined : { editors: { orientation: 0, groups: [{ size: 1 }, { size: 1 }] @@ -280,7 +291,7 @@ export const commonServices: IEditorOverrideServices = { ...getExtensionGalleryServiceOverride({ webOnly: false }), ...getModelServiceOverride(), ...getNotificationServiceOverride(), - ...getDialogsServiceOverride(), + ...getDialogsServiceOverride({ useHtmlFileSystemProvider }), ...getConfigurationServiceOverride(), ...getKeybindingsServiceOverride(), ...getTextmateServiceOverride(), @@ -296,7 +307,7 @@ export const commonServices: IEditorOverrideServices = { ...getSnippetServiceOverride(), ...getOutputServiceOverride(), ...getTerminalServiceOverride(new TerminalBackend()), - ...getSearchServiceOverride(), + ...getSearchServiceOverride({ useHtmlFileSystemProvider }), ...getMarkersServiceOverride(), ...getAccessibilityServiceOverride(), ...getLanguageDetectionWorkerServiceOverride(), diff --git a/demo/src/setup.views.ts b/demo/src/setup.views.ts index 77bd0596..b5af3763 100644 --- a/demo/src/setup.views.ts +++ b/demo/src/setup.views.ts @@ -33,7 +33,7 @@ container.innerHTML = `

Editor

- + diff --git a/demo/src/setup.workbench.ts b/demo/src/setup.workbench.ts index cade7f10..74254eb7 100644 --- a/demo/src/setup.workbench.ts +++ b/demo/src/setup.workbench.ts @@ -14,7 +14,7 @@ document.body.replaceChildren(container) const buttons = document.createElement('div') buttons.innerHTML = ` - + From f2b69d42fa597136ad01a4111b91595bf47574c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 17:03:41 +0200 Subject: [PATCH 12/14] fix(demo): disable some features when html filesystem is used --- demo/src/features/output.ts | 47 ++++++++++++------------ demo/src/features/scm.ts | 4 +-- demo/src/features/search.ts | 71 +++++++++++++++++++------------------ demo/src/main.common.ts | 29 ++++++++------- 4 files changed, 80 insertions(+), 71 deletions(-) diff --git a/demo/src/features/output.ts b/demo/src/features/output.ts index 815b9d5d..51bc307a 100644 --- a/demo/src/features/output.ts +++ b/demo/src/features/output.ts @@ -1,28 +1,31 @@ +import { useHtmlFileSystemProvider } from '../setup.common' import { ExtensionHostKind, registerExtension } from 'vscode/extensions' -const { getApi } = registerExtension({ - name: 'outputDemo', - publisher: 'codingame', - version: '1.0.0', - engines: { - vscode: '*' - } -}, ExtensionHostKind.LocalProcess) +if (!useHtmlFileSystemProvider) { + const { getApi } = registerExtension({ + name: 'outputDemo', + publisher: 'codingame', + version: '1.0.0', + engines: { + vscode: '*' + } + }, ExtensionHostKind.LocalProcess) -void getApi().then(async vscode => { - const fakeOutputChannel = vscode.window.createOutputChannel('Fake output') - const anotherFakeOutputChannel = vscode.window.createOutputChannel('Your code', 'javascript') + void getApi().then(async vscode => { + const fakeOutputChannel = vscode.window.createOutputChannel('Fake output') + const anotherFakeOutputChannel = vscode.window.createOutputChannel('Your code', 'javascript') - fakeOutputChannel.append('Here\'s some fake output\n') - setInterval(() => { - fakeOutputChannel.append('Hello world\n') - }, 1000) + fakeOutputChannel.append('Here\'s some fake output\n') + setInterval(() => { + fakeOutputChannel.append('Hello world\n') + }, 1000) - const mainDocument = await vscode.workspace.openTextDocument(vscode.Uri.file('/tmp/test.js')) - anotherFakeOutputChannel.replace(mainDocument.getText()) - vscode.workspace.onDidChangeTextDocument((e) => { - if (e.document === mainDocument && e.contentChanges.length > 0) { - anotherFakeOutputChannel.replace(e.document.getText()) - } + const mainDocument = await vscode.workspace.openTextDocument(vscode.Uri.file('/tmp/test.js')) + anotherFakeOutputChannel.replace(mainDocument.getText()) + vscode.workspace.onDidChangeTextDocument((e) => { + if (e.document === mainDocument && e.contentChanges.length > 0) { + anotherFakeOutputChannel.replace(e.document.getText()) + } + }) }) -}) +} \ No newline at end of file diff --git a/demo/src/features/scm.ts b/demo/src/features/scm.ts index 696c22f8..66214723 100644 --- a/demo/src/features/scm.ts +++ b/demo/src/features/scm.ts @@ -14,9 +14,9 @@ const { getApi } = registerExtension({ }) void getApi().then(async vscode => { - const workspaceFolder = vscode.workspace.workspaceFolders![0] + const workspaceFolder = vscode.workspace.workspaceFolders?.[0] if (workspaceFolder == null) { - throw new Error('No workspace folder') + return } vscode.commands.registerCommand('scm-demo.click-file', async (uri: Uri) => { diff --git a/demo/src/features/search.ts b/demo/src/features/search.ts index d8b391a5..f5136288 100644 --- a/demo/src/features/search.ts +++ b/demo/src/features/search.ts @@ -1,41 +1,44 @@ import { ExtensionHostKind, registerExtension } from 'vscode/extensions' import * as monaco from 'monaco-editor' +import { useHtmlFileSystemProvider } from '../setup.common' -const { getApi } = registerExtension({ - name: 'searchProvider', - publisher: 'codingame', - version: '1.0.0', - engines: { - vscode: '*' - }, - enabledApiProposals: ['fileSearchProvider', 'textSearchProvider'] -}, ExtensionHostKind.LocalProcess, { - system: true // to be able to use api proposals -}) - -void getApi().then(async api => { - api.workspace.registerFileSearchProvider('file', { - async provideFileSearchResults () { - return monaco.editor.getModels().map(model => model.uri).filter(uri => uri.scheme === 'file') - } +if (!useHtmlFileSystemProvider) { + const { getApi } = registerExtension({ + name: 'searchProvider', + publisher: 'codingame', + version: '1.0.0', + engines: { + vscode: '*' + }, + enabledApiProposals: ['fileSearchProvider', 'textSearchProvider'] + }, ExtensionHostKind.LocalProcess, { + system: true // to be able to use api proposals }) - api.workspace.registerTextSearchProvider('file', { - async provideTextSearchResults (query, _, progress) { - for (const model of monaco.editor.getModels()) { - const matches = model.findMatches(query.pattern, false, query.isRegExp ?? false, query.isCaseSensitive ?? false, query.isWordMatch ?? false ? ' ' : null, true) - if (matches.length > 0) { - const ranges = matches.map(match => new api.Range(match.range.startLineNumber, match.range.startColumn, match.range.endLineNumber, match.range.endColumn)) - progress.report({ - uri: model.uri, - ranges, - preview: { - text: model.getValue(), - matches: ranges - } - }) + + void getApi().then(async api => { + api.workspace.registerFileSearchProvider('file', { + async provideFileSearchResults () { + return monaco.editor.getModels().map(model => model.uri).filter(uri => uri.scheme === 'file') + } + }) + api.workspace.registerTextSearchProvider('file', { + async provideTextSearchResults (query, _, progress) { + for (const model of monaco.editor.getModels()) { + const matches = model.findMatches(query.pattern, false, query.isRegExp ?? false, query.isCaseSensitive ?? false, query.isWordMatch ?? false ? ' ' : null, true) + if (matches.length > 0) { + const ranges = matches.map(match => new api.Range(match.range.startLineNumber, match.range.startColumn, match.range.endLineNumber, match.range.endColumn)) + progress.report({ + uri: model.uri, + ranges, + preview: { + text: model.getValue(), + matches: ranges + } + }) + } } + return {} } - return {} - } + }) }) -}) +} diff --git a/demo/src/main.common.ts b/demo/src/main.common.ts index 0c579f83..885881f7 100644 --- a/demo/src/main.common.ts +++ b/demo/src/main.common.ts @@ -52,6 +52,7 @@ import '@codingame/monaco-vscode-npm-default-extension' import '@codingame/monaco-vscode-media-preview-default-extension' import '@codingame/monaco-vscode-ipynb-default-extension' import { ExtensionHostKind, registerExtension } from 'vscode/extensions' +import { useHtmlFileSystemProvider } from './setup.common' const { getApi } = registerExtension({ name: 'demo-main', @@ -63,20 +64,22 @@ const { getApi } = registerExtension({ }, ExtensionHostKind.LocalProcess) void getApi().then(async vscode => { - const mainModelUri = vscode.Uri.file('/tmp/test.js') - await Promise.all([ - vscode.workspace.openTextDocument(mainModelUri), - vscode.workspace.openTextDocument(monaco.Uri.file('/tmp/test_readonly.js')) // open the file so vscode sees it's locked - ]) + if (!useHtmlFileSystemProvider) { + const mainModelUri = vscode.Uri.file('/tmp/test.js') + await Promise.all([ + vscode.workspace.openTextDocument(mainModelUri), + vscode.workspace.openTextDocument(monaco.Uri.file('/tmp/test_readonly.js')) // open the file so vscode sees it's locked + ]) - const diagnostics = vscode.languages.createDiagnosticCollection('demo') - diagnostics.set(mainModelUri, [{ - range: new vscode.Range(2, 9, 2, 12), - severity: vscode.DiagnosticSeverity.Error, - message: 'This is not a real error, just a demo, don\'t worry', - source: 'Demo', - code: 42 - }]) + const diagnostics = vscode.languages.createDiagnosticCollection('demo') + diagnostics.set(mainModelUri, [{ + range: new vscode.Range(2, 9, 2, 12), + severity: vscode.DiagnosticSeverity.Error, + message: 'This is not a real error, just a demo, don\'t worry', + source: 'Demo', + code: 42 + }]) + } document.querySelector('#toggleFullWorkbench')!.addEventListener('click', async () => { const url = new URL(window.location.href) From 2b5d706730ac23902bd2e9e49b24202b798ba3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 17:03:59 +0200 Subject: [PATCH 13/14] fix(demo): add local file search worker --- demo/src/setup.common.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/src/setup.common.ts b/demo/src/setup.common.ts index 5ff3a9ba..ab5be9e3 100644 --- a/demo/src/setup.common.ts +++ b/demo/src/setup.common.ts @@ -194,7 +194,8 @@ const workerLoaders: Partial> = { textMateWorker: () => new Worker(new URL('@codingame/monaco-vscode-textmate-service-override/worker', import.meta.url), { type: 'module' }), outputLinkComputer: () => new Worker(new URL('@codingame/monaco-vscode-output-service-override/worker', import.meta.url), { type: 'module' }), languageDetectionWorkerService: () => new Worker(new URL('@codingame/monaco-vscode-language-detection-worker-service-override/worker', import.meta.url), { type: 'module' }), - notebookEditorWorkerService: () => new Worker(new URL('@codingame/monaco-vscode-notebook-service-override/worker', import.meta.url), { type: 'module' }) + notebookEditorWorkerService: () => new Worker(new URL('@codingame/monaco-vscode-notebook-service-override/worker', import.meta.url), { type: 'module' }), + localFileSearchWorker: () => new Worker(new URL('@codingame/monaco-vscode-search-service-override/worker', import.meta.url), { type: 'module' }) } window.MonacoEnvironment = { From b3a467f382c2a3a7c6af86518c8cf630cea5c415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Wed, 17 Apr 2024 17:08:35 +0200 Subject: [PATCH 14/14] fix(demo): make it build --- demo/src/features/output.ts | 4 ++-- demo/src/main.common.ts | 5 ++--- demo/src/setup.common.ts | 37 ++++++++++++++++++++----------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/demo/src/features/output.ts b/demo/src/features/output.ts index 51bc307a..55db2489 100644 --- a/demo/src/features/output.ts +++ b/demo/src/features/output.ts @@ -1,5 +1,5 @@ -import { useHtmlFileSystemProvider } from '../setup.common' import { ExtensionHostKind, registerExtension } from 'vscode/extensions' +import { useHtmlFileSystemProvider } from '../setup.common' if (!useHtmlFileSystemProvider) { const { getApi } = registerExtension({ @@ -28,4 +28,4 @@ if (!useHtmlFileSystemProvider) { } }) }) -} \ No newline at end of file +} diff --git a/demo/src/main.common.ts b/demo/src/main.common.ts index 885881f7..564df072 100644 --- a/demo/src/main.common.ts +++ b/demo/src/main.common.ts @@ -1,6 +1,7 @@ import './style.css' import * as monaco from 'monaco-editor' -import './setup.common' +import { ExtensionHostKind, registerExtension } from 'vscode/extensions' +import { useHtmlFileSystemProvider } from './setup.common' import './features/output' import './features/debugger' import './features/search' @@ -51,8 +52,6 @@ import '@codingame/monaco-vscode-markdown-math-default-extension' import '@codingame/monaco-vscode-npm-default-extension' import '@codingame/monaco-vscode-media-preview-default-extension' import '@codingame/monaco-vscode-ipynb-default-extension' -import { ExtensionHostKind, registerExtension } from 'vscode/extensions' -import { useHtmlFileSystemProvider } from './setup.common' const { getApi } = registerExtension({ name: 'demo-main', diff --git a/demo/src/setup.common.ts b/demo/src/setup.common.ts index ab5be9e3..56b1f915 100644 --- a/demo/src/setup.common.ts +++ b/demo/src/setup.common.ts @@ -178,12 +178,11 @@ if (useHtmlFileSystemProvider) { }, null, 2))) fileSystemProvider.registerFile(new RegisteredMemoryFile(monaco.Uri.file('/tmp/.vscode/extensions.json'), JSON.stringify({ - "recommendations": [ - "vscodevim.vim" + recommendations: [ + 'vscodevim.vim' ] }, null, 2))) - registerFileSystemOverlay(1, fileSystemProvider) } @@ -211,7 +210,7 @@ window.MonacoEnvironment = { // Set configuration before initializing service so it's directly available (especially for the theme, to prevent a flicker) await Promise.all([ initUserConfiguration(defaultConfiguration), - initUserKeybindings(defaultKeybindings), + initUserKeybindings(defaultKeybindings) ]) export const constructOptions: IWorkbenchConstructionOptions = { @@ -244,19 +243,23 @@ export const constructOptions: IWorkbenchConstructionOptions = { 'window.title': 'Monaco-Vscode-Api${separator}${dirty}${activeEditorShort}' }, defaultLayout: { - editors: useHtmlFileSystemProvider ? undefined : [{ - uri: monaco.Uri.file('/tmp/test.js'), - viewColumn: 1 - }, { - uri: monaco.Uri.file('/tmp/test.md'), - viewColumn: 2 - }], - layout: useHtmlFileSystemProvider ? undefined : { - editors: { - orientation: 0, - groups: [{ size: 1 }, { size: 1 }] - } - }, + editors: useHtmlFileSystemProvider + ? undefined + : [{ + uri: monaco.Uri.file('/tmp/test.js'), + viewColumn: 1 + }, { + uri: monaco.Uri.file('/tmp/test.md'), + viewColumn: 2 + }], + layout: useHtmlFileSystemProvider + ? undefined + : { + editors: { + orientation: 0, + groups: [{ size: 1 }, { size: 1 }] + } + }, views: [{ id: 'custom-view' }],