@@ -11,9 +11,9 @@ use tower_lsp_server::{
1111 lsp_types:: {
1212 CodeActionParams , CodeActionResponse , ConfigurationItem , Diagnostic ,
1313 DidChangeConfigurationParams , DidChangeTextDocumentParams , DidChangeWatchedFilesParams ,
14- DidCloseTextDocumentParams , DidOpenTextDocumentParams , DidSaveTextDocumentParams ,
15- ExecuteCommandParams , InitializeParams , InitializeResult , InitializedParams , ServerInfo ,
16- Uri , WorkspaceEdit ,
14+ DidChangeWorkspaceFoldersParams , DidCloseTextDocumentParams , DidOpenTextDocumentParams ,
15+ DidSaveTextDocumentParams , ExecuteCommandParams , InitializeParams , InitializeResult ,
16+ InitializedParams , ServerInfo , Uri , WorkspaceEdit ,
1717 } ,
1818} ;
1919// #
@@ -88,9 +88,10 @@ impl Options {
8888}
8989
9090impl LanguageServer for Backend {
91- #[ expect( deprecated) ] // TODO: FIXME
91+ #[ expect( deprecated) ] // `params.root_uri` is deprecated, we are only falling back to it if no workspace folder is provided
9292 async fn initialize ( & self , params : InitializeParams ) -> Result < InitializeResult > {
9393 let server_version = env ! ( "CARGO_PKG_VERSION" ) ;
94+ // initialization_options can be anything, so we are requesting `workspace/configuration` when no initialize options are provided
9495 let options = params. initialization_options . and_then ( |mut value| {
9596 // the client supports the new settings object
9697 if let Ok ( new_settings) = serde_json:: from_value :: < Vec < WorkspaceOption > > ( value. clone ( ) )
@@ -120,27 +121,43 @@ impl LanguageServer for Backend {
120121
121122 let capabilities = Capabilities :: from ( params. capabilities ) ;
122123
123- // ToDo: add support for multiple workspace folders
124- // maybe fallback when the client does not support it
125- let root_worker = WorkspaceWorker :: new ( params. root_uri . unwrap ( ) ) ;
124+ // client sent workspace folders
125+ let workers = if let Some ( workspace_folders) = & params. workspace_folders {
126+ workspace_folders
127+ . iter ( )
128+ . map ( |workspace_folder| WorkspaceWorker :: new ( workspace_folder. uri . clone ( ) ) )
129+ . collect ( )
130+ // client sent deprecated root uri
131+ } else if let Some ( root_uri) = params. root_uri {
132+ vec ! [ WorkspaceWorker :: new( root_uri) ]
133+ // client is in single file mode, create no workers
134+ } else {
135+ vec ! [ ]
136+ } ;
126137
127138 // When the client did not send our custom `initialization_options`,
128139 // or the client does not support `workspace/configuration` request,
129140 // start the linter. We do not start the linter when the client support the request,
130141 // we will init the linter after requesting for the workspace configuration.
131142 if !capabilities. workspace_configuration || options. is_some ( ) {
132- root_worker
133- . init_linter (
134- & options
135- . unwrap_or_default ( )
136- . first ( )
137- . map ( |workspace_options| workspace_options. options . clone ( ) )
138- . unwrap_or_default ( ) ,
139- )
140- . await ;
143+ for worker in & workers {
144+ worker
145+ . init_linter (
146+ & options
147+ . clone ( )
148+ . unwrap_or_default ( )
149+ . iter ( )
150+ . find ( |workspace_option| {
151+ worker. is_responsible_for_uri ( & workspace_option. workspace_uri )
152+ } )
153+ . map ( |workspace_options| workspace_options. options . clone ( ) )
154+ . unwrap_or_default ( ) ,
155+ )
156+ . await ;
157+ }
141158 }
142159
143- * self . workspace_workers . lock ( ) . await = vec ! [ root_worker ] ;
160+ * self . workspace_workers . lock ( ) . await = workers ;
144161
145162 self . capabilities . set ( capabilities. clone ( ) ) . map_err ( |err| {
146163 let message = match err {
@@ -339,6 +356,51 @@ impl LanguageServer for Backend {
339356 self . publish_all_diagnostics ( x) . await ;
340357 }
341358
359+ async fn did_change_workspace_folders ( & self , params : DidChangeWorkspaceFoldersParams ) {
360+ let mut workers = self . workspace_workers . lock ( ) . await ;
361+ let mut cleared_diagnostics = vec ! [ ] ;
362+
363+ for folder in params. event . removed {
364+ let Some ( ( index, worker) ) = workers
365+ . iter ( )
366+ . enumerate ( )
367+ . find ( |( _, worker) | worker. is_responsible_for_uri ( & folder. uri ) )
368+ else {
369+ continue ;
370+ } ;
371+ cleared_diagnostics. extend ( worker. get_clear_diagnostics ( ) ) ;
372+ workers. remove ( index) ;
373+ }
374+
375+ self . publish_all_diagnostics ( & cleared_diagnostics) . await ;
376+
377+ // client support `workspace/configuration` request
378+ if self . capabilities . get ( ) . is_some_and ( |capabilities| capabilities. workspace_configuration )
379+ {
380+ let configurations = self
381+ . request_workspace_configuration (
382+ params. event . added . iter ( ) . map ( |w| & w. uri ) . collect ( ) ,
383+ )
384+ . await ;
385+
386+ for ( index, folder) in params. event . added . iter ( ) . enumerate ( ) {
387+ let worker = WorkspaceWorker :: new ( folder. uri . clone ( ) ) ;
388+ // get the configuration from the response and init the linter
389+ let options = configurations. get ( index) . unwrap_or ( & None ) ;
390+ worker. init_linter ( options. as_ref ( ) . unwrap_or ( & Options :: default ( ) ) ) . await ;
391+ workers. push ( worker) ;
392+ }
393+ // client does not support the request
394+ } else {
395+ for folder in params. event . added {
396+ let worker = WorkspaceWorker :: new ( folder. uri ) ;
397+ // use default options
398+ worker. init_linter ( & Options :: default ( ) ) . await ;
399+ workers. push ( worker) ;
400+ }
401+ }
402+ }
403+
342404 async fn did_save ( & self , params : DidSaveTextDocumentParams ) {
343405 debug ! ( "oxc server did save" ) ;
344406 let uri = & params. text_document . uri ;
0 commit comments