@@ -60,6 +60,7 @@ use lsp_types::TextDocumentSyncCapability;
6060use lsp_types:: TextDocumentSyncKind ;
6161use lsp_types:: Url ;
6262use lsp_types:: WorkDoneProgressOptions ;
63+ use lsp_types:: WorkspaceFolder ;
6364use serde:: de:: DeserializeOwned ;
6465use serde:: Deserialize ;
6566use serde:: Deserializer ;
@@ -264,7 +265,12 @@ pub trait LspContext {
264265 /// implementation defined.
265266 /// `current_file` is the the file that is including the `load()` statement, and should be used
266267 /// 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 > ;
268274
269275 /// Resolve a string literal into a Url and a function that specifies a locaction within that
270276 /// target file.
@@ -276,6 +282,7 @@ pub trait LspContext {
276282 & self ,
277283 literal : & str ,
278284 current_file : & LspUrl ,
285+ workspace_root : Option < & Path > ,
279286 ) -> anyhow:: Result < Option < StringLiteralResult > > ;
280287
281288 /// Get the contents of a starlark program at a given path, if it exists.
@@ -402,8 +409,16 @@ impl<T: LspContext> Backend<T> {
402409 /// NOTE: This uses the last valid parse of a file as a basis for symbol locations.
403410 /// If a file has changed and does result in a valid parse, then symbol locations may
404411 /// 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+ ) ) ;
407422 }
408423
409424 /// Get the file contents of a starlark: URI.
@@ -418,9 +433,14 @@ impl<T: LspContext> Backend<T> {
418433 self . send_response ( new_response ( id, response) ) ;
419434 }
420435
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 > {
422442 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 ) ,
424444 LspUrl :: Starlark ( _) | LspUrl :: Other ( _) => {
425445 Err ( ResolveLoadError :: WrongScheme ( "file://" . to_owned ( ) , current_uri. clone ( ) ) . into ( ) )
426446 }
@@ -453,6 +473,7 @@ impl<T: LspContext> Backend<T> {
453473 source : ResolvedSpan ,
454474 member : Option < & str > ,
455475 uri : LspUrl ,
476+ workspace_root : Option < & Path > ,
456477 ) -> anyhow:: Result < Option < LocationLink > > {
457478 let ret = match definition {
458479 IdentifierDefinition :: Location {
@@ -465,7 +486,7 @@ impl<T: LspContext> Backend<T> {
465486 name,
466487 ..
467488 } => {
468- let load_uri = self . resolve_load_path ( & path, & uri) ?;
489+ let load_uri = self . resolve_load_path ( & path, & uri, workspace_root ) ?;
469490 let loaded_location =
470491 self . get_ast_or_load_from_disk ( & load_uri) ?
471492 . and_then ( |ast| match member {
@@ -481,13 +502,15 @@ impl<T: LspContext> Backend<T> {
481502 }
482503 IdentifierDefinition :: NotFound => None ,
483504 IdentifierDefinition :: LoadPath { path, .. } => {
484- match self . resolve_load_path ( & path, & uri) {
505+ match self . resolve_load_path ( & path, & uri, workspace_root ) {
485506 Ok ( load_uri) => Self :: location_link ( source, & load_uri, Range :: default ( ) ) ?,
486507 Err ( _) => None ,
487508 }
488509 }
489510 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) ?;
491514 match literal {
492515 Some ( StringLiteralResult {
493516 url,
@@ -540,6 +563,7 @@ impl<T: LspContext> Backend<T> {
540563 fn find_definition (
541564 & self ,
542565 params : GotoDefinitionParams ,
566+ initialize_params : & InitializeParams ,
543567 ) -> anyhow:: Result < GotoDefinitionResponse > {
544568 let uri = params
545569 . text_document_position_params
@@ -548,15 +572,21 @@ impl<T: LspContext> Backend<T> {
548572 . try_into ( ) ?;
549573 let line = params. text_document_position_params . position . line ;
550574 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) ;
551577
552578 let location = match self . get_ast ( & uri) {
553579 Some ( ast) => {
554580 let location = ast. find_definition_at_location ( line, character) ;
555581 let source = location. source ( ) . unwrap_or_default ( ) ;
556582 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+ ) ?,
560590 // In this case we don't pass the name along in the root_definition_location,
561591 // so it's simpler to do the lookup here, rather than threading a ton of
562592 // information through.
@@ -582,6 +612,7 @@ impl<T: LspContext> Backend<T> {
582612 . as_str ( ) ,
583613 ) ,
584614 uri,
615+ workspace_root. as_deref ( ) ,
585616 ) ?,
586617 }
587618 }
@@ -594,6 +625,21 @@ impl<T: LspContext> Backend<T> {
594625 } ;
595626 Ok ( GotoDefinitionResponse :: Link ( response) )
596627 }
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+ }
597643}
598644
599645/// The library style pieces
@@ -622,15 +668,15 @@ impl<T: LspContext> Backend<T> {
622668 ) ) ;
623669 }
624670
625- fn main_loop ( & self , _params : InitializeParams ) -> anyhow:: Result < ( ) > {
671+ fn main_loop ( & self , initialize_params : InitializeParams ) -> anyhow:: Result < ( ) > {
626672 self . log_message ( MessageType :: INFO , "Starlark server initialised" ) ;
627673 for msg in & self . connection . receiver {
628674 match msg {
629675 Message :: Request ( req) => {
630676 // TODO(nmj): Also implement DocumentSymbols so that some logic can
631677 // be handled client side.
632678 if let Some ( params) = as_request :: < GotoDefinition > ( & req) {
633- self . goto_definition ( req. id , params) ;
679+ self . goto_definition ( req. id , params, & initialize_params ) ;
634680 } else if let Some ( params) = as_request :: < StarlarkFileContentsRequest > ( & req) {
635681 self . get_starlark_file_contents ( req. id , params) ;
636682 } else if self . connection . handle_shutdown ( & req) ? {
0 commit comments