monaco-languageclient icon indicating copy to clipboard operation
monaco-languageclient copied to clipboard

Windows file uri encoding issues

Open ghord opened this issue 3 months ago • 1 comments

Hi,

For some time I've been working to enable running roslyn c# lsp using monaco-languageclient on Windows OS. Unfortunately, roslyn lsp does not work without files being physically on disc for restore of projects, so using in-memory files only is out of the question. Specifically, it needs Windows paths for workspace folders/files.

I managed to get it working with all the features I need (btw. this library is absolutely awesome!), but I had to write backend proxy that fixes encoding of uris that monaco-languageclient sends to lsp.

From my investigation, it comes down to two issues in monaco-languageclient:

  1. Following code throws error:
const defaultFilesystemProvider = new RegisteredFileSystemProvider(false);
const fileUri = vscode.Uri.file("C:\\temp\\file.cs");
defaultFilesystemProvider.registerFile(new RegisteredMemoryFile(fileUri, ""));
files.js:98  Uncaught (in promise) EntryNotFound (FileSystemError): file not found
    at _FileSystemProviderError.create (files.js:98:33)
    at createFileSystemProviderError (files.js:108:36)
    at RegisteredFileSystemProvider._lookup (index.js:267:27)
    at RegisteredFileSystemProvider._lookupAsDirectory (index.js:278:28)
    at RegisteredFileSystemProvider.mkdirSync (index.js:416:29)
    at RegisteredFileSystemProvider.registerFile (index.js:302:43)

This I could overcome by patching the code myself. I changed RegisteredFileSystemProvider to work with Windows paths correctly, but the code is based on regex with drive letters and I'm not confident 100% correct.

  1. After patching the file provider, both workspace initalization and createModelReference were throwing warnings and errors:


const workspaceUri = vscode.Uri.file('/workspace.code-workspace');
function generateAndInitializeWorkspace(): IWorkspaceProvider {

    defaultFilesystemProvider.registerFile(new RegisteredMemoryFile(
        workspaceUri,
        JSON.stringify(<IStoredWorkspace>{
            workspaceUri,
            folders: [
                {
                    path: "C:\\temp"
                }
            ]
        })))

    return <IWorkspaceProvider>{
        open: async () => false,
        workspace: <Partial<IWorkspace>>{
            workspaceUri,
            label: "My Workspace"

        },
        trusted: true
    }
}
const modelRef = await monaco.editor.createModelReference(fileUri)

(please don't mind different paths than in code above, I only have screenshot from reproduction)

Image

At this point I gave up and used proxy to translate /c/temp/path.cs -> c:\\temp\\path.cs on the fly before it touches my LSP, this resolved the issues. Sorry there is no easy reproduction steps - hosting c# lsp is quite involved currently.

For completness, here is my service initialization code:

// adding services
await initialize({
    ...getTextMateServiceOverride(),
    ...getThemeServiceOverride(),
    ...getLanguagesServiceOverride(),
    ...getConfigurationServiceOverride(),
    ...getKeybindingsServiceOverride(),
    ...getModelServiceOverride(),
    ...getEditorServiceOverride(async (modelRef) => {
        const editor = monaco.editor.getEditors()[0]
        if (!editor) {
            return undefined
        }
        editor.setModel(modelRef.object.textEditorModel)
        return editor
    })
}, undefined, { workspaceProvider: generateAndInitializeWorkspace() });

ghord avatar Sep 13 '25 17:09 ghord