@@ -152,7 +152,8 @@ export let findCodeActionsInDiagnosticsMessage = ({
152152 let codeActionEtractors = [
153153 simpleTypeMismatches ,
154154 didYouMeanAction ,
155- addUndefinedRecordFields ,
155+ addUndefinedRecordFieldsV10 ,
156+ addUndefinedRecordFieldsV11 ,
156157 simpleConversion ,
157158 applyUncurried ,
158159 simpleAddMissingCases ,
@@ -310,11 +311,107 @@ let wrapInSome: codeActionExtractor = ({
310311 return false ;
311312} ;
312313
314+ let handleUndefinedRecordFieldsAction = ( {
315+ recordFieldNames,
316+ codeActions,
317+ file,
318+ range,
319+ diagnostic,
320+ } : {
321+ recordFieldNames : string [ ] ;
322+ codeActions : filesCodeActions ;
323+ file : string ;
324+ range : p . Range ;
325+ diagnostic : p . Diagnostic ;
326+ } ) => {
327+ if ( recordFieldNames != null ) {
328+ codeActions [ file ] = codeActions [ file ] || [ ] ;
329+
330+ // The formatter outputs trailing commas automatically if the record
331+ // definition is on multiple lines, and no trailing comma if it's on a
332+ // single line. We need to adapt to this so we don't accidentally
333+ // insert an invalid comma.
334+ let multilineRecordDefinitionBody = range . start . line !== range . end . line ;
335+
336+ // Let's build up the text we're going to insert.
337+ let newText = "" ;
338+
339+ if ( multilineRecordDefinitionBody ) {
340+ // If it's a multiline body, we know it looks like this:
341+ // ```
342+ // let someRecord = {
343+ // atLeastOneExistingField: string,
344+ // }
345+ // ```
346+ // We can figure out the formatting from the range the code action
347+ // gives us. We'll insert to the direct left of the ending brace.
348+
349+ // The end char is the closing brace, and it's always going to be 2
350+ // characters back from the record fields.
351+ let paddingCharacters = multilineRecordDefinitionBody
352+ ? range . end . character + 2
353+ : 0 ;
354+ let paddingContentRecordField = Array . from ( {
355+ length : paddingCharacters ,
356+ } ) . join ( " " ) ;
357+ let paddingContentEndBrace = Array . from ( {
358+ length : range . end . character ,
359+ } ) . join ( " " ) ;
360+
361+ recordFieldNames . forEach ( ( fieldName , index ) => {
362+ if ( index === 0 ) {
363+ // This adds spacing from the ending brace up to the equivalent
364+ // of the last record field name, needed for the first inserted
365+ // record field name.
366+ newText += " " ;
367+ } else {
368+ // The rest of the new record field names will start from a new
369+ // line, so they need left padding all the way to the same level
370+ // as the rest of the record fields.
371+ newText += paddingContentRecordField ;
372+ }
373+
374+ newText += `${ fieldName } : failwith("TODO"),\n` ;
375+ } ) ;
376+
377+ // Let's put the end brace back where it was (we still have it to the direct right of us).
378+ newText += `${ paddingContentEndBrace } ` ;
379+ } else {
380+ // A single line record definition body is a bit easier - we'll just add the new fields on the same line.
381+ newText += ", " ;
382+ newText += recordFieldNames
383+ . map ( ( fieldName ) => `${ fieldName } : failwith("TODO")` )
384+ . join ( ", " ) ;
385+ }
386+
387+ let codeAction : p . CodeAction = {
388+ title : `Add missing record fields` ,
389+ edit : {
390+ changes : {
391+ [ file ] : insertBeforeEndingChar ( range , newText ) ,
392+ } ,
393+ } ,
394+ diagnostics : [ diagnostic ] ,
395+ kind : p . CodeActionKind . QuickFix ,
396+ isPreferred : true ,
397+ } ;
398+
399+ codeActions [ file ] . push ( {
400+ range,
401+ codeAction,
402+ } ) ;
403+
404+ return true ;
405+ }
406+
407+ return false ;
408+ } ;
409+
313410// This action handles when the compiler errors on certain fields of a record
314411// being undefined. We then offers an action that inserts all of the record
315412// fields, with an `assert false` dummy value. `assert false` is so applying the
316413// code action actually compiles.
317- let addUndefinedRecordFields : codeActionExtractor = ( {
414+ let addUndefinedRecordFieldsV10 : codeActionExtractor = ( {
318415 array,
319416 codeActions,
320417 diagnostic,
@@ -335,85 +432,53 @@ let addUndefinedRecordFields: codeActionExtractor = ({
335432 recordFieldNames . push ( ...line . trim ( ) . split ( " " ) ) ;
336433 } ) ;
337434
338- if ( recordFieldNames != null ) {
339- codeActions [ file ] = codeActions [ file ] || [ ] ;
435+ return handleUndefinedRecordFieldsAction ( {
436+ recordFieldNames,
437+ codeActions,
438+ diagnostic,
439+ file,
440+ range,
441+ } ) ;
442+ }
340443
341- // The formatter outputs trailing commas automatically if the record
342- // definition is on multiple lines, and no trailing comma if it's on a
343- // single line. We need to adapt to this so we don't accidentally
344- // insert an invalid comma.
345- let multilineRecordDefinitionBody = range . start . line !== range . end . line ;
346-
347- // Let's build up the text we're going to insert.
348- let newText = "" ;
349-
350- if ( multilineRecordDefinitionBody ) {
351- // If it's a multiline body, we know it looks like this:
352- // ```
353- // let someRecord = {
354- // atLeastOneExistingField: string,
355- // }
356- // ```
357- // We can figure out the formatting from the range the code action
358- // gives us. We'll insert to the direct left of the ending brace.
359-
360- // The end char is the closing brace, and it's always going to be 2
361- // characters back from the record fields.
362- let paddingCharacters = multilineRecordDefinitionBody
363- ? range . end . character + 2
364- : 0 ;
365- let paddingContentRecordField = Array . from ( {
366- length : paddingCharacters ,
367- } ) . join ( " " ) ;
368- let paddingContentEndBrace = Array . from ( {
369- length : range . end . character ,
370- } ) . join ( " " ) ;
371-
372- recordFieldNames . forEach ( ( fieldName , index ) => {
373- if ( index === 0 ) {
374- // This adds spacing from the ending brace up to the equivalent
375- // of the last record field name, needed for the first inserted
376- // record field name.
377- newText += " " ;
378- } else {
379- // The rest of the new record field names will start from a new
380- // line, so they need left padding all the way to the same level
381- // as the rest of the record fields.
382- newText += paddingContentRecordField ;
383- }
384-
385- newText += `${ fieldName } : assert false,\n` ;
386- } ) ;
444+ return false ;
445+ } ;
387446
388- // Let's put the end brace back where it was (we still have it to the direct right of us).
389- newText += `${ paddingContentEndBrace } ` ;
390- } else {
391- // A single line record definition body is a bit easier - we'll just add the new fields on the same line.
392- newText += ", " ;
393- newText += recordFieldNames
394- . map ( ( fieldName ) => `${ fieldName } : assert false` )
395- . join ( ", " ) ;
396- }
447+ let addUndefinedRecordFieldsV11 : codeActionExtractor = ( {
448+ array,
449+ codeActions,
450+ diagnostic,
451+ file,
452+ index,
453+ line,
454+ range,
455+ } ) => {
456+ if ( line . startsWith ( "Some required record fields are missing:" ) ) {
457+ let recordFieldNames = line
458+ . trim ( )
459+ . split ( "Some required record fields are missing: " ) [ 1 ]
460+ ?. split ( " " ) ;
397461
398- let codeAction : p . CodeAction = {
399- title : `Add missing record fields` ,
400- edit : {
401- changes : {
402- [ file ] : insertBeforeEndingChar ( range , newText ) ,
403- } ,
404- } ,
405- diagnostics : [ diagnostic ] ,
406- kind : p . CodeActionKind . QuickFix ,
407- isPreferred : true ,
408- } ;
462+ // This collects the rest of the fields if fields are printed on
463+ // multiple lines.
464+ let stop = false ;
465+ array . slice ( index + 1 ) . forEach ( ( line ) => {
466+ if ( stop ) return ;
409467
410- codeActions [ file ] . push ( {
411- range,
412- codeAction,
413- } ) ;
468+ recordFieldNames . push ( ...line . trim ( ) . split ( "." ) [ 0 ] . split ( " " ) ) ;
414469
415- return true ;
416- }
470+ if ( line . includes ( "." ) ) {
471+ stop = true ;
472+ }
473+ } ) ;
474+
475+ return handleUndefinedRecordFieldsAction ( {
476+ recordFieldNames,
477+ codeActions,
478+ diagnostic,
479+ file,
480+ range,
481+ } ) ;
417482 }
418483
419484 return false ;
0 commit comments