1/* @internal */ 2namespace ts.codefix { 3 const errorCodeToFixes = createMultiMap<CodeFixRegistration>(); 4 const fixIdToRegistration = new Map<string, CodeFixRegistration>(); 5 6 export function createCodeFixActionWithoutFixAll(fixName: string, changes: FileTextChanges[], description: DiagnosticAndArguments) { 7 return createCodeFixActionWorker(fixName, diagnosticToString(description), changes, /*fixId*/ undefined, /*fixAllDescription*/ undefined); 8 } 9 10 export function createCodeFixAction(fixName: string, changes: FileTextChanges[], description: DiagnosticAndArguments, fixId: {}, fixAllDescription: DiagnosticAndArguments, command?: CodeActionCommand): CodeFixAction { 11 return createCodeFixActionWorker(fixName, diagnosticToString(description), changes, fixId, diagnosticToString(fixAllDescription), command); 12 } 13 14 export function createCodeFixActionMaybeFixAll(fixName: string, changes: FileTextChanges[], description: DiagnosticAndArguments, fixId?: {}, fixAllDescription?: DiagnosticAndArguments, command?: CodeActionCommand) { 15 return createCodeFixActionWorker(fixName, diagnosticToString(description), changes, fixId, fixAllDescription && diagnosticToString(fixAllDescription), command); 16 } 17 18 function createCodeFixActionWorker(fixName: string, description: string, changes: FileTextChanges[], fixId?: {}, fixAllDescription?: string, command?: CodeActionCommand): CodeFixAction { 19 return { fixName, description, changes, fixId, fixAllDescription, commands: command ? [command] : undefined }; 20 } 21 22 export function registerCodeFix(reg: CodeFixRegistration) { 23 for (const error of reg.errorCodes) { 24 errorCodeToFixes.add(String(error), reg); 25 } 26 if (reg.fixIds) { 27 for (const fixId of reg.fixIds) { 28 Debug.assert(!fixIdToRegistration.has(fixId)); 29 fixIdToRegistration.set(fixId, reg); 30 } 31 } 32 } 33 34 export function getSupportedErrorCodes(): string[] { 35 return arrayFrom(errorCodeToFixes.keys()); 36 } 37 38 function removeFixIdIfFixAllUnavailable(registration: CodeFixRegistration, diagnostics: Diagnostic[]) { 39 const { errorCodes } = registration; 40 let maybeFixableDiagnostics = 0; 41 for (const diag of diagnostics) { 42 if (contains(errorCodes, diag.code)) maybeFixableDiagnostics++; 43 if (maybeFixableDiagnostics > 1) break; 44 } 45 46 const fixAllUnavailable = maybeFixableDiagnostics < 2; 47 return ({ fixId, fixAllDescription, ...action }: CodeFixAction): CodeFixAction => { 48 return fixAllUnavailable ? action : { ...action, fixId, fixAllDescription }; 49 }; 50 } 51 52 export function getFixes(context: CodeFixContext): readonly CodeFixAction[] { 53 const diagnostics = getDiagnostics(context); 54 const registrations = errorCodeToFixes.get(String(context.errorCode)); 55 return flatMap(registrations, f => map(f.getCodeActions(context), removeFixIdIfFixAllUnavailable(f, diagnostics))); 56 } 57 58 export function getAllFixes(context: CodeFixAllContext): CombinedCodeActions { 59 // Currently fixId is always a string. 60 return fixIdToRegistration.get(cast(context.fixId, isString))!.getAllCodeActions!(context); 61 } 62 63 export function createCombinedCodeActions(changes: FileTextChanges[], commands?: CodeActionCommand[]): CombinedCodeActions { 64 return { changes, commands }; 65 } 66 67 export function createFileTextChanges(fileName: string, textChanges: TextChange[]): FileTextChanges { 68 return { fileName, textChanges }; 69 } 70 71 export function codeFixAll( 72 context: CodeFixAllContext, 73 errorCodes: number[], 74 use: (changes: textChanges.ChangeTracker, error: DiagnosticWithLocation, commands: Push<CodeActionCommand>) => void, 75 ): CombinedCodeActions { 76 const commands: CodeActionCommand[] = []; 77 const changes = textChanges.ChangeTracker.with(context, t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands))); 78 return createCombinedCodeActions(changes, commands.length === 0 ? undefined : commands); 79 } 80 81 export function eachDiagnostic(context: CodeFixAllContext, errorCodes: readonly number[], cb: (diag: DiagnosticWithLocation) => void): void { 82 for (const diag of getDiagnostics(context)) { 83 if (contains(errorCodes, diag.code)) { 84 cb(diag as DiagnosticWithLocation); 85 } 86 } 87 } 88 89 function getDiagnostics({ program, sourceFile, cancellationToken }: CodeFixContextBase) { 90 return [ 91 ...program.getSemanticDiagnostics(sourceFile, cancellationToken), 92 ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), 93 ...computeSuggestionDiagnostics(sourceFile, program, cancellationToken) 94 ]; 95 } 96} 97