diff options
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/to_proto.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 10 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 54 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 10 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 49 | ||||
-rw-r--r-- | editors/code/src/client.ts | 41 | ||||
-rw-r--r-- | editors/code/src/commands/index.ts | 14 | ||||
-rw-r--r-- | editors/code/src/main.ts | 2 |
11 files changed, 109 insertions, 83 deletions
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index d75c48597..0e4412ade 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -102,6 +102,7 @@ pub struct ClientCapsConfig { | |||
102 | pub hierarchical_symbols: bool, | 102 | pub hierarchical_symbols: bool, |
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 | } | 106 | } |
106 | 107 | ||
107 | impl Default for Config { | 108 | impl Default for Config { |
@@ -294,9 +295,13 @@ impl Config { | |||
294 | 295 | ||
295 | self.assist.allow_snippets(false); | 296 | self.assist.allow_snippets(false); |
296 | if let Some(experimental) = &caps.experimental { | 297 | if let Some(experimental) = &caps.experimental { |
297 | let enable = | 298 | let snippet_text_edit = |
298 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); | 299 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); |
299 | self.assist.allow_snippets(enable); | 300 | self.assist.allow_snippets(snippet_text_edit); |
301 | |||
302 | let code_action_group = | ||
303 | experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); | ||
304 | self.client_caps.code_action_group = code_action_group | ||
300 | } | 305 | } |
301 | } | 306 | } |
302 | } | 307 | } |
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 96466b5c9..c40cfdcdc 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 | group: None, | ||
68 | kind: Some( | 69 | kind: Some( |
69 | "quickfix", | 70 | "quickfix", |
70 | ), | 71 | ), |
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 8f962277f..6dd3fcb2e 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 | group: None, | ||
53 | kind: Some( | 54 | kind: Some( |
54 | "quickfix", | 55 | "quickfix", |
55 | ), | 56 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index afea59525..a500d670a 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 | group: None, | ||
148 | kind: Some("quickfix".to_string()), | 149 | kind: Some("quickfix".to_string()), |
149 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | 150 | edit: Some(lsp_ext::SnippetWorkspaceEdit { |
150 | // FIXME: there's no good reason to use edit_map here.... | 151 | // FIXME: there's no good reason to use edit_map here.... |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 0fd60caf4..c25d90a50 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -133,14 +133,6 @@ pub struct Runnable { | |||
133 | pub cwd: Option<PathBuf>, | 133 | pub cwd: Option<PathBuf>, |
134 | } | 134 | } |
135 | 135 | ||
136 | #[derive(Deserialize, Serialize, Debug)] | ||
137 | #[serde(rename_all = "camelCase")] | ||
138 | pub struct SourceChange { | ||
139 | pub label: String, | ||
140 | pub workspace_edit: SnippetWorkspaceEdit, | ||
141 | pub cursor_position: Option<lsp_types::TextDocumentPositionParams>, | ||
142 | } | ||
143 | |||
144 | pub enum InlayHints {} | 136 | pub enum InlayHints {} |
145 | 137 | ||
146 | impl Request for InlayHints { | 138 | impl Request for InlayHints { |
@@ -196,6 +188,8 @@ impl Request for CodeActionRequest { | |||
196 | pub struct CodeAction { | 188 | pub struct CodeAction { |
197 | pub title: String, | 189 | pub title: String, |
198 | #[serde(skip_serializing_if = "Option::is_none")] | 190 | #[serde(skip_serializing_if = "Option::is_none")] |
191 | pub group: Option<String>, | ||
192 | #[serde(skip_serializing_if = "Option::is_none")] | ||
199 | pub kind: Option<String>, | 193 | pub kind: Option<String>, |
200 | #[serde(skip_serializing_if = "Option::is_none")] | 194 | #[serde(skip_serializing_if = "Option::is_none")] |
201 | pub command: Option<lsp_types::Command>, | 195 | pub command: Option<lsp_types::Command>, |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 25e660bd5..89144f743 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -18,7 +18,7 @@ use lsp_types::{ | |||
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
22 | TextEdit, | 22 | TextEdit, |
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
@@ -720,6 +720,7 @@ pub fn handle_code_action( | |||
720 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 720 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
721 | let line_index = world.analysis().file_line_index(file_id)?; | 721 | let line_index = world.analysis().file_line_index(file_id)?; |
722 | let range = from_proto::text_range(&line_index, params.range); | 722 | let range = from_proto::text_range(&line_index, params.range); |
723 | let frange = FileRange { file_id, range }; | ||
723 | 724 | ||
724 | let diagnostics = world.analysis().diagnostics(file_id)?; | 725 | let diagnostics = world.analysis().diagnostics(file_id)?; |
725 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 726 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
@@ -733,7 +734,8 @@ pub fn handle_code_action( | |||
733 | for source_edit in fixes_from_diagnostics { | 734 | for source_edit in fixes_from_diagnostics { |
734 | let title = source_edit.label.clone(); | 735 | let title = source_edit.label.clone(); |
735 | let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; | 736 | let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; |
736 | let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; | 737 | let action = |
738 | lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; | ||
737 | res.push(action); | 739 | res.push(action); |
738 | } | 740 | } |
739 | 741 | ||
@@ -745,53 +747,9 @@ pub fn handle_code_action( | |||
745 | res.push(fix.action.clone()); | 747 | res.push(fix.action.clone()); |
746 | } | 748 | } |
747 | 749 | ||
748 | let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); | 750 | for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { |
749 | for assist in | 751 | res.push(to_proto::code_action(&world, assist)?.into()); |
750 | world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter() | ||
751 | { | ||
752 | match &assist.group_label { | ||
753 | Some(label) => grouped_assists | ||
754 | .entry(label.to_owned()) | ||
755 | .or_insert_with(|| { | ||
756 | let idx = res.len(); | ||
757 | let dummy = lsp_ext::CodeAction { | ||
758 | title: String::new(), | ||
759 | kind: None, | ||
760 | command: None, | ||
761 | edit: None, | ||
762 | }; | ||
763 | res.push(dummy); | ||
764 | (idx, Vec::new()) | ||
765 | }) | ||
766 | .1 | ||
767 | .push(assist), | ||
768 | None => { | ||
769 | res.push(to_proto::code_action(&world, assist)?.into()); | ||
770 | } | ||
771 | } | ||
772 | } | ||
773 | |||
774 | for (group_label, (idx, assists)) in grouped_assists { | ||
775 | if assists.len() == 1 { | ||
776 | res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into(); | ||
777 | } else { | ||
778 | let title = group_label; | ||
779 | |||
780 | let mut arguments = Vec::with_capacity(assists.len()); | ||
781 | for assist in assists { | ||
782 | let source_change = to_proto::source_change(&world, assist.source_change)?; | ||
783 | arguments.push(to_value(source_change)?); | ||
784 | } | ||
785 | |||
786 | let command = Some(Command { | ||
787 | title: title.clone(), | ||
788 | command: "rust-analyzer.selectAndApplySourceChange".to_string(), | ||
789 | arguments: Some(vec![serde_json::Value::Array(arguments)]), | ||
790 | }); | ||
791 | res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command }; | ||
792 | } | ||
793 | } | 752 | } |
794 | |||
795 | Ok(Some(res)) | 753 | Ok(Some(res)) |
796 | } | 754 | } |
797 | 755 | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index f6f4bb134..461944ada 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -478,15 +478,6 @@ pub(crate) fn resource_op( | |||
478 | Ok(res) | 478 | Ok(res) |
479 | } | 479 | } |
480 | 480 | ||
481 | pub(crate) fn source_change( | ||
482 | world: &WorldSnapshot, | ||
483 | source_change: SourceChange, | ||
484 | ) -> Result<lsp_ext::SourceChange> { | ||
485 | let label = source_change.label.clone(); | ||
486 | let workspace_edit = self::snippet_workspace_edit(world, source_change)?; | ||
487 | Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position: None }) | ||
488 | } | ||
489 | |||
490 | pub(crate) fn snippet_workspace_edit( | 481 | pub(crate) fn snippet_workspace_edit( |
491 | world: &WorldSnapshot, | 482 | world: &WorldSnapshot, |
492 | source_change: SourceChange, | 483 | source_change: SourceChange, |
@@ -606,6 +597,7 @@ fn main() <fold>{ | |||
606 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { | 597 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { |
607 | let res = lsp_ext::CodeAction { | 598 | let res = lsp_ext::CodeAction { |
608 | title: assist.label, | 599 | title: assist.label, |
600 | group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, | ||
609 | kind: Some(String::new()), | 601 | kind: Some(String::new()), |
610 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), | 602 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), |
611 | command: None, | 603 | command: None, |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 7c45aef4c..d90875f8b 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -5,7 +5,7 @@ It's a best effort document, when in doubt, consult the source (and send a PR wi | |||
5 | We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. | 5 | We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. |
6 | All capabilities are enabled via `experimental` field of `ClientCapabilities`. | 6 | All capabilities are enabled via `experimental` field of `ClientCapabilities`. |
7 | 7 | ||
8 | ## `SnippetTextEdit` | 8 | ## Snippet `TextEdit` |
9 | 9 | ||
10 | **Client Capability:** `{ "snippetTextEdit": boolean }` | 10 | **Client Capability:** `{ "snippetTextEdit": boolean }` |
11 | 11 | ||
@@ -36,7 +36,7 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser | |||
36 | * Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)? | 36 | * Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)? |
37 | * Can snippets span multiple files (so far, no)? | 37 | * Can snippets span multiple files (so far, no)? |
38 | 38 | ||
39 | ## `joinLines` | 39 | ## Join Lines |
40 | 40 | ||
41 | **Server Capability:** `{ "joinLines": boolean }` | 41 | **Server Capability:** `{ "joinLines": boolean }` |
42 | 42 | ||
@@ -119,3 +119,48 @@ SSR with query `foo($a:expr, $b:expr) ==>> ($a).foo($b)` will transform, eg `foo | |||
119 | 119 | ||
120 | * Probably needs search without replace mode | 120 | * Probably needs search without replace mode |
121 | * Needs a way to limit the scope to certain files. | 121 | * Needs a way to limit the scope to certain files. |
122 | |||
123 | ## `CodeAction` Groups | ||
124 | |||
125 | **Client Capability:** `{ "codeActionGroup": boolean }` | ||
126 | |||
127 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: | ||
128 | |||
129 | ```typescript | ||
130 | interface CodeAction { | ||
131 | title: string; | ||
132 | group?: string; | ||
133 | ... | ||
134 | } | ||
135 | ``` | ||
136 | |||
137 | All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu. | ||
138 | The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as | ||
139 | |||
140 | ``` | ||
141 | 💡 | ||
142 | +-------------+ | ||
143 | | foo | | ||
144 | +-------------+-----+ | ||
145 | | frobnicate >| bar | | ||
146 | +-------------+-----+ | ||
147 | | baz | | ||
148 | +-----+ | ||
149 | ``` | ||
150 | |||
151 | Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`. | ||
152 | |||
153 | ### Example | ||
154 | |||
155 | ```rust | ||
156 | fn main() { | ||
157 | let x: Entry/*cursor here*/ = todo!(); | ||
158 | } | ||
159 | ``` | ||
160 | |||
161 | Invoking code action at this position will yield two code actions for importing `Entry` from either `collections::HashMap` or `collection::BTreeMap`, grouped under a single "import" group. | ||
162 | |||
163 | ### Unresolved Questions | ||
164 | |||
165 | * Is a fixed two-level structure enough? | ||
166 | * Should we devise a general way to encode custom interaction protocols for GUI refactorings? | ||
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index fac1a0be3..d64f9a3f9 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -41,10 +41,12 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
41 | return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { | 41 | return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { |
42 | if (values === null) return undefined; | 42 | if (values === null) return undefined; |
43 | const result: (vscode.CodeAction | vscode.Command)[] = []; | 43 | const result: (vscode.CodeAction | vscode.Command)[] = []; |
44 | const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>(); | ||
44 | for (const item of values) { | 45 | for (const item of values) { |
45 | if (lc.CodeAction.is(item)) { | 46 | if (lc.CodeAction.is(item)) { |
46 | const action = client.protocol2CodeConverter.asCodeAction(item); | 47 | const action = client.protocol2CodeConverter.asCodeAction(item); |
47 | if (isSnippetEdit(item)) { | 48 | const group = actionGroup(item); |
49 | if (isSnippetEdit(item) || group) { | ||
48 | action.command = { | 50 | action.command = { |
49 | command: "rust-analyzer.applySnippetWorkspaceEdit", | 51 | command: "rust-analyzer.applySnippetWorkspaceEdit", |
50 | title: "", | 52 | title: "", |
@@ -52,12 +54,38 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
52 | }; | 54 | }; |
53 | action.edit = undefined; | 55 | action.edit = undefined; |
54 | } | 56 | } |
55 | result.push(action); | 57 | |
58 | if (group) { | ||
59 | let entry = groups.get(group); | ||
60 | if (!entry) { | ||
61 | entry = { index: result.length, items: [] }; | ||
62 | groups.set(group, entry); | ||
63 | result.push(action); | ||
64 | } | ||
65 | entry.items.push(action); | ||
66 | } else { | ||
67 | result.push(action); | ||
68 | } | ||
56 | } else { | 69 | } else { |
57 | const command = client.protocol2CodeConverter.asCommand(item); | 70 | const command = client.protocol2CodeConverter.asCommand(item); |
58 | result.push(command); | 71 | result.push(command); |
59 | } | 72 | } |
60 | } | 73 | } |
74 | for (const [group, { index, items }] of groups) { | ||
75 | if (items.length === 1) { | ||
76 | result[index] = items[0]; | ||
77 | } else { | ||
78 | const action = new vscode.CodeAction(group); | ||
79 | action.command = { | ||
80 | command: "rust-analyzer.applyActionGroup", | ||
81 | title: "", | ||
82 | arguments: [items.map((item) => { | ||
83 | return { label: item.title, edit: item.command!!.arguments!![0] }; | ||
84 | })], | ||
85 | }; | ||
86 | result[index] = action; | ||
87 | } | ||
88 | } | ||
61 | return result; | 89 | return result; |
62 | }, | 90 | }, |
63 | (_error) => undefined | 91 | (_error) => undefined |
@@ -81,15 +109,16 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
81 | // implementations are still in the "proposed" category for 3.16. | 109 | // implementations are still in the "proposed" category for 3.16. |
82 | client.registerFeature(new CallHierarchyFeature(client)); | 110 | client.registerFeature(new CallHierarchyFeature(client)); |
83 | client.registerFeature(new SemanticTokensFeature(client)); | 111 | client.registerFeature(new SemanticTokensFeature(client)); |
84 | client.registerFeature(new SnippetTextEditFeature()); | 112 | client.registerFeature(new ExperimentalFeatures()); |
85 | 113 | ||
86 | return client; | 114 | return client; |
87 | } | 115 | } |
88 | 116 | ||
89 | class SnippetTextEditFeature implements lc.StaticFeature { | 117 | class ExperimentalFeatures implements lc.StaticFeature { |
90 | fillClientCapabilities(capabilities: lc.ClientCapabilities): void { | 118 | fillClientCapabilities(capabilities: lc.ClientCapabilities): void { |
91 | const caps: any = capabilities.experimental ?? {}; | 119 | const caps: any = capabilities.experimental ?? {}; |
92 | caps.snippetTextEdit = true; | 120 | caps.snippetTextEdit = true; |
121 | caps.codeActionGroup = true; | ||
93 | capabilities.experimental = caps; | 122 | capabilities.experimental = caps; |
94 | } | 123 | } |
95 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 124 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
@@ -107,3 +136,7 @@ function isSnippetEdit(action: lc.CodeAction): boolean { | |||
107 | } | 136 | } |
108 | return false; | 137 | return false; |
109 | } | 138 | } |
139 | |||
140 | function actionGroup(action: lc.CodeAction): string | undefined { | ||
141 | return (action as any).group; | ||
142 | } | ||
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index e5ed77e32..abb53a248 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts | |||
@@ -41,15 +41,11 @@ export function applySourceChange(ctx: Ctx): Cmd { | |||
41 | }; | 41 | }; |
42 | } | 42 | } |
43 | 43 | ||
44 | export function selectAndApplySourceChange(ctx: Ctx): Cmd { | 44 | export function applyActionGroup(_ctx: Ctx): Cmd { |
45 | return async (changes: ra.SourceChange[]) => { | 45 | return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => { |
46 | if (changes.length === 1) { | 46 | const selectedAction = await vscode.window.showQuickPick(actions); |
47 | await sourceChange.applySourceChange(ctx, changes[0]); | 47 | if (!selectedAction) return; |
48 | } else if (changes.length > 0) { | 48 | await applySnippetWorkspaceEdit(selectedAction.edit); |
49 | const selectedChange = await vscode.window.showQuickPick(changes); | ||
50 | if (!selectedChange) return; | ||
51 | await sourceChange.applySourceChange(ctx, selectedChange); | ||
52 | } | ||
53 | }; | 49 | }; |
54 | } | 50 | } |
55 | 51 | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 8b0a9d870..4d4513869 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -92,7 +92,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
92 | ctx.registerCommand('showReferences', commands.showReferences); | 92 | ctx.registerCommand('showReferences', commands.showReferences); |
93 | ctx.registerCommand('applySourceChange', commands.applySourceChange); | 93 | ctx.registerCommand('applySourceChange', commands.applySourceChange); |
94 | ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); | 94 | ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); |
95 | ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); | 95 | ctx.registerCommand('applyActionGroup', commands.applyActionGroup); |
96 | 96 | ||
97 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); | 97 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); |
98 | 98 | ||