@@ -60,6 +60,7 @@ use lsp_types::TextDocumentSyncCapability;
60
60
use lsp_types:: TextDocumentSyncKind ;
61
61
use lsp_types:: Url ;
62
62
use lsp_types:: WorkDoneProgressOptions ;
63
+ use lsp_types:: WorkspaceFolder ;
63
64
use serde:: de:: DeserializeOwned ;
64
65
use serde:: Deserialize ;
65
66
use serde:: Deserializer ;
@@ -264,7 +265,12 @@ pub trait LspContext {
264
265
/// implementation defined.
265
266
/// `current_file` is the the file that is including the `load()` statement, and should be used
266
267
/// if `path` is "relative" in a semantic sense.
267
- fn resolve_load ( & self , path : & str , current_file : & LspUrl ) -> anyhow:: Result < LspUrl > ;
268
+ fn resolve_load (
269
+ & self ,
270
+ path : & str ,
271
+ current_file : & LspUrl ,
272
+ workspace_root : Option < & Path > ,
273
+ ) -> anyhow:: Result < LspUrl > ;
268
274
269
275
/// Resolve a string literal into a Url and a function that specifies a locaction within that
270
276
/// target file.
@@ -276,6 +282,7 @@ pub trait LspContext {
276
282
& self ,
277
283
literal : & str ,
278
284
current_file : & LspUrl ,
285
+ workspace_root : Option < & Path > ,
279
286
) -> anyhow:: Result < Option < StringLiteralResult > > ;
280
287
281
288
/// Get the contents of a starlark program at a given path, if it exists.
@@ -402,8 +409,16 @@ impl<T: LspContext> Backend<T> {
402
409
/// NOTE: This uses the last valid parse of a file as a basis for symbol locations.
403
410
/// If a file has changed and does result in a valid parse, then symbol locations may
404
411
/// be slightly incorrect.
405
- fn goto_definition ( & self , id : RequestId , params : GotoDefinitionParams ) {
406
- self . send_response ( new_response ( id, self . find_definition ( params) ) ) ;
412
+ fn goto_definition (
413
+ & self ,
414
+ id : RequestId ,
415
+ params : GotoDefinitionParams ,
416
+ initialize_params : & InitializeParams ,
417
+ ) {
418
+ self . send_response ( new_response (
419
+ id,
420
+ self . find_definition ( params, initialize_params) ,
421
+ ) ) ;
407
422
}
408
423
409
424
/// Get the file contents of a starlark: URI.
@@ -418,9 +433,14 @@ impl<T: LspContext> Backend<T> {
418
433
self . send_response ( new_response ( id, response) ) ;
419
434
}
420
435
421
- fn resolve_load_path ( & self , path : & str , current_uri : & LspUrl ) -> anyhow:: Result < LspUrl > {
436
+ fn resolve_load_path (
437
+ & self ,
438
+ path : & str ,
439
+ current_uri : & LspUrl ,
440
+ workspace_root : Option < & Path > ,
441
+ ) -> anyhow:: Result < LspUrl > {
422
442
match current_uri {
423
- LspUrl :: File ( _) => self . context . resolve_load ( path, current_uri) ,
443
+ LspUrl :: File ( _) => self . context . resolve_load ( path, current_uri, workspace_root ) ,
424
444
LspUrl :: Starlark ( _) | LspUrl :: Other ( _) => {
425
445
Err ( ResolveLoadError :: WrongScheme ( "file://" . to_owned ( ) , current_uri. clone ( ) ) . into ( ) )
426
446
}
@@ -453,6 +473,7 @@ impl<T: LspContext> Backend<T> {
453
473
source : ResolvedSpan ,
454
474
member : Option < & str > ,
455
475
uri : LspUrl ,
476
+ workspace_root : Option < & Path > ,
456
477
) -> anyhow:: Result < Option < LocationLink > > {
457
478
let ret = match definition {
458
479
IdentifierDefinition :: Location {
@@ -465,7 +486,7 @@ impl<T: LspContext> Backend<T> {
465
486
name,
466
487
..
467
488
} => {
468
- let load_uri = self . resolve_load_path ( & path, & uri) ?;
489
+ let load_uri = self . resolve_load_path ( & path, & uri, workspace_root ) ?;
469
490
let loaded_location =
470
491
self . get_ast_or_load_from_disk ( & load_uri) ?
471
492
. and_then ( |ast| match member {
@@ -481,13 +502,15 @@ impl<T: LspContext> Backend<T> {
481
502
}
482
503
IdentifierDefinition :: NotFound => None ,
483
504
IdentifierDefinition :: LoadPath { path, .. } => {
484
- match self . resolve_load_path ( & path, & uri) {
505
+ match self . resolve_load_path ( & path, & uri, workspace_root ) {
485
506
Ok ( load_uri) => Self :: location_link ( source, & load_uri, Range :: default ( ) ) ?,
486
507
Err ( _) => None ,
487
508
}
488
509
}
489
510
IdentifierDefinition :: StringLiteral { literal, .. } => {
490
- let literal = self . context . resolve_string_literal ( & literal, & uri) ?;
511
+ let literal =
512
+ self . context
513
+ . resolve_string_literal ( & literal, & uri, workspace_root) ?;
491
514
match literal {
492
515
Some ( StringLiteralResult {
493
516
url,
@@ -540,6 +563,7 @@ impl<T: LspContext> Backend<T> {
540
563
fn find_definition (
541
564
& self ,
542
565
params : GotoDefinitionParams ,
566
+ initialize_params : & InitializeParams ,
543
567
) -> anyhow:: Result < GotoDefinitionResponse > {
544
568
let uri = params
545
569
. text_document_position_params
@@ -548,15 +572,21 @@ impl<T: LspContext> Backend<T> {
548
572
. try_into ( ) ?;
549
573
let line = params. text_document_position_params . position . line ;
550
574
let character = params. text_document_position_params . position . character ;
575
+ let workspace_root =
576
+ Self :: get_workspace_root ( initialize_params. workspace_folders . as_ref ( ) , & uri) ;
551
577
552
578
let location = match self . get_ast ( & uri) {
553
579
Some ( ast) => {
554
580
let location = ast. find_definition_at_location ( line, character) ;
555
581
let source = location. source ( ) . unwrap_or_default ( ) ;
556
582
match location {
557
- Definition :: Identifier ( definition) => {
558
- self . resolve_definition_location ( definition, source, None , uri) ?
559
- }
583
+ Definition :: Identifier ( definition) => self . resolve_definition_location (
584
+ definition,
585
+ source,
586
+ None ,
587
+ uri,
588
+ workspace_root. as_deref ( ) ,
589
+ ) ?,
560
590
// In this case we don't pass the name along in the root_definition_location,
561
591
// so it's simpler to do the lookup here, rather than threading a ton of
562
592
// information through.
@@ -582,6 +612,7 @@ impl<T: LspContext> Backend<T> {
582
612
. as_str ( ) ,
583
613
) ,
584
614
uri,
615
+ workspace_root. as_deref ( ) ,
585
616
) ?,
586
617
}
587
618
}
@@ -594,6 +625,21 @@ impl<T: LspContext> Backend<T> {
594
625
} ;
595
626
Ok ( GotoDefinitionResponse :: Link ( response) )
596
627
}
628
+
629
+ fn get_workspace_root (
630
+ workspace_roots : Option < & Vec < WorkspaceFolder > > ,
631
+ target : & LspUrl ,
632
+ ) -> Option < PathBuf > {
633
+ match target {
634
+ LspUrl :: File ( target) => workspace_roots. and_then ( |roots| {
635
+ roots
636
+ . iter ( )
637
+ . filter_map ( |root| root. uri . to_file_path ( ) . ok ( ) )
638
+ . find ( |root| target. starts_with ( root) )
639
+ } ) ,
640
+ _ => None ,
641
+ }
642
+ }
597
643
}
598
644
599
645
/// The library style pieces
@@ -622,15 +668,15 @@ impl<T: LspContext> Backend<T> {
622
668
) ) ;
623
669
}
624
670
625
- fn main_loop ( & self , _params : InitializeParams ) -> anyhow:: Result < ( ) > {
671
+ fn main_loop ( & self , initialize_params : InitializeParams ) -> anyhow:: Result < ( ) > {
626
672
self . log_message ( MessageType :: INFO , "Starlark server initialised" ) ;
627
673
for msg in & self . connection . receiver {
628
674
match msg {
629
675
Message :: Request ( req) => {
630
676
// TODO(nmj): Also implement DocumentSymbols so that some logic can
631
677
// be handled client side.
632
678
if let Some ( params) = as_request :: < GotoDefinition > ( & req) {
633
- self . goto_definition ( req. id , params) ;
679
+ self . goto_definition ( req. id , params, & initialize_params ) ;
634
680
} else if let Some ( params) = as_request :: < StarlarkFileContentsRequest > ( & req) {
635
681
self . get_starlark_file_contents ( req. id , params) ;
636
682
} else if self . connection . handle_shutdown ( & req) ? {
0 commit comments