From faf003763516074c619cee7e43ca8bc365540c92 Mon Sep 17 00:00:00 2001 From: "Jeremy A. Kolb" Date: Fri, 11 Jan 2019 15:16:55 -0500 Subject: Code lens support for running tests --- crates/ra_lsp_server/src/caps.rs | 6 +- crates/ra_lsp_server/src/main_loop.rs | 1 + crates/ra_lsp_server/src/main_loop/handlers.rs | 202 +++++++++++++++---------- crates/ra_lsp_server/src/req.rs | 4 +- 4 files changed, 128 insertions(+), 85 deletions(-) (limited to 'crates') diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index 2599a4ca6..be6a6ead6 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs @@ -1,5 +1,5 @@ use languageserver_types::{ - CodeActionProviderCapability, CompletionOptions, DocumentOnTypeFormattingOptions, + CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, ExecuteCommandOptions, FoldingRangeProviderCapability, RenameOptions, RenameProviderCapability, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, @@ -32,7 +32,9 @@ pub fn server_capabilities() -> ServerCapabilities { document_symbol_provider: Some(true), workspace_symbol_provider: Some(true), code_action_provider: Some(CodeActionProviderCapability::Simple(true)), - code_lens_provider: None, + code_lens_provider: Some(CodeLensOptions { + resolve_provider: None, + }), document_formatting_provider: Some(true), document_range_formatting_provider: None, document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index c43637351..726c758aa 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -300,6 +300,7 @@ fn on_request( .on::(handlers::handle_decorations)? .on::(handlers::handle_completion)? .on::(handlers::handle_code_action)? + .on::(handlers::handle_code_lens)? .on::(handlers::handle_folding_range)? .on::(handlers::handle_signature_help)? .on::(handlers::handle_hover)? diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index aad9d6568..f881bd703 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use gen_lsp_server::ErrorCode; use languageserver_types::{ - CodeActionResponse, Command, Diagnostic, DiagnosticSeverity, DocumentFormattingParams, + CodeActionResponse, Command, CodeLens, Diagnostic, DiagnosticSeverity, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkedString, MarkupContent, MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams, @@ -291,97 +291,93 @@ pub fn handle_runnables( env: FxHashMap::default(), }); return Ok(res); +} - fn runnable_args( - world: &ServerWorld, - file_id: FileId, - kind: &RunnableKind, - ) -> Result> { - let spec = CargoTargetSpec::for_file(world, file_id)?; - let mut res = Vec::new(); - match kind { - RunnableKind::Test { name } => { - res.push("test".to_string()); - if let Some(spec) = spec { - spec.push_to(&mut res); - } - res.push("--".to_string()); - res.push(name.to_string()); - res.push("--nocapture".to_string()); +fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Result> { + let spec = CargoTargetSpec::for_file(world, file_id)?; + let mut res = Vec::new(); + match kind { + RunnableKind::Test { name } => { + res.push("test".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut res); } - RunnableKind::TestMod { path } => { - res.push("test".to_string()); - if let Some(spec) = spec { - spec.push_to(&mut res); - } - res.push("--".to_string()); - res.push(path.to_string()); - res.push("--nocapture".to_string()); + res.push("--".to_string()); + res.push(name.to_string()); + res.push("--nocapture".to_string()); + } + RunnableKind::TestMod { path } => { + res.push("test".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut res); } - RunnableKind::Bin => { - res.push("run".to_string()); - if let Some(spec) = spec { - spec.push_to(&mut res); - } + res.push("--".to_string()); + res.push(path.to_string()); + res.push("--nocapture".to_string()); + } + RunnableKind::Bin => { + res.push("run".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut res); } } - Ok(res) } + Ok(res) +} - struct CargoTargetSpec { - package: String, - target: String, - target_kind: TargetKind, - } +struct CargoTargetSpec { + package: String, + target: String, + target_kind: TargetKind, +} - impl CargoTargetSpec { - fn for_file(world: &ServerWorld, file_id: FileId) -> Result> { - let &crate_id = match world.analysis().crate_for(file_id)?.first() { - Some(crate_id) => crate_id, - None => return Ok(None), +impl CargoTargetSpec { + fn for_file(world: &ServerWorld, file_id: FileId) -> Result> { + let &crate_id = match world.analysis().crate_for(file_id)?.first() { + Some(crate_id) => crate_id, + None => return Ok(None), + }; + let file_id = world.analysis().crate_root(crate_id)?; + let path = world + .vfs + .read() + .file2path(ra_vfs::VfsFile(file_id.0.into())); + let res = world.workspaces.iter().find_map(|ws| { + let tgt = ws.cargo.target_by_root(&path)?; + let res = CargoTargetSpec { + package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(), + target: tgt.name(&ws.cargo).to_string(), + target_kind: tgt.kind(&ws.cargo), }; - let file_id = world.analysis().crate_root(crate_id)?; - let path = world - .vfs - .read() - .file2path(ra_vfs::VfsFile(file_id.0.into())); - let res = world.workspaces.iter().find_map(|ws| { - let tgt = ws.cargo.target_by_root(&path)?; - let res = CargoTargetSpec { - package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(), - target: tgt.name(&ws.cargo).to_string(), - target_kind: tgt.kind(&ws.cargo), - }; - Some(res) - }); - Ok(res) - } + Some(res) + }); + Ok(res) + } - fn push_to(self, buf: &mut Vec) { - buf.push("--package".to_string()); - buf.push(self.package); - match self.target_kind { - TargetKind::Bin => { - buf.push("--bin".to_string()); - buf.push(self.target); - } - TargetKind::Test => { - buf.push("--test".to_string()); - buf.push(self.target); - } - TargetKind::Bench => { - buf.push("--bench".to_string()); - buf.push(self.target); - } - TargetKind::Example => { - buf.push("--example".to_string()); - buf.push(self.target); - } - TargetKind::Lib => { - buf.push("--lib".to_string()); - } - TargetKind::Other => (), + fn push_to(self, buf: &mut Vec) { + buf.push("--package".to_string()); + buf.push(self.package); + match self.target_kind { + TargetKind::Bin => { + buf.push("--bin".to_string()); + buf.push(self.target); + } + TargetKind::Test => { + buf.push("--test".to_string()); + buf.push(self.target); + } + TargetKind::Bench => { + buf.push("--bench".to_string()); + buf.push(self.target); + } + TargetKind::Example => { + buf.push("--example".to_string()); + buf.push(self.target); + } + TargetKind::Lib => { + buf.push("--lib".to_string()); } + TargetKind::Other => (), } } } @@ -666,6 +662,50 @@ pub fn handle_code_action( Ok(Some(CodeActionResponse::Commands(res))) } +pub fn handle_code_lens( + world: ServerWorld, + params: req::CodeLensParams, +) -> Result>> { + let file_id = params.text_document.try_conv_with(&world)?; + let line_index = world.analysis().file_line_index(file_id); + + let mut lenses: Vec = Default::default(); + + for runnable in world.analysis().runnables(file_id)? { + match &runnable.kind { + RunnableKind::Test { name: _ } | RunnableKind::TestMod { path: _ } => { + let args = runnable_args(&world, file_id, &runnable.kind)?; + + let range = runnable.range.conv_with(&line_index); + + // This represents the actual command that will be run. + let r: req::Runnable = req::Runnable { + range, + label: Default::default(), + bin: "cargo".into(), + args, + env: Default::default(), + }; + + let lens = CodeLens { + range, + command: Some(Command { + title: "Run Test".into(), + command: "ra-lsp.run-single".into(), + arguments: Some(vec![to_value(r).unwrap()]), + }), + data: None, + }; + + lenses.push(lens); + } + _ => continue, + }; + } + + return Ok(Some(lenses)); +} + pub fn handle_document_highlight( world: ServerWorld, params: req::TextDocumentPositionParams, diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index b41e90328..c2b16725b 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize}; use url_serde; pub use languageserver_types::{ - notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CompletionParams, - CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, + notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, CodeLensParams, + CompletionParams, CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, -- cgit v1.2.3 From 72d48b08fb88624c1e91341b0cd47a9bd0c1d5ff Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Sat, 12 Jan 2019 13:00:58 -0500 Subject: Move `CargoTargetSpec` and friends to cargo_target_spec module --- crates/ra_lsp_server/src/cargo_target_spec.rs | 100 +++++++++++++++++++++++++ crates/ra_lsp_server/src/lib.rs | 1 + crates/ra_lsp_server/src/main_loop/handlers.rs | 91 +--------------------- 3 files changed, 102 insertions(+), 90 deletions(-) create mode 100644 crates/ra_lsp_server/src/cargo_target_spec.rs (limited to 'crates') diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs new file mode 100644 index 000000000..a66f14b82 --- /dev/null +++ b/crates/ra_lsp_server/src/cargo_target_spec.rs @@ -0,0 +1,100 @@ +use crate::{ + project_model::TargetKind, + server_world::ServerWorld, + Result +}; + +use ra_ide_api::{FileId, RunnableKind}; + +pub(crate) fn runnable_args( + world: &ServerWorld, + file_id: FileId, + kind: &RunnableKind, +) -> Result> { + let spec = CargoTargetSpec::for_file(world, file_id)?; + let mut res = Vec::new(); + match kind { + RunnableKind::Test { name } => { + res.push("test".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut res); + } + res.push("--".to_string()); + res.push(name.to_string()); + res.push("--nocapture".to_string()); + } + RunnableKind::TestMod { path } => { + res.push("test".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut res); + } + res.push("--".to_string()); + res.push(path.to_string()); + res.push("--nocapture".to_string()); + } + RunnableKind::Bin => { + res.push("run".to_string()); + if let Some(spec) = spec { + spec.push_to(&mut res); + } + } + } + Ok(res) +} + +pub struct CargoTargetSpec { + pub package: String, + pub target: String, + pub target_kind: TargetKind, +} + +impl CargoTargetSpec { + pub fn for_file(world: &ServerWorld, file_id: FileId) -> Result> { + let &crate_id = match world.analysis().crate_for(file_id)?.first() { + Some(crate_id) => crate_id, + None => return Ok(None), + }; + let file_id = world.analysis().crate_root(crate_id)?; + let path = world + .vfs + .read() + .file2path(ra_vfs::VfsFile(file_id.0.into())); + let res = world.workspaces.iter().find_map(|ws| { + let tgt = ws.cargo.target_by_root(&path)?; + let res = CargoTargetSpec { + package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(), + target: tgt.name(&ws.cargo).to_string(), + target_kind: tgt.kind(&ws.cargo), + }; + Some(res) + }); + Ok(res) + } + + pub fn push_to(self, buf: &mut Vec) { + buf.push("--package".to_string()); + buf.push(self.package); + match self.target_kind { + TargetKind::Bin => { + buf.push("--bin".to_string()); + buf.push(self.target); + } + TargetKind::Test => { + buf.push("--test".to_string()); + buf.push(self.target); + } + TargetKind::Bench => { + buf.push("--bench".to_string()); + buf.push(self.target); + } + TargetKind::Example => { + buf.push("--example".to_string()); + buf.push(self.target); + } + TargetKind::Lib => { + buf.push("--lib".to_string()); + } + TargetKind::Other => (), + } + } +} diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs index 725b1258a..f93d4b37d 100644 --- a/crates/ra_lsp_server/src/lib.rs +++ b/crates/ra_lsp_server/src/lib.rs @@ -1,4 +1,5 @@ mod caps; +mod cargo_target_spec; mod conv; mod main_loop; mod project_model; diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index f881bd703..7f6146b6c 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -17,8 +17,8 @@ use serde_json::to_value; use std::io::Write; use crate::{ + cargo_target_spec::{CargoTargetSpec, runnable_args}, conv::{to_location, to_location_link, Conv, ConvWith, MapConvWith, TryConvWith}, - project_model::TargetKind, req::{self, Decoration}, server_world::ServerWorld, LspError, Result, @@ -293,95 +293,6 @@ pub fn handle_runnables( return Ok(res); } -fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Result> { - let spec = CargoTargetSpec::for_file(world, file_id)?; - let mut res = Vec::new(); - match kind { - RunnableKind::Test { name } => { - res.push("test".to_string()); - if let Some(spec) = spec { - spec.push_to(&mut res); - } - res.push("--".to_string()); - res.push(name.to_string()); - res.push("--nocapture".to_string()); - } - RunnableKind::TestMod { path } => { - res.push("test".to_string()); - if let Some(spec) = spec { - spec.push_to(&mut res); - } - res.push("--".to_string()); - res.push(path.to_string()); - res.push("--nocapture".to_string()); - } - RunnableKind::Bin => { - res.push("run".to_string()); - if let Some(spec) = spec { - spec.push_to(&mut res); - } - } - } - Ok(res) -} - -struct CargoTargetSpec { - package: String, - target: String, - target_kind: TargetKind, -} - -impl CargoTargetSpec { - fn for_file(world: &ServerWorld, file_id: FileId) -> Result> { - let &crate_id = match world.analysis().crate_for(file_id)?.first() { - Some(crate_id) => crate_id, - None => return Ok(None), - }; - let file_id = world.analysis().crate_root(crate_id)?; - let path = world - .vfs - .read() - .file2path(ra_vfs::VfsFile(file_id.0.into())); - let res = world.workspaces.iter().find_map(|ws| { - let tgt = ws.cargo.target_by_root(&path)?; - let res = CargoTargetSpec { - package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(), - target: tgt.name(&ws.cargo).to_string(), - target_kind: tgt.kind(&ws.cargo), - }; - Some(res) - }); - Ok(res) - } - - fn push_to(self, buf: &mut Vec) { - buf.push("--package".to_string()); - buf.push(self.package); - match self.target_kind { - TargetKind::Bin => { - buf.push("--bin".to_string()); - buf.push(self.target); - } - TargetKind::Test => { - buf.push("--test".to_string()); - buf.push(self.target); - } - TargetKind::Bench => { - buf.push("--bench".to_string()); - buf.push(self.target); - } - TargetKind::Example => { - buf.push("--example".to_string()); - buf.push(self.target); - } - TargetKind::Lib => { - buf.push("--lib".to_string()); - } - TargetKind::Other => (), - } - } -} - pub fn handle_decorations( world: ServerWorld, params: TextDocumentIdentifier, -- cgit v1.2.3