@@ -27,7 +27,8 @@ use log::{error, trace, warn};
27
27
use miette:: Diagnostic ;
28
28
pub use project_system:: JSFileEntry ;
29
29
use protocol:: {
30
- CompletionList , DiagnosticUpdate , Hover , Location , SignatureHelp , WorkspaceConfigurationUpdate ,
30
+ CompletionList , DiagnosticUpdate , Hover , Location , NotebookMetadata , SignatureHelp ,
31
+ WorkspaceConfigurationUpdate ,
31
32
} ;
32
33
use qsc:: { compile:: Error , PackageType , TargetProfile } ;
33
34
use qsc_project:: FileSystemAsync ;
@@ -46,11 +47,12 @@ type PinnedFuture<T> = Pin<Box<dyn Future<Output = T>>>;
46
47
type AsyncFunction < ' a , Arg , Return > = Box < dyn Fn ( Arg ) -> PinnedFuture < Return > + ' a > ;
47
48
48
49
pub struct LanguageService < ' a > {
49
- /// Workspace configuration can include compiler settings
50
- /// that affect error checking and other language server behavior.
51
- /// Currently these settings apply to all documents in the
52
- /// workspace. Per-document configurations are not supported.
53
- configuration : WorkspaceConfiguration ,
50
+ /// Workspace-wide configuration settings. These can include compiler settings that
51
+ /// affect error checking and other language server behavior.
52
+ ///
53
+ /// Some settings can be set both at the compilation scope and at the workspace scope.
54
+ /// Compilation-scoped settings take precedence over workspace-scoped settings.
55
+ configuration : Configuration ,
54
56
/// A `CompilationUri` is an identifier for a unique compilation.
55
57
/// It is NOT required to be a uri that represents an actual document.
56
58
///
@@ -62,7 +64,11 @@ pub struct LanguageService<'a> {
62
64
/// The `CompilatinUri` is used when compilation-level errors get reported
63
65
/// to the client. Compilation-level errors are defined as errors without
64
66
/// an associated source document.
65
- compilations : FxHashMap < CompilationUri , Compilation > ,
67
+ ///
68
+ /// `PartialConfiguration` contains configuration overrides for this
69
+ /// compilation, explicitly specified through a project manifest (not currently implemented)
70
+ /// or notebook metadata.
71
+ compilations : FxHashMap < CompilationUri , ( Compilation , PartialConfiguration ) > ,
66
72
/// All the documents that we were told about by the client.
67
73
///
68
74
/// This map doesn't necessarily contain ALL the documents that
@@ -99,12 +105,12 @@ pub enum PendingUpdate {
99
105
}
100
106
101
107
#[ derive( Debug ) ]
102
- struct WorkspaceConfiguration {
108
+ struct Configuration {
103
109
pub target_profile : TargetProfile ,
104
110
pub package_type : PackageType ,
105
111
}
106
112
107
- impl Default for WorkspaceConfiguration {
113
+ impl Default for Configuration {
108
114
fn default ( ) -> Self {
109
115
Self {
110
116
target_profile : TargetProfile :: Full ,
@@ -113,6 +119,12 @@ impl Default for WorkspaceConfiguration {
113
119
}
114
120
}
115
121
122
+ #[ derive( Default ) ]
123
+ struct PartialConfiguration {
124
+ pub target_profile : Option < TargetProfile > ,
125
+ pub package_type : Option < PackageType > ,
126
+ }
127
+
116
128
#[ derive( Debug ) ]
117
129
struct OpenDocument {
118
130
/// This version is the document version provided by the client.
@@ -133,7 +145,7 @@ impl<'a> LanguageService<'a> {
133
145
+ ' a ,
134
146
) -> Self {
135
147
LanguageService {
136
- configuration : WorkspaceConfiguration :: default ( ) ,
148
+ configuration : Configuration :: default ( ) ,
137
149
compilations : FxHashMap :: default ( ) ,
138
150
open_documents : FxHashMap :: default ( ) ,
139
151
documents_with_errors : FxHashSet :: default ( ) ,
@@ -149,6 +161,8 @@ impl<'a> LanguageService<'a> {
149
161
/// Updates the workspace configuration. If any compiler settings are updated,
150
162
/// a recompilation may be triggered, which will result in a new set of diagnostics
151
163
/// being published.
164
+ ///
165
+ /// LSP: workspace/didChangeConfiguration
152
166
pub fn update_configuration ( & mut self , configuration : & WorkspaceConfigurationUpdate ) {
153
167
trace ! ( "update_configuration: {configuration:?}" ) ;
154
168
@@ -214,7 +228,8 @@ impl<'a> LanguageService<'a> {
214
228
uri. into ( )
215
229
} ;
216
230
trace ! ( "Loaded project uri {uri} with {} sources" , sources. len( ) ) ;
217
- self . compilations . insert ( uri. clone ( ) , compilation) ;
231
+ self . compilations
232
+ . insert ( uri. clone ( ) , ( compilation, PartialConfiguration :: default ( ) ) ) ;
218
233
219
234
// There may be open buffers with sources in the project.
220
235
// These buffers need to have their diagnostics reloaded,
@@ -278,8 +293,12 @@ impl<'a> LanguageService<'a> {
278
293
/// containing the "%%qsharp" cell magic.
279
294
///
280
295
/// LSP: notebookDocument/didOpen, notebookDocument/didChange
281
- pub fn update_notebook_document < ' b , I > ( & mut self , notebook_uri : & str , cells : I )
282
- where
296
+ pub fn update_notebook_document < ' b , I > (
297
+ & mut self ,
298
+ notebook_uri : & str ,
299
+ notebook_metadata : & NotebookMetadata ,
300
+ cells : I ,
301
+ ) where
283
302
I : Iterator < Item = ( & ' b str , u32 , & ' b str ) > , // uri, version, text - basically DidChangeTextDocumentParams in LSP
284
303
{
285
304
trace ! ( "update_notebook_document: {notebook_uri}" ) ;
@@ -290,9 +309,15 @@ impl<'a> LanguageService<'a> {
290
309
self . open_documents
291
310
. retain ( |_, open_doc| notebook_uri != open_doc. compilation . as_ref ( ) ) ;
292
311
312
+ let notebook_configuration = PartialConfiguration {
313
+ target_profile : notebook_metadata. target_profile ,
314
+ package_type : None ,
315
+ } ;
316
+ let configuration = merge_configurations ( & notebook_configuration, & self . configuration ) ;
317
+
293
318
// Compile the notebook and add each cell into the document map
294
- let compilation =
295
- Compilation :: new_notebook ( cells. map ( |( cell_uri, version, cell_contents) | {
319
+ let compilation = Compilation :: new_notebook (
320
+ cells. map ( |( cell_uri, version, cell_contents) | {
296
321
trace ! ( "update_notebook_document: cell: {cell_uri} {version}" ) ;
297
322
self . open_documents . insert (
298
323
( * cell_uri) . into ( ) ,
@@ -302,10 +327,14 @@ impl<'a> LanguageService<'a> {
302
327
} ,
303
328
) ;
304
329
( Arc :: from ( cell_uri) , Arc :: from ( cell_contents) )
305
- } ) ) ;
330
+ } ) ,
331
+ configuration. target_profile ,
332
+ ) ;
306
333
307
- self . compilations
308
- . insert ( compilation_uri. clone ( ) , compilation) ;
334
+ self . compilations . insert (
335
+ compilation_uri. clone ( ) ,
336
+ ( compilation, notebook_configuration) ,
337
+ ) ;
309
338
310
339
self . publish_diagnostics ( ) ;
311
340
}
@@ -430,7 +459,7 @@ impl<'a> LanguageService<'a> {
430
459
panic ! ( "{op_name} should not be called before compilation has been initialized" , )
431
460
} ) ;
432
461
433
- let res = op ( compilation, uri, offset) ;
462
+ let res = op ( & compilation. 0 , uri, offset) ;
434
463
trace ! ( "{op_name} result: {res:?}" ) ;
435
464
res
436
465
}
@@ -443,7 +472,7 @@ impl<'a> LanguageService<'a> {
443
472
444
473
for ( compilation_uri, compilation) in & self . compilations {
445
474
trace ! ( "publishing diagnostics for {compilation_uri}" ) ;
446
- for ( uri, errors) in map_errors_to_docs ( compilation_uri, & compilation. errors ) {
475
+ for ( uri, errors) in map_errors_to_docs ( compilation_uri, & compilation. 0 . errors ) {
447
476
if !self . documents_with_errors . insert ( uri. clone ( ) ) {
448
477
// We already published diagnostics for this document for
449
478
// a different compilation.
@@ -486,6 +515,9 @@ impl<'a> LanguageService<'a> {
486
515
self . configuration . target_profile = target_profile;
487
516
}
488
517
518
+ // Possible optimization: some projects will have overrides for these configurations,
519
+ // so workspace updates won't impact them. We could exclude those projects
520
+ // from recompilation, but we don't right now.
489
521
trace ! ( "need_recompile after configuration update: {need_recompile}" ) ;
490
522
need_recompile
491
523
}
@@ -495,16 +527,31 @@ impl<'a> LanguageService<'a> {
495
527
/// diagnostics for all documents.
496
528
fn recompile_all ( & mut self ) {
497
529
for compilation in self . compilations . values_mut ( ) {
498
- compilation. recompile (
499
- self . configuration . package_type ,
500
- self . configuration . target_profile ,
501
- ) ;
530
+ let configuration = merge_configurations ( & compilation. 1 , & self . configuration ) ;
531
+ compilation
532
+ . 0
533
+ . recompile ( configuration . package_type , configuration . target_profile ) ;
502
534
}
503
535
504
536
self . publish_diagnostics ( ) ;
505
537
}
506
538
}
507
539
540
+ /// Merges workspace configuration with any compilation-specific overrides.
541
+ fn merge_configurations (
542
+ compilation_scope : & PartialConfiguration ,
543
+ workspace_scope : & Configuration ,
544
+ ) -> Configuration {
545
+ Configuration {
546
+ target_profile : compilation_scope
547
+ . target_profile
548
+ . unwrap_or ( workspace_scope. target_profile ) ,
549
+ package_type : compilation_scope
550
+ . package_type
551
+ . unwrap_or ( workspace_scope. package_type ) ,
552
+ }
553
+ }
554
+
508
555
fn map_errors_to_docs (
509
556
compilation_uri : & Arc < str > ,
510
557
errors : & Vec < Error > ,
0 commit comments