aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_lsp_server/src/caps.rs6
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs202
-rw-r--r--crates/ra_lsp_server/src/req.rs4
-rw-r--r--editors/code/src/commands/index.ts2
-rw-r--r--editors/code/src/commands/run_single.ts63
-rw-r--r--editors/code/src/extension.ts3
7 files changed, 196 insertions, 85 deletions
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 @@
1use languageserver_types::{ 1use languageserver_types::{
2 CodeActionProviderCapability, CompletionOptions, DocumentOnTypeFormattingOptions, 2 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions,
3 ExecuteCommandOptions, FoldingRangeProviderCapability, RenameOptions, RenameProviderCapability, 3 ExecuteCommandOptions, FoldingRangeProviderCapability, RenameOptions, RenameProviderCapability,
4 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, 4 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
5 TextDocumentSyncOptions, 5 TextDocumentSyncOptions,
@@ -32,7 +32,9 @@ pub fn server_capabilities() -> ServerCapabilities {
32 document_symbol_provider: Some(true), 32 document_symbol_provider: Some(true),
33 workspace_symbol_provider: Some(true), 33 workspace_symbol_provider: Some(true),
34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)), 34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
35 code_lens_provider: None, 35 code_lens_provider: Some(CodeLensOptions {
36 resolve_provider: None,
37 }),
36 document_formatting_provider: Some(true), 38 document_formatting_provider: Some(true),
37 document_range_formatting_provider: None, 39 document_range_formatting_provider: None,
38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 40 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(
300 .on::<req::DecorationsRequest>(handlers::handle_decorations)? 300 .on::<req::DecorationsRequest>(handlers::handle_decorations)?
301 .on::<req::Completion>(handlers::handle_completion)? 301 .on::<req::Completion>(handlers::handle_completion)?
302 .on::<req::CodeActionRequest>(handlers::handle_code_action)? 302 .on::<req::CodeActionRequest>(handlers::handle_code_action)?
303 .on::<req::CodeLensRequest>(handlers::handle_code_lens)?
303 .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)? 304 .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)?
304 .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)? 305 .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)?
305 .on::<req::HoverRequest>(handlers::handle_hover)? 306 .on::<req::HoverRequest>(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;
2 2
3use gen_lsp_server::ErrorCode; 3use gen_lsp_server::ErrorCode;
4use languageserver_types::{ 4use languageserver_types::{
5 CodeActionResponse, Command, Diagnostic, DiagnosticSeverity, DocumentFormattingParams, 5 CodeActionResponse, Command, CodeLens, Diagnostic, DiagnosticSeverity, DocumentFormattingParams,
6 DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, 6 DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
7 FoldingRangeParams, Hover, HoverContents, Location, MarkedString, MarkupContent, MarkupKind, 7 FoldingRangeParams, Hover, HoverContents, Location, MarkedString, MarkupContent, MarkupKind,
8 ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams, 8 ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams,
@@ -291,97 +291,93 @@ pub fn handle_runnables(
291 env: FxHashMap::default(), 291 env: FxHashMap::default(),
292 }); 292 });
293 return Ok(res); 293 return Ok(res);
294}
294 295
295 fn runnable_args( 296fn runnable_args(world: &ServerWorld, file_id: FileId, kind: &RunnableKind) -> Result<Vec<String>> {
296 world: &ServerWorld, 297 let spec = CargoTargetSpec::for_file(world, file_id)?;
297 file_id: FileId, 298 let mut res = Vec::new();
298 kind: &RunnableKind, 299 match kind {
299 ) -> Result<Vec<String>> { 300 RunnableKind::Test { name } => {
300 let spec = CargoTargetSpec::for_file(world, file_id)?; 301 res.push("test".to_string());
301 let mut res = Vec::new(); 302 if let Some(spec) = spec {
302 match kind { 303 spec.push_to(&mut res);
303 RunnableKind::Test { name } => {
304 res.push("test".to_string());
305 if let Some(spec) = spec {
306 spec.push_to(&mut res);
307 }
308 res.push("--".to_string());
309 res.push(name.to_string());
310 res.push("--nocapture".to_string());
311 } 304 }
312 RunnableKind::TestMod { path } => { 305 res.push("--".to_string());
313 res.push("test".to_string()); 306 res.push(name.to_string());
314 if let Some(spec) = spec { 307 res.push("--nocapture".to_string());
315 spec.push_to(&mut res); 308 }
316 } 309 RunnableKind::TestMod { path } => {
317 res.push("--".to_string()); 310 res.push("test".to_string());
318 res.push(path.to_string()); 311 if let Some(spec) = spec {
319 res.push("--nocapture".to_string()); 312 spec.push_to(&mut res);
320 } 313 }
321 RunnableKind::Bin => { 314 res.push("--".to_string());
322 res.push("run".to_string()); 315 res.push(path.to_string());
323 if let Some(spec) = spec { 316 res.push("--nocapture".to_string());
324 spec.push_to(&mut res); 317 }
325 } 318 RunnableKind::Bin => {
319 res.push("run".to_string());
320 if let Some(spec) = spec {
321 spec.push_to(&mut res);
326 } 322 }
327 } 323 }
328 Ok(res)
329 } 324 }
325 Ok(res)
326}
330 327
331 struct CargoTargetSpec { 328struct CargoTargetSpec {
332 package: String, 329 package: String,
333 target: String, 330 target: String,
334 target_kind: TargetKind, 331 target_kind: TargetKind,
335 } 332}
336 333
337 impl CargoTargetSpec { 334impl CargoTargetSpec {
338 fn for_file(world: &ServerWorld, file_id: FileId) -> Result<Option<CargoTargetSpec>> { 335 fn for_file(world: &ServerWorld, file_id: FileId) -> Result<Option<CargoTargetSpec>> {
339 let &crate_id = match world.analysis().crate_for(file_id)?.first() { 336 let &crate_id = match world.analysis().crate_for(file_id)?.first() {
340 Some(crate_id) => crate_id, 337 Some(crate_id) => crate_id,
341 None => return Ok(None), 338 None => return Ok(None),
339 };
340 let file_id = world.analysis().crate_root(crate_id)?;
341 let path = world
342 .vfs
343 .read()
344 .file2path(ra_vfs::VfsFile(file_id.0.into()));
345 let res = world.workspaces.iter().find_map(|ws| {
346 let tgt = ws.cargo.target_by_root(&path)?;
347 let res = CargoTargetSpec {
348 package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(),
349 target: tgt.name(&ws.cargo).to_string(),
350 target_kind: tgt.kind(&ws.cargo),
342 }; 351 };
343 let file_id = world.analysis().crate_root(crate_id)?; 352 Some(res)
344 let path = world 353 });
345 .vfs 354 Ok(res)
346 .read() 355 }
347 .file2path(ra_vfs::VfsFile(file_id.0.into()));
348 let res = world.workspaces.iter().find_map(|ws| {
349 let tgt = ws.cargo.target_by_root(&path)?;
350 let res = CargoTargetSpec {
351 package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(),
352 target: tgt.name(&ws.cargo).to_string(),
353 target_kind: tgt.kind(&ws.cargo),
354 };
355 Some(res)
356 });
357 Ok(res)
358 }
359 356
360 fn push_to(self, buf: &mut Vec<String>) { 357 fn push_to(self, buf: &mut Vec<String>) {
361 buf.push("--package".to_string()); 358 buf.push("--package".to_string());
362 buf.push(self.package); 359 buf.push(self.package);
363 match self.target_kind { 360 match self.target_kind {
364 TargetKind::Bin => { 361 TargetKind::Bin => {
365 buf.push("--bin".to_string()); 362 buf.push("--bin".to_string());
366 buf.push(self.target); 363 buf.push(self.target);
367 } 364 }
368 TargetKind::Test => { 365 TargetKind::Test => {
369 buf.push("--test".to_string()); 366 buf.push("--test".to_string());
370 buf.push(self.target); 367 buf.push(self.target);
371 } 368 }
372 TargetKind::Bench => { 369 TargetKind::Bench => {
373 buf.push("--bench".to_string()); 370 buf.push("--bench".to_string());
374 buf.push(self.target); 371 buf.push(self.target);
375 } 372 }
376 TargetKind::Example => { 373 TargetKind::Example => {
377 buf.push("--example".to_string()); 374 buf.push("--example".to_string());
378 buf.push(self.target); 375 buf.push(self.target);
379 } 376 }
380 TargetKind::Lib => { 377 TargetKind::Lib => {
381 buf.push("--lib".to_string()); 378 buf.push("--lib".to_string());
382 }
383 TargetKind::Other => (),
384 } 379 }
380 TargetKind::Other => (),
385 } 381 }
386 } 382 }
387} 383}
@@ -666,6 +662,50 @@ pub fn handle_code_action(
666 Ok(Some(CodeActionResponse::Commands(res))) 662 Ok(Some(CodeActionResponse::Commands(res)))
667} 663}
668 664
665pub fn handle_code_lens(
666 world: ServerWorld,
667 params: req::CodeLensParams,
668) -> Result<Option<Vec<CodeLens>>> {
669 let file_id = params.text_document.try_conv_with(&world)?;
670 let line_index = world.analysis().file_line_index(file_id);
671
672 let mut lenses: Vec<CodeLens> = Default::default();
673
674 for runnable in world.analysis().runnables(file_id)? {
675 match &runnable.kind {
676 RunnableKind::Test { name: _ } | RunnableKind::TestMod { path: _ } => {
677 let args = runnable_args(&world, file_id, &runnable.kind)?;
678
679 let range = runnable.range.conv_with(&line_index);
680
681 // This represents the actual command that will be run.
682 let r: req::Runnable = req::Runnable {
683 range,
684 label: Default::default(),
685 bin: "cargo".into(),
686 args,
687 env: Default::default(),
688 };
689
690 let lens = CodeLens {
691 range,
692 command: Some(Command {
693 title: "Run Test".into(),
694 command: "ra-lsp.run-single".into(),
695 arguments: Some(vec![to_value(r).unwrap()]),
696 }),
697 data: None,
698 };
699
700 lenses.push(lens);
701 }
702 _ => continue,
703 };
704 }
705
706 return Ok(Some(lenses));
707}
708
669pub fn handle_document_highlight( 709pub fn handle_document_highlight(
670 world: ServerWorld, 710 world: ServerWorld,
671 params: req::TextDocumentPositionParams, 711 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};
4use url_serde; 4use url_serde;
5 5
6pub use languageserver_types::{ 6pub use languageserver_types::{
7 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CompletionParams, 7 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, CodeLensParams,
8 CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, 8 CompletionParams, CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams,
9 DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, 9 DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult,
10 PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit, 10 PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit,
11 TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, 11 TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams,
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 33e2b34a2..c8bb55591 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -4,6 +4,7 @@ import * as joinLines from './join_lines';
4import * as matchingBrace from './matching_brace'; 4import * as matchingBrace from './matching_brace';
5import * as onEnter from './on_enter'; 5import * as onEnter from './on_enter';
6import * as parentModule from './parent_module'; 6import * as parentModule from './parent_module';
7import * as runSingle from './run_single';
7import * as runnables from './runnables'; 8import * as runnables from './runnables';
8import * as syntaxTree from './syntaxTree'; 9import * as syntaxTree from './syntaxTree';
9 10
@@ -13,6 +14,7 @@ export {
13 joinLines, 14 joinLines,
14 matchingBrace, 15 matchingBrace,
15 parentModule, 16 parentModule,
17 runSingle,
16 runnables, 18 runnables,
17 syntaxTree, 19 syntaxTree,
18 onEnter 20 onEnter
diff --git a/editors/code/src/commands/run_single.ts b/editors/code/src/commands/run_single.ts
new file mode 100644
index 000000000..855bcdb06
--- /dev/null
+++ b/editors/code/src/commands/run_single.ts
@@ -0,0 +1,63 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4interface Runnable {
5 range: lc.Range;
6 label: string;
7 bin: string;
8 args: string[];
9 env: { [index: string]: string };
10}
11
12interface CargoTaskDefinition extends vscode.TaskDefinition {
13 type: 'cargo';
14 label: string;
15 command: string;
16 args: string[];
17 env?: { [key: string]: string };
18}
19
20function createTask(spec: Runnable): vscode.Task {
21 const TASK_SOURCE = 'Rust';
22 const definition: CargoTaskDefinition = {
23 type: 'cargo',
24 label: 'cargo',
25 command: spec.bin,
26 args: spec.args,
27 env: spec.env
28 };
29
30 const execOption: vscode.ShellExecutionOptions = {
31 cwd: '.',
32 env: definition.env
33 };
34 const exec = new vscode.ShellExecution(definition.command, definition.args, execOption);
35
36 const f = vscode.workspace.workspaceFolders![0];
37 const t = new vscode.Task(
38 definition,
39 f,
40 definition.label,
41 TASK_SOURCE,
42 exec,
43 ['$rustc']
44 );
45 t.presentationOptions.clear = true
46 return t;
47}
48
49export async function handle(runnable: Runnable) {
50 const editor = vscode.window.activeTextEditor;
51 if (editor == null || editor.document.languageId !== 'rust') {
52 return;
53 }
54
55 const task = createTask(runnable);
56 task.group = vscode.TaskGroup.Build;
57 task.presentationOptions = {
58 reveal: vscode.TaskRevealKind.Always,
59 panel: vscode.TaskPanelKind.Dedicated,
60 };
61
62 return vscode.tasks.executeTask(task);
63} \ No newline at end of file
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
index 4acd54d90..acbb1f734 100644
--- a/editors/code/src/extension.ts
+++ b/editors/code/src/extension.ts
@@ -55,6 +55,9 @@ export function activate(context: vscode.ExtensionContext) {
55 ); 55 );
56 overrideCommand('type', commands.onEnter.handle); 56 overrideCommand('type', commands.onEnter.handle);
57 57
58 // Unlike the above this does not send requests to the language server
59 registerCommand('ra-lsp.run-single', commands.runSingle.handle);
60
58 // Notifications are events triggered by the language server 61 // Notifications are events triggered by the language server
59 const allNotifications: Iterable< 62 const allNotifications: Iterable<
60 [string, lc.GenericNotificationHandler] 63 [string, lc.GenericNotificationHandler]