22
33use lsp_server as lsp;
44use lsp_types as types;
5+ use lsp_types:: InitializeParams ;
6+ use lsp_types:: WorkspaceFolder ;
57use std:: num:: NonZeroUsize ;
8+ use std:: ops:: Deref ;
69use std:: panic:: PanicInfo ;
710use std:: str:: FromStr ;
11+ use thiserror:: Error ;
812use types:: ClientCapabilities ;
913use types:: CodeActionKind ;
1014use types:: CodeActionOptions ;
@@ -18,6 +22,7 @@ use types::OneOf;
1822use types:: TextDocumentSyncCapability ;
1923use types:: TextDocumentSyncKind ;
2024use types:: TextDocumentSyncOptions ;
25+ use types:: Url ;
2126use types:: WorkDoneProgressOptions ;
2227use types:: WorkspaceFoldersServerCapabilities ;
2328
@@ -29,6 +34,7 @@ use self::schedule::Task;
2934use crate :: session:: AllSettings ;
3035use crate :: session:: ClientSettings ;
3136use crate :: session:: Session ;
37+ use crate :: session:: WorkspaceSettingsMap ;
3238use crate :: PositionEncoding ;
3339
3440mod api;
@@ -71,17 +77,23 @@ impl Server {
7177
7278 crate :: message:: init_messenger ( connection. make_sender ( ) ) ;
7379
80+ let InitializeParams {
81+ initialization_options,
82+ workspace_folders,
83+ client_info,
84+ ..
85+ } = init_params;
86+
7487 let mut all_settings = AllSettings :: from_value (
75- init_params
76- . initialization_options
88+ initialization_options
7789 . unwrap_or_else ( || serde_json:: Value :: Object ( serde_json:: Map :: default ( ) ) ) ,
7890 ) ;
7991 if let Some ( preview) = preview {
8092 all_settings. set_preview ( preview) ;
8193 }
8294 let AllSettings {
8395 global_settings,
84- mut workspace_settings,
96+ workspace_settings,
8597 } = all_settings;
8698
8799 crate :: trace:: init_tracing (
@@ -91,34 +103,13 @@ impl Server {
91103 . log_level
92104 . unwrap_or ( crate :: trace:: LogLevel :: Info ) ,
93105 global_settings. tracing . log_file . as_deref ( ) ,
94- init_params . client_info . as_ref ( ) ,
106+ client_info. as_ref ( ) ,
95107 ) ;
96108
97- let mut workspace_for_url = |url : lsp_types:: Url | {
98- let Some ( workspace_settings) = workspace_settings. as_mut ( ) else {
99- return ( url, ClientSettings :: default ( ) ) ;
100- } ;
101- let settings = workspace_settings. remove ( & url) . unwrap_or_else ( || {
102- tracing:: warn!( "No workspace settings found for {}" , url) ;
103- ClientSettings :: default ( )
104- } ) ;
105- ( url, settings)
106- } ;
107-
108- let workspaces = init_params
109- . workspace_folders
110- . filter ( |folders| !folders. is_empty ( ) )
111- . map ( |folders| folders. into_iter ( ) . map ( |folder| {
112- workspace_for_url ( folder. uri )
113- } ) . collect ( ) )
114- . or_else ( || {
115- tracing:: warn!( "No workspace(s) were provided during initialization. Using the current working directory as a default workspace..." ) ;
116- let uri = types:: Url :: from_file_path ( std:: env:: current_dir ( ) . ok ( ) ?) . ok ( ) ?;
117- Some ( vec ! [ workspace_for_url( uri) ] )
118- } )
119- . ok_or_else ( || {
120- anyhow:: anyhow!( "Failed to get the current working directory while creating a default workspace." )
121- } ) ?;
109+ let workspaces = Workspaces :: from_workspace_folders (
110+ workspace_folders,
111+ workspace_settings. unwrap_or_default ( ) ,
112+ ) ?;
122113
123114 Ok ( Self {
124115 connection,
@@ -127,7 +118,7 @@ impl Server {
127118 & client_capabilities,
128119 position_encoding,
129120 global_settings,
130- workspaces,
121+ & workspaces,
131122 ) ?,
132123 client_capabilities,
133124 } )
@@ -462,3 +453,121 @@ impl FromStr for SupportedCommand {
462453 } )
463454 }
464455}
456+
457+ #[ derive( Debug ) ]
458+ pub struct Workspaces ( Vec < Workspace > ) ;
459+
460+ impl Workspaces {
461+ pub fn new ( workspaces : Vec < Workspace > ) -> Self {
462+ Self ( workspaces)
463+ }
464+
465+ /// Create the workspaces from the provided workspace folders as provided by the client during
466+ /// initialization.
467+ fn from_workspace_folders (
468+ workspace_folders : Option < Vec < WorkspaceFolder > > ,
469+ mut workspace_settings : WorkspaceSettingsMap ,
470+ ) -> std:: result:: Result < Workspaces , WorkspacesError > {
471+ let mut client_settings_for_url = |url : & Url | {
472+ workspace_settings. remove ( url) . unwrap_or_else ( || {
473+ tracing:: info!(
474+ "No workspace settings found for {}, using default settings" ,
475+ url
476+ ) ;
477+ ClientSettings :: default ( )
478+ } )
479+ } ;
480+
481+ let workspaces =
482+ if let Some ( folders) = workspace_folders. filter ( |folders| !folders. is_empty ( ) ) {
483+ folders
484+ . into_iter ( )
485+ . map ( |folder| {
486+ let settings = client_settings_for_url ( & folder. uri ) ;
487+ Workspace :: new ( folder. uri ) . with_settings ( settings)
488+ } )
489+ . collect ( )
490+ } else {
491+ let current_dir = std:: env:: current_dir ( ) . map_err ( WorkspacesError :: Io ) ?;
492+ tracing:: info!(
493+ "No workspace(s) were provided during initialization. \
494+ Using the current working directory as a default workspace: {}",
495+ current_dir. display( )
496+ ) ;
497+ let uri = Url :: from_file_path ( current_dir)
498+ . map_err ( |( ) | WorkspacesError :: InvalidCurrentDir ) ?;
499+ let settings = client_settings_for_url ( & uri) ;
500+ vec ! [ Workspace :: default ( uri) . with_settings( settings) ]
501+ } ;
502+
503+ Ok ( Workspaces ( workspaces) )
504+ }
505+ }
506+
507+ impl Deref for Workspaces {
508+ type Target = [ Workspace ] ;
509+
510+ fn deref ( & self ) -> & Self :: Target {
511+ & self . 0
512+ }
513+ }
514+
515+ #[ derive( Error , Debug ) ]
516+ enum WorkspacesError {
517+ #[ error( transparent) ]
518+ Io ( #[ from] std:: io:: Error ) ,
519+ #[ error( "Failed to create a URL from the current working directory" ) ]
520+ InvalidCurrentDir ,
521+ }
522+
523+ #[ derive( Debug ) ]
524+ pub struct Workspace {
525+ /// The [`Url`] pointing to the root of the workspace.
526+ url : Url ,
527+ /// The client settings for this workspace.
528+ settings : Option < ClientSettings > ,
529+ /// Whether this is the default workspace as created by the server. This will be the case when
530+ /// no workspace folders were provided during initialization.
531+ is_default : bool ,
532+ }
533+
534+ impl Workspace {
535+ /// Create a new workspace with the given root URL.
536+ pub fn new ( url : Url ) -> Self {
537+ Self {
538+ url,
539+ settings : None ,
540+ is_default : false ,
541+ }
542+ }
543+
544+ /// Create a new default workspace with the given root URL.
545+ pub fn default ( url : Url ) -> Self {
546+ Self {
547+ url,
548+ settings : None ,
549+ is_default : true ,
550+ }
551+ }
552+
553+ /// Set the client settings for this workspace.
554+ pub fn with_settings ( mut self , settings : ClientSettings ) -> Self {
555+ self . settings = Some ( settings) ;
556+ self
557+ }
558+
559+ /// Returns the root URL of the workspace.
560+ pub ( crate ) fn url ( & self ) -> & Url {
561+ & self . url
562+ }
563+
564+ /// Returns the client settings for this workspace.
565+ pub ( crate ) fn settings ( & self ) -> Option < & ClientSettings > {
566+ self . settings . as_ref ( )
567+ }
568+
569+ /// Returns true if this is the default workspace.
570+ pub ( crate ) fn is_default ( & self ) -> bool {
571+ self . is_default
572+ }
573+ }
0 commit comments