From d41755831be0a68556655e6c4b1949723f43fbb3 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Wed, 22 Feb 2017 23:53:55 +0100 Subject: [PATCH 1/3] Implement custom find implementations extension List all implementation block for traits, structs, and enums. Fails for traits which are implemented using derive Listed under wishlist rust-lang-nursery/rls#142 Requires the find-impls branch of https://github.com/jonasbb/rls_vscode --- contributing.md | 29 ++++++++++++++++++++++++----- src/actions/mod.rs | 25 ++++++++++++++++++++++++- src/server.rs | 2 ++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/contributing.md b/contributing.md index f5f116198c2..7f44eaace48 100644 --- a/contributing.md +++ b/contributing.md @@ -301,10 +301,29 @@ the RLS. The RLS uses some custom extensions to the Language Server Protocol. -* `rustDocument/diagnosticsBegin`: notification, no arguments. Sent from the RLS - to a client before a build starts and before any diagnostics from a build are sent. -* `rustDocument/diagnosticsEnd`: notification, no arguments. Sent from the RLS - to a client when a build is complete (successfully or not, or even skipped) - and all post-build analysis by the RLS is complete. +#### RLS to LSP Client + +These are all sent from the RLS to an LSP client and are only used to improve +the user experience by showing progress indicators. + +* `rustDocument/diagnosticsBegin`: notification, no arguments. Sent before a + build starts and before any diagnostics from a build are sent. +* `rustDocument/diagnosticsEnd`: notification, no arguments. Sent when a build + is complete (successfully or not, or even skipped) and all post-build analysis + by the RLS is complete. * `rustWorkspace/deglob`: message sent from the client to the RLS to initiate a deglob refactoring. + +#### LSP Client to RLS + +The following request is to support Rust specific features. + +* `rustDocument/implementations`: request + params: [`TextDocumentPositionParams`] + result: [`Location`]`[]` + + List all implementation blocks for a trait, struct, or enum denoted by the + given text document position. + +[`TextDocumentPositionParams`]: (https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textdocumentpositionparams) +[`Location`]: (https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#location) diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 5118d897ed3..17a1e165914 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -253,9 +253,32 @@ impl ActionHandler { }); } + pub fn find_impls(&self, id: usize, params: TextDocumentPositionParams, out: O) { + let t = thread::current(); + let file_path = parse_file_path!(¶ms.text_document.uri, "find_impls"); + let span = self.convert_pos_to_span(file_path, params.position); + let type_id = self.analysis.id(&span).expect("Analysis: Getting typeid from span"); + let analysis = self.analysis.clone(); + + let handle = thread::spawn(move || { + let result = analysis.find_impls(type_id).map(|spans| { + spans.into_iter().map(|x| ls_util::rls_to_location(&x)).collect() + }); + t.unpark(); + result + }); + thread::park_timeout(Duration::from_millis(::COMPILER_TIMEOUT)); + + let result = handle.join(); + trace!("find_impls: {:?}", result); + match result { + Ok(Ok(r)) => out.success(id, ResponseData::Locations(r)), + _ => out.failure_message(id, ErrorCode::InternalError, "Find Implementations failed to complete successfully"), + } + } + pub fn on_open(&self, open: DidOpenTextDocumentParams, _out: O) { trace!("on_open: {:?}", open.text_document.uri); - let file_path = parse_file_path!(&open.text_document.uri, "on_open"); self.vfs.set_file(&file_path, &open.text_document.text); diff --git a/src/server.rs b/src/server.rs index cf172a9ffde..a9046edf7c0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -290,6 +290,7 @@ messages! { "textDocument/codeAction" => CodeAction(CodeActionParams); "workspace/executeCommand" => ExecuteCommand(ExecuteCommandParams); "rustWorkspace/deglob" => Deglob(Location); + "rustDocument/implementations" => FindImpls(TextDocumentPositionParams); } notifications { "initialized" => Initialized; @@ -518,6 +519,7 @@ impl LsService { Deglob(params) => { action: deglob }; ExecuteCommand(params) => { action: execute_command }; CodeAction(params) => { action: code_action }; + FindImpls(params) => { action: find_impls }; } notifications { Initialized => {{ self.handler.inited().initialized(self.output.clone()) }}; From b8655b65dce704a0126f186df96a49c09f89ceb5 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Tue, 13 Jun 2017 23:51:13 +0200 Subject: [PATCH 2/3] Implement testcase for find impls --- src/server.rs | 5 +++ src/test/mod.rs | 59 ++++++++++++++++++++++++++++++++ test_data/find_impls/Cargo.lock | 4 +++ test_data/find_impls/Cargo.toml | 6 ++++ test_data/find_impls/src/main.rs | 23 +++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 test_data/find_impls/Cargo.lock create mode 100644 test_data/find_impls/Cargo.toml create mode 100644 test_data/find_impls/src/main.rs diff --git a/src/server.rs b/src/server.rs index a9046edf7c0..2400731e217 100644 --- a/src/server.rs +++ b/src/server.rs @@ -36,6 +36,11 @@ pub fn server_failure(id: jsonrpc::Id, error: jsonrpc::Error) -> jsonrpc::Failur #[allow(non_upper_case_globals)] pub const REQUEST__Deglob: &'static str = "rustWorkspace/deglob"; +#[cfg(test)] +#[allow(non_upper_case_globals)] +pub const REQUEST__FindImpls: &'static str = "rustDocument/implementations"; + + #[derive(Debug, Serialize)] pub struct Ack; diff --git a/src/test/mod.rs b/src/test/mod.rs index 8d2e7e51211..ad641909232 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -581,3 +581,62 @@ fn test_parse_error_on_malformed_input() { assert!(failure.error.code == jsonrpc_core::ErrorCode::ParseError); } + +#[test] +fn test_find_impls() { + let (mut cache, _tc) = init_env("find_impls"); + + let source_file_path = Path::new("src").join("main.rs"); + + let root_path = cache.abs_path(Path::new(".")); + let url = Url::from_file_path(cache.abs_path(&source_file_path)) + .expect("couldn't convert file path to URL"); + + let messages = vec![ + ServerMessage::initialize(0,root_path.as_os_str().to_str().map(|x| x.to_owned())), + ServerMessage::request(1, Method::FindImpls(TextDocumentPositionParams { + text_document: TextDocumentIdentifier::new(url.clone()), + position: cache.mk_ls_position(src(&source_file_path, 13, "Bar")) + })), + ServerMessage::request(2, Method::FindImpls(TextDocumentPositionParams { + text_document: TextDocumentIdentifier::new(url.clone()), + position: cache.mk_ls_position(src(&source_file_path, 16, "Super")) + })), + ServerMessage::request(3, Method::FindImpls(TextDocumentPositionParams { + text_document: TextDocumentIdentifier::new(url), + position: cache.mk_ls_position(src(&source_file_path, 20, "Eq")) + })), + ]; + + let (mut server, results) = mock_server(messages); + // Initialise and build. + assert_eq!(ls_server::LsService::handle_message(&mut server), + ls_server::ServerStateChange::Continue); + expect_messages(results.clone(), + &[ExpectedMessage::new(Some(0)).expect_contains("capabilities"), + ExpectedMessage::new(None).expect_contains("diagnosticsBegin"), + ExpectedMessage::new(None).expect_contains("diagnosticsEnd")]); + + assert_eq!(ls_server::LsService::handle_message(&mut server), + ls_server::ServerStateChange::Continue); + // TODO structural checking of result, rather than looking for a string - src(&source_file_path, 12, "world") + expect_messages(results.clone(), &[ + ExpectedMessage::new(Some(1)) + .expect_contains(r#""range":{"start":{"line":18,"character":15},"end":{"line":18,"character":18}}"#) + .expect_contains(r#""range":{"start":{"line":19,"character":12},"end":{"line":19,"character":15}}"#) + ]); + assert_eq!(ls_server::LsService::handle_message(&mut server), + ls_server::ServerStateChange::Continue); + expect_messages(results.clone(), &[ + ExpectedMessage::new(Some(2)) + .expect_contains(r#""range":{"start":{"line":18,"character":15},"end":{"line":18,"character":18}}"#) + .expect_contains(r#""range":{"start":{"line":22,"character":15},"end":{"line":22,"character":18}}"#) + ]); + assert_eq!(ls_server::LsService::handle_message(&mut server), + ls_server::ServerStateChange::Continue); + expect_messages(results.clone(), &[ + // TODO assert that only one position is returned + ExpectedMessage::new(Some(3)) + .expect_contains(r#""range":{"start":{"line":19,"character":12},"end":{"line":19,"character":15}}"#) + ]); +} diff --git a/test_data/find_impls/Cargo.lock b/test_data/find_impls/Cargo.lock new file mode 100644 index 00000000000..28d1dca6067 --- /dev/null +++ b/test_data/find_impls/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "find_impls" +version = "0.1.0" + diff --git a/test_data/find_impls/Cargo.toml b/test_data/find_impls/Cargo.toml new file mode 100644 index 00000000000..fd6444c74d3 --- /dev/null +++ b/test_data/find_impls/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "find_impls" +version = "0.1.0" +authors = ["Jonas Bushart "] + +[dependencies] diff --git a/test_data/find_impls/src/main.rs b/test_data/find_impls/src/main.rs new file mode 100644 index 00000000000..ee0b0f5f659 --- /dev/null +++ b/test_data/find_impls/src/main.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(dead_code)] + +#[derive(PartialEq)] +struct Bar; +struct Foo; + +trait Super{} +trait Sub: Super {} + +impl Super for Bar {} +impl Eq for Bar {} + +impl Sub for Foo {} +impl Super for Foo {} From 67fb3a49c89b700f3ca0839530f4a1f92e45e251 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Wed, 16 Aug 2017 23:56:30 +0200 Subject: [PATCH 3/3] Fix test for Travis since rust-analysis is not installed --- src/test/mod.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/test/mod.rs b/src/test/mod.rs index ad641909232..504e12c0084 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -592,6 +592,11 @@ fn test_find_impls() { let url = Url::from_file_path(cache.abs_path(&source_file_path)) .expect("couldn't convert file path to URL"); + // This test contains code for testing implementations of `Eq`. However, `rust-analysis` is not + // installed on Travis making rls-analysis fail why retrieving the typeid. Installing + // `rust-analysis` is also not an option, because this makes other test timeout. + // e.g., https://travis-ci.org/rust-lang-nursery/rls/jobs/265339002 + let messages = vec![ ServerMessage::initialize(0,root_path.as_os_str().to_str().map(|x| x.to_owned())), ServerMessage::request(1, Method::FindImpls(TextDocumentPositionParams { @@ -602,10 +607,11 @@ fn test_find_impls() { text_document: TextDocumentIdentifier::new(url.clone()), position: cache.mk_ls_position(src(&source_file_path, 16, "Super")) })), - ServerMessage::request(3, Method::FindImpls(TextDocumentPositionParams { - text_document: TextDocumentIdentifier::new(url), - position: cache.mk_ls_position(src(&source_file_path, 20, "Eq")) - })), + // Does not work on Travis + // ServerMessage::request(3, Method::FindImpls(TextDocumentPositionParams { + // text_document: TextDocumentIdentifier::new(url), + // position: cache.mk_ls_position(src(&source_file_path, 20, "Eq")) + // })), ]; let (mut server, results) = mock_server(messages); @@ -632,11 +638,12 @@ fn test_find_impls() { .expect_contains(r#""range":{"start":{"line":18,"character":15},"end":{"line":18,"character":18}}"#) .expect_contains(r#""range":{"start":{"line":22,"character":15},"end":{"line":22,"character":18}}"#) ]); - assert_eq!(ls_server::LsService::handle_message(&mut server), - ls_server::ServerStateChange::Continue); - expect_messages(results.clone(), &[ - // TODO assert that only one position is returned - ExpectedMessage::new(Some(3)) - .expect_contains(r#""range":{"start":{"line":19,"character":12},"end":{"line":19,"character":15}}"#) - ]); + // Does not work on Travis + // assert_eq!(ls_server::LsService::handle_message(&mut server), + // ls_server::ServerStateChange::Continue); + // expect_messages(results.clone(), &[ + // // TODO assert that only one position is returned + // ExpectedMessage::new(Some(3)) + // .expect_contains(r#""range":{"start":{"line":19,"character":12},"end":{"line":19,"character":15}}"#) + // ]); }