|
| 1 | +/* @internal */ |
| 2 | +namespace ts.refactor.addOrRemoveBracesToArrowFunction { |
| 3 | + const refactorName = "Convert overload list to single signature"; |
| 4 | + const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message; |
| 5 | + registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); |
| 6 | + |
| 7 | + |
| 8 | + function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { |
| 9 | + const { file, startPosition, program } = context; |
| 10 | + const info = getConvertableOverloadListAtPosition(file, startPosition, program); |
| 11 | + if (!info) return emptyArray; |
| 12 | + |
| 13 | + return [{ |
| 14 | + name: refactorName, |
| 15 | + description: refactorDescription, |
| 16 | + actions: [{ |
| 17 | + name: refactorName, |
| 18 | + description: refactorDescription |
| 19 | + }] |
| 20 | + }]; |
| 21 | + } |
| 22 | + |
| 23 | + function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { |
| 24 | + const { file, startPosition, program } = context; |
| 25 | + const signatureDecls = getConvertableOverloadListAtPosition(file, startPosition, program); |
| 26 | + if (!signatureDecls) return undefined; |
| 27 | + |
| 28 | + const lastDeclaration = signatureDecls[signatureDecls.length - 1]; |
| 29 | + let updated = lastDeclaration; |
| 30 | + switch (lastDeclaration.kind) { |
| 31 | + case SyntaxKind.MethodSignature: { |
| 32 | + updated = updateMethodSignature( |
| 33 | + lastDeclaration, |
| 34 | + lastDeclaration.typeParameters, |
| 35 | + getNewParametersForCombinedSignature(signatureDecls), |
| 36 | + lastDeclaration.type, |
| 37 | + lastDeclaration.name, |
| 38 | + lastDeclaration.questionToken |
| 39 | + ); |
| 40 | + break; |
| 41 | + } |
| 42 | + case SyntaxKind.CallSignature: { |
| 43 | + updated = updateCallSignature( |
| 44 | + lastDeclaration, |
| 45 | + lastDeclaration.typeParameters, |
| 46 | + getNewParametersForCombinedSignature(signatureDecls), |
| 47 | + lastDeclaration.type, |
| 48 | + ); |
| 49 | + break; |
| 50 | + } |
| 51 | + case SyntaxKind.ConstructSignature: { |
| 52 | + updated = updateConstructSignature( |
| 53 | + lastDeclaration, |
| 54 | + lastDeclaration.typeParameters, |
| 55 | + getNewParametersForCombinedSignature(signatureDecls), |
| 56 | + lastDeclaration.type, |
| 57 | + ); |
| 58 | + break; |
| 59 | + } |
| 60 | + case SyntaxKind.FunctionDeclaration: { |
| 61 | + updated = updateFunctionDeclaration( |
| 62 | + lastDeclaration, |
| 63 | + lastDeclaration.decorators, |
| 64 | + lastDeclaration.modifiers, |
| 65 | + lastDeclaration.asteriskToken, |
| 66 | + lastDeclaration.name, |
| 67 | + lastDeclaration.typeParameters, |
| 68 | + getNewParametersForCombinedSignature(signatureDecls), |
| 69 | + lastDeclaration.type, |
| 70 | + lastDeclaration.body |
| 71 | + ); |
| 72 | + break; |
| 73 | + } |
| 74 | + default: return Debug.failBadSyntaxKind(lastDeclaration, "Unhandled signature kind in overload list conversion refactoring"); |
| 75 | + } |
| 76 | + |
| 77 | + if (updated === lastDeclaration) { |
| 78 | + return; // No edits to apply, do nothing |
| 79 | + } |
| 80 | + |
| 81 | + const edits = textChanges.ChangeTracker.with(context, t => { |
| 82 | + t.replaceNodeRange(file, signatureDecls[0], signatureDecls[signatureDecls.length - 1], updated); |
| 83 | + }); |
| 84 | + |
| 85 | + return { renameFilename: undefined, renameLocation: undefined, edits }; |
| 86 | + |
| 87 | + function getNewParametersForCombinedSignature(signatureDeclarations: (MethodSignature | CallSignatureDeclaration | ConstructSignatureDeclaration | FunctionDeclaration)[]): NodeArray<ParameterDeclaration> { |
| 88 | + return createNodeArray([ |
| 89 | + createParameter( |
| 90 | + /*decorators*/ undefined, |
| 91 | + /*modifiers*/ undefined, |
| 92 | + createToken(SyntaxKind.DotDotDotToken), |
| 93 | + "args", |
| 94 | + /*questionToken*/ undefined, |
| 95 | + createUnionTypeNode(map(signatureDeclarations, convertSignatureParametersToTuple)) |
| 96 | + ) |
| 97 | + ]); |
| 98 | + } |
| 99 | + |
| 100 | + function convertSignatureParametersToTuple(decl: MethodSignature | CallSignatureDeclaration | ConstructSignatureDeclaration | FunctionDeclaration): TupleTypeNode { |
| 101 | + return setEmitFlags(createTupleTypeNode(map(decl.parameters, convertParameterToNamedTupleMember)), EmitFlags.SingleLine); |
| 102 | + } |
| 103 | + |
| 104 | + function convertParameterToNamedTupleMember(p: ParameterDeclaration): NamedTupleMember { |
| 105 | + Debug.assert(isIdentifier(p.name)); // This is checked during refactoring applicability checking |
| 106 | + return setTextRange(createNamedTupleMember( |
| 107 | + p.dotDotDotToken, |
| 108 | + p.name, |
| 109 | + p.questionToken, |
| 110 | + p.type || createKeywordTypeNode(SyntaxKind.AnyKeyword) |
| 111 | + ), p); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + function getConvertableOverloadListAtPosition(file: SourceFile, startPosition: number, program: Program) { |
| 116 | + const node = getTokenAtPosition(file, startPosition); |
| 117 | + const containingDecl = findAncestor(node, n => isFunctionLikeDeclaration(n)); |
| 118 | + if (!containingDecl) { |
| 119 | + return; |
| 120 | + } |
| 121 | + const checker = program.getTypeChecker(); |
| 122 | + const signatureSymbol = containingDecl.symbol; |
| 123 | + if (!signatureSymbol) { |
| 124 | + return; |
| 125 | + } |
| 126 | + const decls = signatureSymbol.declarations; |
| 127 | + if (length(decls) <= 1) { |
| 128 | + return; |
| 129 | + } |
| 130 | + if (!every(decls, d => getSourceFileOfNode(d) === file)) { |
| 131 | + return; |
| 132 | + } |
| 133 | + const kindOne = decls[0].kind; |
| 134 | + if (kindOne !== SyntaxKind.MethodSignature && kindOne !== SyntaxKind.CallSignature && kindOne !== SyntaxKind.ConstructSignature && kindOne !== SyntaxKind.FunctionDeclaration) { |
| 135 | + return; |
| 136 | + } |
| 137 | + if (!every(decls, d => d.kind === kindOne)) { |
| 138 | + return; |
| 139 | + } |
| 140 | + const signatureDecls = decls as (MethodSignature | CallSignatureDeclaration | ConstructSignatureDeclaration | FunctionDeclaration)[]; |
| 141 | + if (some(signatureDecls, d => !!d.typeParameters || some(d.parameters, p => !!p.decorators || !!p.modifiers || !isIdentifier(p.name)))) { |
| 142 | + return; |
| 143 | + } |
| 144 | + const signatures = mapDefined(signatureDecls, d => checker.getSignatureFromDeclaration(d)); |
| 145 | + if (length(signatures) !== length(decls)) { |
| 146 | + return; |
| 147 | + } |
| 148 | + const returnOne = checker.getReturnTypeOfSignature(signatures[0]); |
| 149 | + if (!every(signatures, s => checker.getReturnTypeOfSignature(s) === returnOne)) { |
| 150 | + return; |
| 151 | + } |
| 152 | + |
| 153 | + return signatureDecls; |
| 154 | + } |
| 155 | +} |
0 commit comments