diff options
Diffstat (limited to 'crates')
9 files changed, 150 insertions, 47 deletions
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 12d5716e8..34c2d75fe 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -77,7 +77,7 @@ pub use crate::{ | |||
77 | }; | 77 | }; |
78 | 78 | ||
79 | pub use hir::Documentation; | 79 | pub use hir::Documentation; |
80 | pub use ra_assists::{AssistConfig, AssistId}; | 80 | pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; |
81 | pub use ra_db::{ | 81 | pub use ra_db::{ |
82 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, | 82 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, |
83 | }; | 83 | }; |
@@ -142,14 +142,6 @@ pub struct AnalysisHost { | |||
142 | db: RootDatabase, | 142 | db: RootDatabase, |
143 | } | 143 | } |
144 | 144 | ||
145 | #[derive(Debug)] | ||
146 | pub struct Assist { | ||
147 | pub id: AssistId, | ||
148 | pub label: String, | ||
149 | pub group_label: Option<String>, | ||
150 | pub source_change: SourceChange, | ||
151 | } | ||
152 | |||
153 | impl AnalysisHost { | 145 | impl AnalysisHost { |
154 | pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { | 146 | pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { |
155 | AnalysisHost { db: RootDatabase::new(lru_capacity) } | 147 | AnalysisHost { db: RootDatabase::new(lru_capacity) } |
@@ -470,20 +462,23 @@ impl Analysis { | |||
470 | self.with_db(|db| completion::completions(db, config, position).map(Into::into)) | 462 | self.with_db(|db| completion::completions(db, config, position).map(Into::into)) |
471 | } | 463 | } |
472 | 464 | ||
473 | /// Computes assists (aka code actions aka intentions) for the given | 465 | /// Computes resolved assists with source changes for the given position. |
466 | pub fn resolved_assists( | ||
467 | &self, | ||
468 | config: &AssistConfig, | ||
469 | frange: FileRange, | ||
470 | ) -> Cancelable<Vec<ResolvedAssist>> { | ||
471 | self.with_db(|db| ra_assists::Assist::resolved(db, config, frange)) | ||
472 | } | ||
473 | |||
474 | /// Computes unresolved assists (aka code actions aka intentions) for the given | ||
474 | /// position. | 475 | /// position. |
475 | pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { | 476 | pub fn unresolved_assists( |
476 | self.with_db(|db| { | 477 | &self, |
477 | ra_assists::Assist::resolved(db, config, frange) | 478 | config: &AssistConfig, |
478 | .into_iter() | 479 | frange: FileRange, |
479 | .map(|assist| Assist { | 480 | ) -> Cancelable<Vec<Assist>> { |
480 | id: assist.assist.id, | 481 | self.with_db(|db| Assist::unresolved(db, config, frange)) |
481 | label: assist.assist.label, | ||
482 | group_label: assist.assist.group.map(|it| it.0), | ||
483 | source_change: assist.source_change, | ||
484 | }) | ||
485 | .collect() | ||
486 | }) | ||
487 | } | 482 | } |
488 | 483 | ||
489 | /// Computes the set of diagnostics for the given file. | 484 | /// Computes the set of diagnostics for the given file. |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c0f7c2c0c..9e8e7ab82 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -103,6 +103,7 @@ pub struct ClientCapsConfig { | |||
103 | pub code_action_literals: bool, | 103 | pub code_action_literals: bool, |
104 | pub work_done_progress: bool, | 104 | pub work_done_progress: bool, |
105 | pub code_action_group: bool, | 105 | pub code_action_group: bool, |
106 | pub resolve_code_action: bool, | ||
106 | } | 107 | } |
107 | 108 | ||
108 | impl Default for Config { | 109 | impl Default for Config { |
@@ -299,7 +300,11 @@ impl Config { | |||
299 | 300 | ||
300 | let code_action_group = | 301 | let code_action_group = |
301 | experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); | 302 | experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); |
302 | self.client_caps.code_action_group = code_action_group | 303 | self.client_caps.code_action_group = code_action_group; |
304 | |||
305 | let resolve_code_action = | ||
306 | experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true); | ||
307 | self.client_caps.resolve_code_action = resolve_code_action; | ||
303 | } | 308 | } |
304 | } | 309 | } |
305 | } | 310 | } |
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap index c40cfdcdc..272057b47 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap | |||
@@ -65,6 +65,7 @@ expression: diag | |||
65 | fixes: [ | 65 | fixes: [ |
66 | CodeAction { | 66 | CodeAction { |
67 | title: "return the expression directly", | 67 | title: "return the expression directly", |
68 | id: None, | ||
68 | group: None, | 69 | group: None, |
69 | kind: Some( | 70 | kind: Some( |
70 | "quickfix", | 71 | "quickfix", |
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap index 6dd3fcb2e..9a7972ff5 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap | |||
@@ -50,6 +50,7 @@ expression: diag | |||
50 | fixes: [ | 50 | fixes: [ |
51 | CodeAction { | 51 | CodeAction { |
52 | title: "consider prefixing with an underscore", | 52 | title: "consider prefixing with an underscore", |
53 | id: None, | ||
53 | group: None, | 54 | group: None, |
54 | kind: Some( | 55 | kind: Some( |
55 | "quickfix", | 56 | "quickfix", |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index a500d670a..257910e09 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -145,6 +145,7 @@ fn map_rust_child_diagnostic( | |||
145 | } else { | 145 | } else { |
146 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { | 146 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { |
147 | title: rd.message.clone(), | 147 | title: rd.message.clone(), |
148 | id: None, | ||
148 | group: None, | 149 | group: None, |
149 | kind: Some("quickfix".to_string()), | 150 | kind: Some("quickfix".to_string()), |
150 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | 151 | edit: Some(lsp_ext::SnippetWorkspaceEdit { |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 173c23b9e..05b76e7c8 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -98,6 +98,23 @@ pub struct JoinLinesParams { | |||
98 | pub ranges: Vec<Range>, | 98 | pub ranges: Vec<Range>, |
99 | } | 99 | } |
100 | 100 | ||
101 | pub enum ResolveCodeActionRequest {} | ||
102 | |||
103 | impl Request for ResolveCodeActionRequest { | ||
104 | type Params = ResolveCodeActionParams; | ||
105 | type Result = Option<SnippetWorkspaceEdit>; | ||
106 | const METHOD: &'static str = "experimental/resolveCodeAction"; | ||
107 | } | ||
108 | |||
109 | /// Params for the ResolveCodeActionRequest | ||
110 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | ||
111 | #[serde(rename_all = "camelCase")] | ||
112 | pub struct ResolveCodeActionParams { | ||
113 | pub code_action_params: lsp_types::CodeActionParams, | ||
114 | pub id: String, | ||
115 | pub label: String, | ||
116 | } | ||
117 | |||
101 | pub enum OnEnter {} | 118 | pub enum OnEnter {} |
102 | 119 | ||
103 | impl Request for OnEnter { | 120 | impl Request for OnEnter { |
@@ -197,6 +214,8 @@ impl Request for CodeActionRequest { | |||
197 | pub struct CodeAction { | 214 | pub struct CodeAction { |
198 | pub title: String, | 215 | pub title: String, |
199 | #[serde(skip_serializing_if = "Option::is_none")] | 216 | #[serde(skip_serializing_if = "Option::is_none")] |
217 | pub id: Option<String>, | ||
218 | #[serde(skip_serializing_if = "Option::is_none")] | ||
200 | pub group: Option<String>, | 219 | pub group: Option<String>, |
201 | #[serde(skip_serializing_if = "Option::is_none")] | 220 | #[serde(skip_serializing_if = "Option::is_none")] |
202 | pub kind: Option<String>, | 221 | pub kind: Option<String>, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f1287d52c..ad9dd4c59 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -517,6 +517,7 @@ fn on_request( | |||
517 | .on::<lsp_ext::Runnables>(handlers::handle_runnables)? | 517 | .on::<lsp_ext::Runnables>(handlers::handle_runnables)? |
518 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? | 518 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? |
519 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? | 519 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? |
520 | .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)? | ||
520 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 521 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
521 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 522 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
522 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 523 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index bc7c7f1ef..b342f4bb7 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -693,40 +693,35 @@ pub fn handle_formatting( | |||
693 | }])) | 693 | }])) |
694 | } | 694 | } |
695 | 695 | ||
696 | pub fn handle_code_action( | 696 | fn handle_fixes( |
697 | world: WorldSnapshot, | 697 | world: &WorldSnapshot, |
698 | params: lsp_types::CodeActionParams, | 698 | params: &lsp_types::CodeActionParams, |
699 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | 699 | res: &mut Vec<lsp_ext::CodeAction>, |
700 | let _p = profile("handle_code_action"); | 700 | ) -> Result<()> { |
701 | // We intentionally don't support command-based actions, as those either | ||
702 | // requires custom client-code anyway, or requires server-initiated edits. | ||
703 | // Server initiated edits break causality, so we avoid those as well. | ||
704 | if !world.config.client_caps.code_action_literals { | ||
705 | return Ok(None); | ||
706 | } | ||
707 | |||
708 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 701 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
709 | let line_index = world.analysis().file_line_index(file_id)?; | 702 | let line_index = world.analysis().file_line_index(file_id)?; |
710 | let range = from_proto::text_range(&line_index, params.range); | 703 | let range = from_proto::text_range(&line_index, params.range); |
711 | let frange = FileRange { file_id, range }; | ||
712 | 704 | ||
713 | let diagnostics = world.analysis().diagnostics(file_id)?; | 705 | let diagnostics = world.analysis().diagnostics(file_id)?; |
714 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | ||
715 | 706 | ||
716 | let fixes_from_diagnostics = diagnostics | 707 | let fixes_from_diagnostics = diagnostics |
717 | .into_iter() | 708 | .into_iter() |
718 | .filter_map(|d| Some((d.range, d.fix?))) | 709 | .filter_map(|d| Some((d.range, d.fix?))) |
719 | .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) | 710 | .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) |
720 | .map(|(_range, fix)| fix); | 711 | .map(|(_range, fix)| fix); |
721 | |||
722 | for fix in fixes_from_diagnostics { | 712 | for fix in fixes_from_diagnostics { |
723 | let title = fix.label; | 713 | let title = fix.label; |
724 | let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; | 714 | let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; |
725 | let action = | 715 | let action = lsp_ext::CodeAction { |
726 | lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; | 716 | title, |
717 | id: None, | ||
718 | group: None, | ||
719 | kind: None, | ||
720 | edit: Some(edit), | ||
721 | command: None, | ||
722 | }; | ||
727 | res.push(action); | 723 | res.push(action); |
728 | } | 724 | } |
729 | |||
730 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { | 725 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { |
731 | let fix_range = from_proto::text_range(&line_index, fix.range); | 726 | let fix_range = from_proto::text_range(&line_index, fix.range); |
732 | if fix_range.intersect(range).is_none() { | 727 | if fix_range.intersect(range).is_none() { |
@@ -734,13 +729,67 @@ pub fn handle_code_action( | |||
734 | } | 729 | } |
735 | res.push(fix.action.clone()); | 730 | res.push(fix.action.clone()); |
736 | } | 731 | } |
732 | Ok(()) | ||
733 | } | ||
734 | |||
735 | pub fn handle_code_action( | ||
736 | world: WorldSnapshot, | ||
737 | params: lsp_types::CodeActionParams, | ||
738 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | ||
739 | let _p = profile("handle_code_action"); | ||
740 | // We intentionally don't support command-based actions, as those either | ||
741 | // requires custom client-code anyway, or requires server-initiated edits. | ||
742 | // Server initiated edits break causality, so we avoid those as well. | ||
743 | if !world.config.client_caps.code_action_literals { | ||
744 | return Ok(None); | ||
745 | } | ||
746 | |||
747 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | ||
748 | let line_index = world.analysis().file_line_index(file_id)?; | ||
749 | let range = from_proto::text_range(&line_index, params.range); | ||
750 | let frange = FileRange { file_id, range }; | ||
751 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | ||
752 | |||
753 | handle_fixes(&world, ¶ms, &mut res)?; | ||
737 | 754 | ||
738 | for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { | 755 | if world.config.client_caps.resolve_code_action { |
739 | res.push(to_proto::code_action(&world, assist)?.into()); | 756 | for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter() |
757 | { | ||
758 | res.push(to_proto::unresolved_code_action(&world, assist)?.into()); | ||
759 | } | ||
760 | } else { | ||
761 | for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { | ||
762 | res.push(to_proto::resolved_code_action(&world, assist)?.into()); | ||
763 | } | ||
740 | } | 764 | } |
765 | |||
741 | Ok(Some(res)) | 766 | Ok(Some(res)) |
742 | } | 767 | } |
743 | 768 | ||
769 | pub fn handle_resolve_code_action( | ||
770 | world: WorldSnapshot, | ||
771 | params: lsp_ext::ResolveCodeActionParams, | ||
772 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { | ||
773 | if !world.config.client_caps.resolve_code_action { | ||
774 | return Ok(None); | ||
775 | } | ||
776 | |||
777 | let _p = profile("handle_resolve_code_action"); | ||
778 | let file_id = from_proto::file_id(&world, ¶ms.code_action_params.text_document.uri)?; | ||
779 | let line_index = world.analysis().file_line_index(file_id)?; | ||
780 | let range = from_proto::text_range(&line_index, params.code_action_params.range); | ||
781 | let frange = FileRange { file_id, range }; | ||
782 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | ||
783 | |||
784 | for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { | ||
785 | res.push(to_proto::resolved_code_action(&world, assist)?.into()); | ||
786 | } | ||
787 | Ok(res | ||
788 | .into_iter() | ||
789 | .find(|action| action.id.clone().unwrap() == params.id && action.title == params.label) | ||
790 | .and_then(|action| action.edit)) | ||
791 | } | ||
792 | |||
744 | pub fn handle_code_lens( | 793 | pub fn handle_code_lens( |
745 | world: WorldSnapshot, | 794 | world: WorldSnapshot, |
746 | params: lsp_types::CodeLensParams, | 795 | params: lsp_types::CodeLensParams, |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2fbbb4e63..db78c4b5c 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -3,8 +3,8 @@ use ra_db::{FileId, FileRange}; | |||
3 | use ra_ide::{ | 3 | use ra_ide::{ |
4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, | 4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, |
5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, | 5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, |
6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, | 6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, |
7 | SourceChange, SourceFileEdit, TextEdit, | 7 | ResolvedAssist, Severity, SourceChange, SourceFileEdit, TextEdit, |
8 | }; | 8 | }; |
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | 9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; |
10 | use ra_vfs::LineEndings; | 10 | use ra_vfs::LineEndings; |
@@ -617,10 +617,41 @@ fn main() <fold>{ | |||
617 | } | 617 | } |
618 | } | 618 | } |
619 | 619 | ||
620 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { | 620 | pub(crate) fn unresolved_code_action( |
621 | world: &WorldSnapshot, | ||
622 | assist: Assist, | ||
623 | ) -> Result<lsp_ext::CodeAction> { | ||
621 | let res = lsp_ext::CodeAction { | 624 | let res = lsp_ext::CodeAction { |
622 | title: assist.label, | 625 | title: assist.label, |
623 | group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, | 626 | id: Some(assist.id.0.to_owned()), |
627 | group: assist.group.and_then(|it| { | ||
628 | if world.config.client_caps.code_action_group { | ||
629 | None | ||
630 | } else { | ||
631 | Some(it.0) | ||
632 | } | ||
633 | }), | ||
634 | kind: Some(String::new()), | ||
635 | edit: None, | ||
636 | command: None, | ||
637 | }; | ||
638 | Ok(res) | ||
639 | } | ||
640 | |||
641 | pub(crate) fn resolved_code_action( | ||
642 | world: &WorldSnapshot, | ||
643 | assist: ResolvedAssist, | ||
644 | ) -> Result<lsp_ext::CodeAction> { | ||
645 | let res = lsp_ext::CodeAction { | ||
646 | title: assist.assist.label, | ||
647 | id: Some(assist.assist.id.0.to_owned()), | ||
648 | group: assist.assist.group.and_then(|it| { | ||
649 | if world.config.client_caps.code_action_group { | ||
650 | None | ||
651 | } else { | ||
652 | Some(it.0) | ||
653 | } | ||
654 | }), | ||
624 | kind: Some(String::new()), | 655 | kind: Some(String::new()), |
625 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), | 656 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), |
626 | command: None, | 657 | command: None, |