1- import { monaco , registerTextModelContentProvider } from '@codingame/monaco-editor-wrapper'
2- import {
3- MonacoLanguageClient , DisposableCollection
4- } from 'monaco-languageclient'
1+ import { monaco } from '@codingame/monaco-editor-wrapper'
2+ import { FileSystemProviderCapabilities , FileSystemProviderError , FileSystemProviderErrorCode , FileType , IFileSystemProviderWithFileReadWriteCapability , IStat , registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override'
53import { StaticFeature , FeatureState } from 'vscode-languageclient/lib/common/api'
64import { DidSaveTextDocumentNotification , Disposable , DocumentSelector , Emitter , ServerCapabilities , TextDocumentSyncOptions } from 'vscode-languageserver-protocol'
75import * as vscode from 'vscode'
6+ import { IDisposable } from 'monaco-editor'
7+ import { DisposableStore } from 'vscode/monaco'
88import { willShutdownNotificationType , WillShutdownParams } from './customRequests'
99import { Infrastructure } from './infrastructure'
1010import { LanguageClientManager } from './languageClient'
11- import { getServices } from './services '
11+ import { MonacoLanguageClient } from './createLanguageClient '
1212
1313interface ResolvedTextDocumentSyncCapabilities {
1414 resolvedTextDocumentSync ?: TextDocumentSyncOptions
@@ -31,7 +31,7 @@ export class InitializeTextDocumentFeature implements StaticFeature {
3131 const languageClient = this . languageClient
3232 async function saveFile ( textDocument : vscode . TextDocument ) {
3333 if ( documentSelector != null && vscode . languages . match ( documentSelector , textDocument ) > 0 && textDocument . uri . scheme === 'file' ) {
34- await infrastructure . saveFileContent ?.( textDocument , vscode . TextDocumentSaveReason . Manual , languageClient )
34+ await infrastructure . saveFileContent ?.( textDocument . uri , textDocument . getText ( ) , languageClient )
3535
3636 // Always send notification even if the server doesn't support it (because csharp register the didSave feature too late)
3737 await languageClient . sendNotification ( DidSaveTextDocumentNotification . type , {
@@ -53,7 +53,7 @@ export class InitializeTextDocumentFeature implements StaticFeature {
5353 }
5454 }
5555
56- dispose ( ) : void {
56+ clear ( ) : void {
5757 this . didOpenTextDocumentDisposable ?. dispose ( )
5858 }
5959}
@@ -73,7 +73,78 @@ export class WillDisposeFeature implements StaticFeature {
7373 }
7474 }
7575
76- dispose ( ) : void { }
76+ clear ( ) : void { }
77+ }
78+
79+ const encoder = new TextEncoder ( )
80+ const decoder = new TextDecoder ( )
81+ class InfrastructureTextFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability {
82+ capabilities = FileSystemProviderCapabilities . FileReadWrite | FileSystemProviderCapabilities . PathCaseSensitive | FileSystemProviderCapabilities . Readonly
83+ constructor ( private infrastructure : Infrastructure , private languageClientManager : LanguageClientManager ) {
84+ }
85+
86+ private cachedContent : Map < string , Promise < string | undefined > > = new Map ( )
87+ private async getFileContent ( resource : monaco . Uri ) : Promise < string | undefined > {
88+ if ( ! this . cachedContent . has ( resource . toString ( ) ) && resource . toString ( ) . includes ( '.' ) ) {
89+ this . cachedContent . set ( resource . toString ( ) , this . infrastructure . getFileContent ! ( resource , this . languageClientManager ) )
90+ }
91+ return await this . cachedContent . get ( resource . toString ( ) )
92+ }
93+
94+ async readFile ( resource : monaco . Uri ) : Promise < Uint8Array > {
95+ if ( ! resource . toString ( ) . startsWith ( this . infrastructure . rootUri ) ) {
96+ throw FileSystemProviderError . create ( 'file not found' , FileSystemProviderErrorCode . FileNotFound )
97+ }
98+ const content = await this . getFileContent ( resource )
99+ return encoder . encode ( content )
100+ }
101+
102+ async writeFile ( ) : Promise < void > {
103+ throw FileSystemProviderError . create ( 'not allowed' , FileSystemProviderErrorCode . NoPermissions )
104+ }
105+
106+ onDidChangeCapabilities = new vscode . EventEmitter < never > ( ) . event
107+ onDidChangeFile = new vscode . EventEmitter < never > ( ) . event
108+ watch ( ) : IDisposable {
109+ return {
110+ dispose ( ) { }
111+ }
112+ }
113+
114+ async stat ( resource : monaco . Uri ) : Promise < IStat > {
115+ if ( ! resource . toString ( ) . startsWith ( this . infrastructure . rootUri ) ) {
116+ throw FileSystemProviderError . create ( 'file not found' , FileSystemProviderErrorCode . FileNotFound )
117+ }
118+ try {
119+ const content = await this . getFileContent ( resource )
120+ if ( content != null ) {
121+ return {
122+ type : FileType . File ,
123+ size : encoder . encode ( content ) . length ,
124+ mtime : Date . now ( ) ,
125+ ctime : Date . now ( )
126+ }
127+ }
128+ } catch ( err ) {
129+ throw FileSystemProviderError . create ( err as Error , FileSystemProviderErrorCode . Unknown )
130+ }
131+ throw FileSystemProviderError . create ( 'file not found' , FileSystemProviderErrorCode . FileNotFound )
132+ }
133+
134+ async mkdir ( ) : Promise < void > {
135+ }
136+
137+ async readdir ( ) {
138+ return [ ]
139+ }
140+
141+ delete ( ) : Promise < void > {
142+ throw new Error ( 'Method not implemented.' )
143+ }
144+
145+ rename ( ) : Promise < void > {
146+ throw new Error ( 'Method not implemented.' )
147+ }
77148}
78149
79150export class FileSystemFeature implements StaticFeature {
@@ -82,22 +153,27 @@ export class FileSystemFeature implements StaticFeature {
82153 constructor ( private infrastructure : Infrastructure , private languageClientManager : LanguageClientManager ) { }
83154
84155 private registerFileHandlers ( ) : Disposable {
85- const disposableCollection = new DisposableCollection ( )
86- const infrastructure = this . infrastructure
87- const languageClientManager = this . languageClientManager
88- disposableCollection . push ( registerTextModelContentProvider ( 'file' , {
89- async provideTextContent ( resource : monaco . Uri ) : Promise < monaco . editor . ITextModel | null > {
90- return await infrastructure . getFileContent ( resource , languageClientManager )
91- }
92- } ) )
93- disposableCollection . push ( getServices ( ) . workspace . registerSaveDocumentHandler ( {
94- async saveTextContent ( document , reason ) {
95- if ( languageClientManager . isModelManaged ( document ) && document . uri . scheme === 'file' ) {
96- await infrastructure . saveFileContent ?.( document , reason , languageClientManager )
156+ const disposables = new DisposableStore ( )
157+
158+ // Register readonly file system overlay to access remote files
159+ if ( this . infrastructure . getFileContent != null ) {
160+ disposables . add ( registerFileSystemOverlay ( - 1 , new InfrastructureTextFileSystemProvider ( this . infrastructure , this . languageClientManager ) ) )
161+ }
162+
163+ if ( this . infrastructure . saveFileContent != null ) {
164+ const watcher = vscode . workspace . createFileSystemWatcher ( new vscode . RelativePattern ( vscode . Uri . parse ( this . infrastructure . rootUri ) , '**/*' ) )
165+ disposables . add ( watcher )
166+ const onFileChange = async ( uri : vscode . Uri ) => {
167+ if ( ( await vscode . workspace . fs . stat ( uri ) ) . type === vscode . FileType . File ) {
168+ const content = await vscode . workspace . fs . readFile ( uri )
169+ await this . infrastructure . saveFileContent ?.( uri , decoder . decode ( content ) , this . languageClientManager )
97170 }
98171 }
99- } ) )
100- return disposableCollection
172+ watcher . onDidChange ( onFileChange )
173+ watcher . onDidCreate ( onFileChange )
174+ }
175+
176+ return disposables
101177 }
102178
103179 fillClientCapabilities ( ) : void { }
@@ -117,4 +193,8 @@ export class FileSystemFeature implements StaticFeature {
117193 this . disposable ?. dispose ( )
118194 this . disposable = undefined
119195 }
196+
197+ clear ( ) : void {
198+ this . dispose ( )
199+ }
120200}
0 commit comments