fix duplication of leading and trailing comments on refactored function

This commit is contained in:
Gabriela Britto 2019-02-06 15:40:58 -08:00
parent 18b2b55387
commit b87392c22c

View file

@ -5,6 +5,7 @@ namespace ts.refactor.convertToNamedParameters {
const actionNameNamedParameters = "Convert to named parameters";
const actionDescriptionNamedParameters = "Convert to named parameters";
const minimumParameterLength = 1;
let refactorSucceeded = true;
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
@ -26,25 +27,27 @@ namespace ts.refactor.convertToNamedParameters {
function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined {
Debug.assert(actionName === actionNameNamedParameters);
const { file, startPosition, program, cancellationToken, host } = context;
const func = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker());
if (!func || !cancellationToken) return undefined;
const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker());
if (!functionDeclaration || !cancellationToken) return undefined;
const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, func));
return { renameFilename: undefined, renameLocation: undefined, edits };
const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, cancellationToken, host, t, functionDeclaration));
return refactorSucceeded ? { renameFilename: undefined, renameLocation: undefined, edits } : undefined;
}
function doChange(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken, host: LanguageServiceHost, changes: textChanges.ChangeTracker, functionDeclaration: ValidFunctionDeclaration): void {
const newParamDeclaration = createObjectParameter(functionDeclaration, program, host);
const newFunctionDeclaration = getSynthesizedDeepClone(updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration)), /*includeTrivia*/ false);
const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param, /*includeTrivia*/ true));
const newFunctionDeclaration = updateDeclarationParameters(functionDeclaration, createNodeArray(newParamDeclaration));
suppressLeadingAndTrailingTrivia(newFunctionDeclaration, /*recursive*/ false);
changes.replaceNode(sourceFile, functionDeclaration, newFunctionDeclaration);
const nameNodes = getFunctionDeclarationNames(functionDeclaration);
const functionRefs = flatMap(nameNodes, name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken));
const functionCalls = deduplicate(getDirectFunctionCalls(functionRefs), (a, b) => a === b);
refactorSucceeded = true; // TODO: check if a bad reference was found
forEach(functionCalls, call => {
if (call.arguments && call.arguments.length) {
const newArguments = getSynthesizedDeepClone(createArgumentObject(functionDeclaration, call.arguments));
const newArguments = getSynthesizedDeepClone(createNewArguments(functionDeclaration, call.arguments), /*includeTrivia*/ true);
changes.replaceNodeRange(getSourceFileOfNode(call), first(call.arguments), last(call.arguments), newArguments);
}});
}
@ -125,14 +128,14 @@ namespace ts.refactor.convertToNamedParameters {
return isVariableDeclaration(functionDeclaration.parent) && !functionDeclaration.parent.type && isVarConst(functionDeclaration.parent) && isValidParameterNodeArray(functionDeclaration.parameters);
}
return false;
}
function isValidParameterNodeArray(parameters: NodeArray<ParameterDeclaration>): boolean {
return parameters && getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration);
}
function isValidParameterNodeArray(parameters: NodeArray<ParameterDeclaration>): parameters is ValidParameterNodeArray {
return parameters && getRefactorableParametersLength(parameters) > minimumParameterLength && every(parameters, isValidParameterDeclaration);
}
function isValidParameterDeclaration(paramDecl: ParameterDeclaration): paramDecl is ValidParameterDeclaration {
return !paramDecl.modifiers && isIdentifier(paramDecl.name);
function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration {
return !paramDeclaration.modifiers && isIdentifier(paramDeclaration.name);
}
}
function hasThisParameter(parameters: NodeArray<ParameterDeclaration>): boolean {
@ -153,7 +156,7 @@ namespace ts.refactor.convertToNamedParameters {
return parameters;
}
function createArgumentObject(functionDeclaration: ValidFunctionDeclaration, args: NodeArray<Expression>): ObjectLiteralExpression {
function createNewArguments(functionDeclaration: ValidFunctionDeclaration, args: NodeArray<Expression>): ObjectLiteralExpression {
const parameters = getRefactorableParameters(functionDeclaration.parameters);
const hasRestParameter = isRestParameter(last(parameters));
const nonRestArguments = hasRestParameter ? args.slice(0, parameters.length - 1) : args;
@ -168,16 +171,17 @@ namespace ts.refactor.convertToNamedParameters {
return createObjectLiteral(properties, /*multiLine*/ false);
}
function createObjectParameter(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray<ParameterDeclaration> {
function createNewParameters(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray<ParameterDeclaration> {
const refactorableParameters = getRefactorableParameters(functionDeclaration.parameters);
const bindingElements = map(
refactorableParameters,
paramDecl => {
return createBindingElement(
const element = createBindingElement(
/*dotDotDotToken*/ undefined,
/*propertyName*/ undefined,
getParameterName(paramDecl),
isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer); });
isRestParameter(paramDecl) ? createArrayLiteral() : paramDecl.initializer);
return element; });
const paramName = createObjectBindingPattern(bindingElements);
const paramType = createParamTypeNode(refactorableParameters);
@ -214,7 +218,7 @@ namespace ts.refactor.convertToNamedParameters {
return createPropertySignature(
/*modifiers*/ undefined,
paramDeclaration.name,
getParameterName(paramDeclaration),
paramDeclaration.initializer || isRestParameter(paramDeclaration) ? createToken(SyntaxKind.QuestionToken) : paramDeclaration.questionToken,
paramType,
/*initializer*/ undefined);
@ -227,8 +231,8 @@ namespace ts.refactor.convertToNamedParameters {
}
}
function getParameterName(paramDecl: ValidParameterDeclaration): string {
return getTextOfIdentifierOrLiteral(paramDecl.name);
function getParameterName(paramDeclaration: ValidParameterDeclaration) {
return getTextOfIdentifierOrLiteral(paramDeclaration.name);
}
function getFunctionDeclarationNames(functionDeclaration: ValidFunctionDeclaration): Node[] {
@ -256,6 +260,8 @@ namespace ts.refactor.convertToNamedParameters {
}
}
type ValidParameterNodeArray = NodeArray<ValidParameterDeclaration>;
type ValidVariableDeclaration = VariableDeclaration & { type: undefined };
interface ValidConstructor extends ConstructorDeclaration {