@@ -3,7 +3,7 @@ import * as lc from "vscode-languageclient/node";
33import * as vscode from "vscode" ;
44import * as ra from "../src/lsp_ext" ;
55import * as Is from "vscode-languageclient/lib/common/utils/is" ;
6- import { assert , unwrapUndefinable } from "./util" ;
6+ import { assert } from "./util" ;
77import * as diagnostics from "./diagnostics" ;
88import { WorkspaceEdit } from "vscode" ;
99import { type Config , prepareVSCodeConfig } from "./config" ;
@@ -188,11 +188,17 @@ export async function createClient(
188188 context : await client . code2ProtocolConverter . asCodeActionContext ( context , token ) ,
189189 } ;
190190 const callback = async (
191- values : ( lc . Command | lc . CodeAction ) [ ] | null ,
191+ values : ( lc . Command | lc . CodeAction | object ) [ ] | null ,
192192 ) : Promise < ( vscode . Command | vscode . CodeAction ) [ ] | undefined > => {
193193 if ( values === null ) return undefined ;
194194 const result : ( vscode . CodeAction | vscode . Command ) [ ] = [ ] ;
195- const groups = new Map < string , { index : number ; items : vscode . CodeAction [ ] } > ( ) ;
195+ const groups = new Map <
196+ string ,
197+ {
198+ primary : vscode . CodeAction ;
199+ items : { label : string ; arguments : lc . CodeAction } [ ] ;
200+ }
201+ > ( ) ;
196202 for ( const item of values ) {
197203 // In our case we expect to get code edits only from diagnostics
198204 if ( lc . CodeAction . is ( item ) ) {
@@ -204,62 +210,55 @@ export async function createClient(
204210 result . push ( action ) ;
205211 continue ;
206212 }
207- assert (
208- isCodeActionWithoutEditsAndCommands ( item ) ,
209- "We don't expect edits or commands here" ,
210- ) ;
211- // eslint-disable-next-line @typescript-eslint/no-explicit-any
212- const kind = client . protocol2CodeConverter . asCodeActionKind ( ( item as any ) . kind ) ;
213- const action = new vscode . CodeAction ( item . title , kind ) ;
214- // eslint-disable-next-line @typescript-eslint/no-explicit-any
215- const group = ( item as any ) . group ;
216- action . command = {
217- command : "rust-analyzer.resolveCodeAction" ,
218- title : item . title ,
219- arguments : [ item ] ,
220- } ;
213+ assertIsCodeActionWithoutEditsAndCommands ( item ) ;
214+ const kind = client . protocol2CodeConverter . asCodeActionKind ( item . kind ) ;
215+ const group = item . group ;
221216
222- // Set a dummy edit, so that VS Code doesn't try to resolve this.
223- action . edit = new WorkspaceEdit ( ) ;
217+ const mkAction = ( ) => {
218+ const action = new vscode . CodeAction ( item . title , kind ) ;
219+ action . command = {
220+ command : "rust-analyzer.resolveCodeAction" ,
221+ title : item . title ,
222+ arguments : [ item ] ,
223+ } ;
224+ // Set a dummy edit, so that VS Code doesn't try to resolve this.
225+ action . edit = new WorkspaceEdit ( ) ;
226+ return action ;
227+ } ;
224228
225229 if ( group ) {
226230 let entry = groups . get ( group ) ;
227231 if ( ! entry ) {
228- entry = { index : result . length , items : [ ] } ;
232+ entry = { primary : mkAction ( ) , items : [ ] } ;
229233 groups . set ( group , entry ) ;
230- result . push ( action ) ;
234+ } else {
235+ entry . items . push ( {
236+ label : item . title ,
237+ arguments : item ,
238+ } ) ;
231239 }
232- entry . items . push ( action ) ;
233240 } else {
234- result . push ( action ) ;
241+ result . push ( mkAction ( ) ) ;
235242 }
236243 }
237- for ( const [ group , { index, items } ] of groups ) {
238- if ( items . length === 1 ) {
239- const item = unwrapUndefinable ( items [ 0 ] ) ;
240- result [ index ] = item ;
241- } else {
242- const action = new vscode . CodeAction ( group ) ;
243- const item = unwrapUndefinable ( items [ 0 ] ) ;
244- action . kind = item . kind ;
245- action . command = {
244+ for ( const [ group , { items, primary } ] of groups ) {
245+ // This group contains more than one item, so rewrite it to be a group action
246+ if ( items . length !== 0 ) {
247+ const args = [
248+ {
249+ label : primary . title ,
250+ arguments : primary . command ! . arguments ! [ 0 ] ,
251+ } ,
252+ ...items ,
253+ ] ;
254+ primary . title = group ;
255+ primary . command = {
246256 command : "rust-analyzer.applyActionGroup" ,
247257 title : "" ,
248- arguments : [
249- items . map ( ( item ) => {
250- return {
251- label : item . title ,
252- arguments : item . command ! . arguments ! [ 0 ] ,
253- } ;
254- } ) ,
255- ] ,
258+ arguments : [ args ] ,
256259 } ;
257-
258- // Set a dummy edit, so that VS Code doesn't try to resolve this.
259- action . edit = new WorkspaceEdit ( ) ;
260-
261- result [ index ] = action ;
262260 }
261+ result . push ( primary ) ;
263262 }
264263 return result ;
265264 } ;
@@ -363,17 +362,22 @@ class OverrideFeatures implements lc.StaticFeature {
363362 clear ( ) : void { }
364363}
365364
366- // eslint-disable-next-line @typescript-eslint/no-explicit-any
367- function isCodeActionWithoutEditsAndCommands ( value : any ) : boolean {
368- const candidate : lc . CodeAction = value ;
369- return (
365+ function assertIsCodeActionWithoutEditsAndCommands (
366+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
367+ candidate : any ,
368+ ) : asserts candidate is lc . CodeAction & {
369+ group ?: string ;
370+ } {
371+ assert (
370372 candidate &&
371- Is . string ( candidate . title ) &&
372- ( candidate . diagnostics === void 0 ||
373- Is . typedArray ( candidate . diagnostics , lc . Diagnostic . is ) ) &&
374- ( candidate . kind === void 0 || Is . string ( candidate . kind ) ) &&
375- candidate . edit === void 0 &&
376- candidate . command === void 0
373+ Is . string ( candidate . title ) &&
374+ ( candidate . diagnostics === undefined ||
375+ Is . typedArray ( candidate . diagnostics , lc . Diagnostic . is ) ) &&
376+ ( candidate . group === undefined || Is . string ( candidate . group ) ) &&
377+ ( candidate . kind === undefined || Is . string ( candidate . kind ) ) &&
378+ candidate . edit === undefined &&
379+ candidate . command === undefined ,
380+ `Expected a CodeAction without edits or commands, got: ${ JSON . stringify ( candidate ) } ` ,
377381 ) ;
378382}
379383
0 commit comments