Skip to content

Commit dcf6b37

Browse files
ndmitchellfacebook-github-bot
authored andcommitted
Add workspace_root to the LSP
Summary: Useful if you want to resolve strings relative to a project root (e.g. your .buckconfig). Reviewed By: milend Differential Revision: D47190446 fbshipit-source-id: cee0fa3c9e11d19d819b8e5e89fc0755397371f3
1 parent 07df380 commit dcf6b37

File tree

3 files changed

+80
-21
lines changed

3 files changed

+80
-21
lines changed

starlark/bin/eval.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,12 @@ impl LspContext for Context {
292292
}
293293
}
294294

295-
fn resolve_load(&self, path: &str, current_file: &LspUrl) -> anyhow::Result<LspUrl> {
295+
fn resolve_load(
296+
&self,
297+
path: &str,
298+
current_file: &LspUrl,
299+
_workspace_root: Option<&Path>,
300+
) -> anyhow::Result<LspUrl> {
296301
let path = PathBuf::from(path);
297302
match current_file {
298303
LspUrl::File(current_file_path) => {
@@ -314,13 +319,15 @@ impl LspContext for Context {
314319
&self,
315320
literal: &str,
316321
current_file: &LspUrl,
322+
workspace_root: Option<&Path>,
317323
) -> anyhow::Result<Option<StringLiteralResult>> {
318-
self.resolve_load(literal, current_file).map(|url| {
319-
Some(StringLiteralResult {
320-
url,
321-
location_finder: None,
324+
self.resolve_load(literal, current_file, workspace_root)
325+
.map(|url| {
326+
Some(StringLiteralResult {
327+
url,
328+
location_finder: None,
329+
})
322330
})
323-
})
324331
}
325332

326333
fn get_load_contents(&self, uri: &LspUrl) -> anyhow::Result<Option<String>> {

starlark/src/lsp/server.rs

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use lsp_types::TextDocumentSyncCapability;
6060
use lsp_types::TextDocumentSyncKind;
6161
use lsp_types::Url;
6262
use lsp_types::WorkDoneProgressOptions;
63+
use lsp_types::WorkspaceFolder;
6364
use serde::de::DeserializeOwned;
6465
use serde::Deserialize;
6566
use 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)? {

starlark/src/lsp/test.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,12 @@ impl LspContext for TestServerContext {
147147
}
148148
}
149149

150-
fn resolve_load(&self, path: &str, current_file: &LspUrl) -> anyhow::Result<LspUrl> {
150+
fn resolve_load(
151+
&self,
152+
path: &str,
153+
current_file: &LspUrl,
154+
_workspace_root: Option<&Path>,
155+
) -> anyhow::Result<LspUrl> {
151156
let path = PathBuf::from(path);
152157
match current_file {
153158
LspUrl::File(current_file_path) => {
@@ -169,6 +174,7 @@ impl LspContext for TestServerContext {
169174
&self,
170175
literal: &str,
171176
current_file: &LspUrl,
177+
workspace_root: Option<&Path>,
172178
) -> anyhow::Result<Option<StringLiteralResult>> {
173179
let re = regex::Regex::new(r#"--(\d+):(\d+)$"#)?;
174180
let (literal, span) = match re.captures(literal) {
@@ -183,7 +189,7 @@ impl LspContext for TestServerContext {
183189
}
184190
None => (literal.to_owned(), None),
185191
};
186-
self.resolve_load(&literal, current_file)
192+
self.resolve_load(&literal, current_file, workspace_root)
187193
.map(|url| match &url {
188194
LspUrl::File(u) => match u.extension() {
189195
Some(e) if e == "star" => Some(StringLiteralResult {

0 commit comments

Comments
 (0)