Merge pull request #30089 from Microsoft/convert-to-named-parameters
Convert to named parameters
This commit is contained in:
commit
d2364f555f
48 changed files with 1387 additions and 80 deletions
|
@ -917,7 +917,7 @@ namespace ts {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deduplicates an unsorted array.
|
* Deduplicates an unsorted array.
|
||||||
* @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates.
|
* @param equalityComparer An `EqualityComparer` used to determine if two values are duplicates.
|
||||||
* @param comparer An optional `Comparer` used to sort entries before comparison, though the
|
* @param comparer An optional `Comparer` used to sort entries before comparison, though the
|
||||||
* result will remain in the original order in `array`.
|
* result will remain in the original order in `array`.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4875,5 +4875,9 @@
|
||||||
"Enable the 'experimentalDecorators' option in your configuration file": {
|
"Enable the 'experimentalDecorators' option in your configuration file": {
|
||||||
"category": "Message",
|
"category": "Message",
|
||||||
"code": 95074
|
"code": 95074
|
||||||
|
},
|
||||||
|
"Convert to named parameters": {
|
||||||
|
"category": "Message",
|
||||||
|
"code": 95075
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace ts.codefix {
|
||||||
precedingNode = ctorDeclaration.parent.parent;
|
precedingNode = ctorDeclaration.parent.parent;
|
||||||
newClassDeclaration = createClassFromVariableDeclaration(ctorDeclaration as VariableDeclaration);
|
newClassDeclaration = createClassFromVariableDeclaration(ctorDeclaration as VariableDeclaration);
|
||||||
if ((<VariableDeclarationList>ctorDeclaration.parent).declarations.length === 1) {
|
if ((<VariableDeclarationList>ctorDeclaration.parent).declarations.length === 1) {
|
||||||
copyComments(precedingNode, newClassDeclaration!, sourceFile); // TODO: GH#18217
|
copyLeadingComments(precedingNode, newClassDeclaration!, sourceFile); // TODO: GH#18217
|
||||||
changes.delete(sourceFile, precedingNode);
|
changes.delete(sourceFile, precedingNode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -48,7 +48,7 @@ namespace ts.codefix {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyComments(ctorDeclaration, newClassDeclaration, sourceFile);
|
copyLeadingComments(ctorDeclaration, newClassDeclaration, sourceFile);
|
||||||
|
|
||||||
// Because the preceding node could be touched, we need to insert nodes before delete nodes.
|
// Because the preceding node could be touched, we need to insert nodes before delete nodes.
|
||||||
changes.insertNodeAfter(sourceFile, precedingNode!, newClassDeclaration);
|
changes.insertNodeAfter(sourceFile, precedingNode!, newClassDeclaration);
|
||||||
|
@ -112,7 +112,7 @@ namespace ts.codefix {
|
||||||
const fullModifiers = concatenate(modifiers, getModifierKindFromSource(functionExpression, SyntaxKind.AsyncKeyword));
|
const fullModifiers = concatenate(modifiers, getModifierKindFromSource(functionExpression, SyntaxKind.AsyncKeyword));
|
||||||
const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
|
const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
|
||||||
/*typeParameters*/ undefined, functionExpression.parameters, /*type*/ undefined, functionExpression.body);
|
/*typeParameters*/ undefined, functionExpression.parameters, /*type*/ undefined, functionExpression.body);
|
||||||
copyComments(assignmentBinaryExpression, method, sourceFile);
|
copyLeadingComments(assignmentBinaryExpression, method, sourceFile);
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ namespace ts.codefix {
|
||||||
const fullModifiers = concatenate(modifiers, getModifierKindFromSource(arrowFunction, SyntaxKind.AsyncKeyword));
|
const fullModifiers = concatenate(modifiers, getModifierKindFromSource(arrowFunction, SyntaxKind.AsyncKeyword));
|
||||||
const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
|
const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
|
||||||
/*typeParameters*/ undefined, arrowFunction.parameters, /*type*/ undefined, bodyBlock);
|
/*typeParameters*/ undefined, arrowFunction.parameters, /*type*/ undefined, bodyBlock);
|
||||||
copyComments(assignmentBinaryExpression, method, sourceFile);
|
copyLeadingComments(assignmentBinaryExpression, method, sourceFile);
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ namespace ts.codefix {
|
||||||
}
|
}
|
||||||
const prop = createProperty(/*decorators*/ undefined, modifiers, memberDeclaration.name, /*questionToken*/ undefined,
|
const prop = createProperty(/*decorators*/ undefined, modifiers, memberDeclaration.name, /*questionToken*/ undefined,
|
||||||
/*type*/ undefined, assignmentBinaryExpression.right);
|
/*type*/ undefined, assignmentBinaryExpression.right);
|
||||||
copyComments(assignmentBinaryExpression.parent, prop, sourceFile);
|
copyLeadingComments(assignmentBinaryExpression.parent, prop, sourceFile);
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,30 +304,6 @@ namespace ts.codefix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined {
|
|
||||||
const checker = program.getTypeChecker();
|
|
||||||
let typeIsAccessible = true;
|
|
||||||
const notAccessible = () => { typeIsAccessible = false; };
|
|
||||||
const res = checker.typeToTypeNode(type, enclosingScope, /*flags*/ undefined, {
|
|
||||||
trackSymbol: (symbol, declaration, meaning) => {
|
|
||||||
// TODO: GH#18217
|
|
||||||
typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning!, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible;
|
|
||||||
},
|
|
||||||
reportInaccessibleThisError: notAccessible,
|
|
||||||
reportPrivateInBaseOfClassExpression: notAccessible,
|
|
||||||
reportInaccessibleUniqueSymbolError: notAccessible,
|
|
||||||
moduleResolverHost: {
|
|
||||||
readFile: host.readFile,
|
|
||||||
fileExists: host.fileExists,
|
|
||||||
directoryExists: host.directoryExists,
|
|
||||||
getSourceFiles: program.getSourceFiles,
|
|
||||||
getCurrentDirectory: program.getCurrentDirectory,
|
|
||||||
getCommonSourceDirectory: program.getCommonSourceDirectory,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return typeIsAccessible ? res : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>, program: Program, cancellationToken: CancellationToken): ReadonlyArray<Identifier> {
|
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>, program: Program, cancellationToken: CancellationToken): ReadonlyArray<Identifier> {
|
||||||
// Position shouldn't matter since token is not a SourceFile.
|
// Position shouldn't matter since token is not a SourceFile.
|
||||||
return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry =>
|
return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry =>
|
||||||
|
|
|
@ -68,8 +68,8 @@ namespace ts.OrganizeImports {
|
||||||
else {
|
else {
|
||||||
// Note: Delete the surrounding trivia because it will have been retained in newImportDecls.
|
// Note: Delete the surrounding trivia because it will have been retained in newImportDecls.
|
||||||
changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, {
|
changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, {
|
||||||
useNonAdjustedStartPosition: true, // Leave header comment in place
|
leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, // Leave header comment in place
|
||||||
useNonAdjustedEndPosition: false,
|
trailingTriviaOption: textChanges.TrailingTriviaOption.Include,
|
||||||
suffix: getNewLineOrDefaultFromHost(host, formatContext.options),
|
suffix: getNewLineOrDefaultFromHost(host, formatContext.options),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,13 +48,13 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction {
|
||||||
const returnStatement = createReturn(expression);
|
const returnStatement = createReturn(expression);
|
||||||
body = createBlock([returnStatement], /* multiLine */ true);
|
body = createBlock([returnStatement], /* multiLine */ true);
|
||||||
suppressLeadingAndTrailingTrivia(body);
|
suppressLeadingAndTrailingTrivia(body);
|
||||||
copyComments(expression!, returnStatement, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ true);
|
copyLeadingComments(expression!, returnStatement, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ true);
|
||||||
}
|
}
|
||||||
else if (actionName === removeBracesActionName && returnStatement) {
|
else if (actionName === removeBracesActionName && returnStatement) {
|
||||||
const actualExpression = expression || createVoidZero();
|
const actualExpression = expression || createVoidZero();
|
||||||
body = needsParentheses(actualExpression) ? createParen(actualExpression) : actualExpression;
|
body = needsParentheses(actualExpression) ? createParen(actualExpression) : actualExpression;
|
||||||
suppressLeadingAndTrailingTrivia(body);
|
suppressLeadingAndTrailingTrivia(body);
|
||||||
copyComments(returnStatement, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
|
copyLeadingComments(returnStatement, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Debug.fail("invalid action");
|
Debug.fail("invalid action");
|
||||||
|
|
520
src/services/refactors/convertToNamedParameters.ts
Normal file
520
src/services/refactors/convertToNamedParameters.ts
Normal file
|
@ -0,0 +1,520 @@
|
||||||
|
/* @internal */
|
||||||
|
namespace ts.refactor.convertToNamedParameters {
|
||||||
|
const refactorName = "Convert to named parameters";
|
||||||
|
const minimumParameterLength = 2;
|
||||||
|
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||||
|
|
||||||
|
|
||||||
|
function getAvailableActions(context: RefactorContext): ReadonlyArray<ApplicableRefactorInfo> {
|
||||||
|
const { file, startPosition } = context;
|
||||||
|
const isJSFile = isSourceFileJS(file);
|
||||||
|
if (isJSFile) return emptyArray; // TODO: GH#30113
|
||||||
|
const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker());
|
||||||
|
if (!functionDeclaration) return emptyArray;
|
||||||
|
|
||||||
|
const description = getLocaleSpecificMessage(Diagnostics.Convert_to_named_parameters);
|
||||||
|
return [{
|
||||||
|
name: refactorName,
|
||||||
|
description,
|
||||||
|
actions: [{
|
||||||
|
name: refactorName,
|
||||||
|
description
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined {
|
||||||
|
Debug.assert(actionName === refactorName);
|
||||||
|
const { file, startPosition, program, cancellationToken, host } = context;
|
||||||
|
const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, program.getTypeChecker());
|
||||||
|
if (!functionDeclaration || !cancellationToken) return undefined;
|
||||||
|
|
||||||
|
const groupedReferences = getGroupedReferences(functionDeclaration, program, cancellationToken);
|
||||||
|
if (groupedReferences.valid) {
|
||||||
|
const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, host, t, functionDeclaration, groupedReferences));
|
||||||
|
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { edits: [] }; // TODO: GH#30113
|
||||||
|
}
|
||||||
|
|
||||||
|
function doChange(
|
||||||
|
sourceFile: SourceFile,
|
||||||
|
program: Program,
|
||||||
|
host: LanguageServiceHost,
|
||||||
|
changes: textChanges.ChangeTracker,
|
||||||
|
functionDeclaration: ValidFunctionDeclaration,
|
||||||
|
groupedReferences: GroupedReferences): void {
|
||||||
|
const newParamDeclaration = map(createNewParameters(functionDeclaration, program, host), param => getSynthesizedDeepClone(param));
|
||||||
|
changes.replaceNodeRangeWithNodes(
|
||||||
|
sourceFile,
|
||||||
|
first(functionDeclaration.parameters),
|
||||||
|
last(functionDeclaration.parameters),
|
||||||
|
newParamDeclaration,
|
||||||
|
{ joiner: ", ",
|
||||||
|
// indentation is set to 0 because otherwise the object parameter will be indented if there is a `this` parameter
|
||||||
|
indentation: 0,
|
||||||
|
leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll,
|
||||||
|
trailingTriviaOption: textChanges.TrailingTriviaOption.Include
|
||||||
|
});
|
||||||
|
|
||||||
|
const functionCalls = sortAndDeduplicate(groupedReferences.functionCalls, /*comparer*/ (a, b) => compareValues(a.pos, b.pos));
|
||||||
|
for (const call of functionCalls) {
|
||||||
|
if (call.arguments && call.arguments.length) {
|
||||||
|
const newArgument = getSynthesizedDeepClone(createNewArgument(functionDeclaration, call.arguments), /*includeTrivia*/ true);
|
||||||
|
changes.replaceNodeRange(
|
||||||
|
getSourceFileOfNode(call),
|
||||||
|
first(call.arguments),
|
||||||
|
last(call.arguments),
|
||||||
|
newArgument,
|
||||||
|
{ leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, trailingTriviaOption: textChanges.TrailingTriviaOption.Include });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, program: Program, cancellationToken: CancellationToken): GroupedReferences {
|
||||||
|
const functionNames = getFunctionNames(functionDeclaration);
|
||||||
|
const classNames = isConstructorDeclaration(functionDeclaration) ? getClassNames(functionDeclaration) : [];
|
||||||
|
const names = deduplicate([...functionNames, ...classNames], equateValues);
|
||||||
|
const checker = program.getTypeChecker();
|
||||||
|
|
||||||
|
const references = flatMap(names, /*mapfn*/ name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken));
|
||||||
|
const groupedReferences = groupReferences(references);
|
||||||
|
|
||||||
|
if (!every(groupedReferences.declarations, decl => contains(names, decl))) {
|
||||||
|
groupedReferences.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupedReferences;
|
||||||
|
|
||||||
|
function groupReferences(referenceEntries: ReadonlyArray<FindAllReferences.Entry>): GroupedReferences {
|
||||||
|
const classReferences: ClassReferences = { accessExpressions: [], typeUsages: [] };
|
||||||
|
const groupedReferences: GroupedReferences = { functionCalls: [], declarations: [], classReferences, valid: true };
|
||||||
|
const functionSymbols = map(functionNames, checker.getSymbolAtLocation);
|
||||||
|
const classSymbols = map(classNames, checker.getSymbolAtLocation);
|
||||||
|
const isConstructor = isConstructorDeclaration(functionDeclaration);
|
||||||
|
|
||||||
|
for (const entry of referenceEntries) {
|
||||||
|
if (entry.kind !== FindAllReferences.EntryKind.Node) {
|
||||||
|
groupedReferences.valid = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (contains(functionSymbols, checker.getSymbolAtLocation(entry.node), symbolComparer)) {
|
||||||
|
const decl = entryToDeclaration(entry);
|
||||||
|
if (decl) {
|
||||||
|
groupedReferences.declarations.push(decl);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const call = entryToFunctionCall(entry);
|
||||||
|
if (call) {
|
||||||
|
groupedReferences.functionCalls.push(call);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the refactored function is a constructor, we must also check if the references to its class are valid
|
||||||
|
if (isConstructor && contains(classSymbols, checker.getSymbolAtLocation(entry.node), symbolComparer)) {
|
||||||
|
const decl = entryToDeclaration(entry);
|
||||||
|
if (decl) {
|
||||||
|
groupedReferences.declarations.push(decl);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessExpression = entryToAccessExpression(entry);
|
||||||
|
if (accessExpression) {
|
||||||
|
classReferences.accessExpressions.push(accessExpression);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only class declarations are allowed to be used as a type (in a heritage clause),
|
||||||
|
// otherwise `findAllReferences` might not be able to track constructor calls.
|
||||||
|
if (isClassDeclaration(functionDeclaration.parent)) {
|
||||||
|
const type = entryToType(entry);
|
||||||
|
if (type) {
|
||||||
|
classReferences.typeUsages.push(type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groupedReferences.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupedReferences;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function symbolComparer(a: Symbol, b: Symbol): boolean {
|
||||||
|
return getSymbolTarget(a) === getSymbolTarget(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryToDeclaration(entry: FindAllReferences.NodeEntry): Node | undefined {
|
||||||
|
if (isDeclaration(entry.node.parent)) {
|
||||||
|
return entry.node;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryToFunctionCall(entry: FindAllReferences.NodeEntry): CallExpression | NewExpression | undefined {
|
||||||
|
if (entry.node.parent) {
|
||||||
|
const functionReference = entry.node;
|
||||||
|
const parent = functionReference.parent;
|
||||||
|
switch (parent.kind) {
|
||||||
|
// Function call (foo(...) or super(...))
|
||||||
|
case SyntaxKind.CallExpression:
|
||||||
|
const callExpression = tryCast(parent, isCallExpression);
|
||||||
|
if (callExpression && callExpression.expression === functionReference) {
|
||||||
|
return callExpression;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// Constructor call (new Foo(...))
|
||||||
|
case SyntaxKind.NewExpression:
|
||||||
|
const newExpression = tryCast(parent, isNewExpression);
|
||||||
|
if (newExpression && newExpression.expression === functionReference) {
|
||||||
|
return newExpression;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// Method call (x.foo(...))
|
||||||
|
case SyntaxKind.PropertyAccessExpression:
|
||||||
|
const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression);
|
||||||
|
if (propertyAccessExpression && propertyAccessExpression.parent && propertyAccessExpression.name === functionReference) {
|
||||||
|
const callExpression = tryCast(propertyAccessExpression.parent, isCallExpression);
|
||||||
|
if (callExpression && callExpression.expression === propertyAccessExpression) {
|
||||||
|
return callExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// Method call (x["foo"](...))
|
||||||
|
case SyntaxKind.ElementAccessExpression:
|
||||||
|
const elementAccessExpression = tryCast(parent, isElementAccessExpression);
|
||||||
|
if (elementAccessExpression && elementAccessExpression.parent && elementAccessExpression.argumentExpression === functionReference) {
|
||||||
|
const callExpression = tryCast(elementAccessExpression.parent, isCallExpression);
|
||||||
|
if (callExpression && callExpression.expression === elementAccessExpression) {
|
||||||
|
return callExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryToAccessExpression(entry: FindAllReferences.NodeEntry): ElementAccessExpression | PropertyAccessExpression | undefined {
|
||||||
|
if (entry.node.parent) {
|
||||||
|
const reference = entry.node;
|
||||||
|
const parent = reference.parent;
|
||||||
|
switch (parent.kind) {
|
||||||
|
// `C.foo`
|
||||||
|
case SyntaxKind.PropertyAccessExpression:
|
||||||
|
const propertyAccessExpression = tryCast(parent, isPropertyAccessExpression);
|
||||||
|
if (propertyAccessExpression && propertyAccessExpression.expression === reference) {
|
||||||
|
return propertyAccessExpression;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// `C["foo"]`
|
||||||
|
case SyntaxKind.ElementAccessExpression:
|
||||||
|
const elementAccessExpression = tryCast(parent, isElementAccessExpression);
|
||||||
|
if (elementAccessExpression && elementAccessExpression.expression === reference) {
|
||||||
|
return elementAccessExpression;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryToType(entry: FindAllReferences.NodeEntry): Node | undefined {
|
||||||
|
const reference = entry.node;
|
||||||
|
if (getMeaningFromLocation(reference) === SemanticMeaning.Type || isExpressionWithTypeArgumentsInClassExtendsClause(reference.parent)) {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFunctionDeclarationAtPosition(file: SourceFile, startPosition: number, checker: TypeChecker): ValidFunctionDeclaration | undefined {
|
||||||
|
const node = getTouchingToken(file, startPosition);
|
||||||
|
const functionDeclaration = getContainingFunction(node);
|
||||||
|
if (functionDeclaration
|
||||||
|
&& isValidFunctionDeclaration(functionDeclaration, checker)
|
||||||
|
&& rangeContainsRange(functionDeclaration, node)
|
||||||
|
&& !(functionDeclaration.body && rangeContainsRange(functionDeclaration.body, node))) return functionDeclaration;
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidFunctionDeclaration(functionDeclaration: SignatureDeclaration, checker: TypeChecker): functionDeclaration is ValidFunctionDeclaration {
|
||||||
|
if (!isValidParameterNodeArray(functionDeclaration.parameters)) return false;
|
||||||
|
switch (functionDeclaration.kind) {
|
||||||
|
case SyntaxKind.FunctionDeclaration:
|
||||||
|
case SyntaxKind.MethodDeclaration:
|
||||||
|
return !!functionDeclaration.name && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration);
|
||||||
|
case SyntaxKind.Constructor:
|
||||||
|
if (isClassDeclaration(functionDeclaration.parent)) {
|
||||||
|
return !!functionDeclaration.body && !!functionDeclaration.parent.name && !checker.isImplementationOfOverload(functionDeclaration);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return isValidVariableDeclaration(functionDeclaration.parent.parent) && !!functionDeclaration.body && !checker.isImplementationOfOverload(functionDeclaration);
|
||||||
|
}
|
||||||
|
case SyntaxKind.FunctionExpression:
|
||||||
|
case SyntaxKind.ArrowFunction:
|
||||||
|
return isValidVariableDeclaration(functionDeclaration.parent);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidParameterNodeArray(parameters: NodeArray<ParameterDeclaration>): parameters is ValidParameterNodeArray {
|
||||||
|
return getRefactorableParametersLength(parameters) >= minimumParameterLength && every(parameters, isValidParameterDeclaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidParameterDeclaration(paramDeclaration: ParameterDeclaration): paramDeclaration is ValidParameterDeclaration {
|
||||||
|
return !paramDeclaration.modifiers && !paramDeclaration.decorators && isIdentifier(paramDeclaration.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidVariableDeclaration(node: Node): node is ValidVariableDeclaration {
|
||||||
|
return isVariableDeclaration(node) && isVarConst(node) && isIdentifier(node.name) && !node.type; // TODO: GH#30113
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasThisParameter(parameters: NodeArray<ParameterDeclaration>): boolean {
|
||||||
|
return parameters.length > 0 && isThis(parameters[0].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRefactorableParametersLength(parameters: NodeArray<ParameterDeclaration>): number {
|
||||||
|
if (hasThisParameter(parameters)) {
|
||||||
|
return parameters.length - 1;
|
||||||
|
}
|
||||||
|
return parameters.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRefactorableParameters(parameters: NodeArray<ValidParameterDeclaration>): NodeArray<ValidParameterDeclaration> {
|
||||||
|
if (hasThisParameter(parameters)) {
|
||||||
|
parameters = createNodeArray(parameters.slice(1), parameters.hasTrailingComma);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewArgument(functionDeclaration: ValidFunctionDeclaration, functionArguments: NodeArray<Expression>): ObjectLiteralExpression {
|
||||||
|
const parameters = getRefactorableParameters(functionDeclaration.parameters);
|
||||||
|
const hasRestParameter = isRestParameter(last(parameters));
|
||||||
|
const nonRestArguments = hasRestParameter ? functionArguments.slice(0, parameters.length - 1) : functionArguments;
|
||||||
|
const properties = map(nonRestArguments, (arg, i) => {
|
||||||
|
const property = createPropertyAssignment(getParameterName(parameters[i]), arg);
|
||||||
|
suppressLeadingAndTrailingTrivia(property.initializer);
|
||||||
|
copyComments(arg, property);
|
||||||
|
return property;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasRestParameter && functionArguments.length >= parameters.length) {
|
||||||
|
const restArguments = functionArguments.slice(parameters.length - 1);
|
||||||
|
const restProperty = createPropertyAssignment(getParameterName(last(parameters)), createArrayLiteral(restArguments));
|
||||||
|
properties.push(restProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectLiteral = createObjectLiteral(properties, /*multiLine*/ false);
|
||||||
|
return objectLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewParameters(functionDeclaration: ValidFunctionDeclaration, program: Program, host: LanguageServiceHost): NodeArray<ParameterDeclaration> {
|
||||||
|
const refactorableParameters = getRefactorableParameters(functionDeclaration.parameters);
|
||||||
|
const bindingElements = map(refactorableParameters, createBindingElementFromParameterDeclaration);
|
||||||
|
const objectParameterName = createObjectBindingPattern(bindingElements);
|
||||||
|
const objectParameterType = createParameterTypeNode(refactorableParameters);
|
||||||
|
const checker = program.getTypeChecker();
|
||||||
|
|
||||||
|
let objectInitializer: Expression | undefined;
|
||||||
|
// If every parameter in the original function was optional, add an empty object initializer to the new object parameter
|
||||||
|
if (every(refactorableParameters, checker.isOptionalParameter)) {
|
||||||
|
objectInitializer = createObjectLiteral();
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectParameter = createParameter(
|
||||||
|
/*decorators*/ undefined,
|
||||||
|
/*modifiers*/ undefined,
|
||||||
|
/*dotDotDotToken*/ undefined,
|
||||||
|
objectParameterName,
|
||||||
|
/*questionToken*/ undefined,
|
||||||
|
objectParameterType,
|
||||||
|
objectInitializer);
|
||||||
|
|
||||||
|
if (hasThisParameter(functionDeclaration.parameters)) {
|
||||||
|
const thisParameter = functionDeclaration.parameters[0];
|
||||||
|
const newThisParameter = createParameter(
|
||||||
|
/*decorators*/ undefined,
|
||||||
|
/*modifiers*/ undefined,
|
||||||
|
/*dotDotDotToken*/ undefined,
|
||||||
|
thisParameter.name,
|
||||||
|
/*questionToken*/ undefined,
|
||||||
|
thisParameter.type);
|
||||||
|
|
||||||
|
suppressLeadingAndTrailingTrivia(newThisParameter.name);
|
||||||
|
copyComments(thisParameter.name, newThisParameter.name);
|
||||||
|
if (thisParameter.type) {
|
||||||
|
suppressLeadingAndTrailingTrivia(newThisParameter.type!);
|
||||||
|
copyComments(thisParameter.type, newThisParameter.type!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createNodeArray([newThisParameter, objectParameter]);
|
||||||
|
}
|
||||||
|
return createNodeArray([objectParameter]);
|
||||||
|
|
||||||
|
function createParameterTypeNode(parameters: NodeArray<ValidParameterDeclaration>): TypeLiteralNode {
|
||||||
|
const members = map(parameters, createPropertySignatureFromParameterDeclaration);
|
||||||
|
const typeNode = addEmitFlags(createTypeLiteralNode(members), EmitFlags.SingleLine);
|
||||||
|
return typeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPropertySignatureFromParameterDeclaration(parameterDeclaration: ValidParameterDeclaration): PropertySignature {
|
||||||
|
let parameterType = parameterDeclaration.type;
|
||||||
|
if (!parameterType && (parameterDeclaration.initializer || isRestParameter(parameterDeclaration))) {
|
||||||
|
parameterType = getTypeNode(parameterDeclaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
const propertySignature = createPropertySignature(
|
||||||
|
/*modifiers*/ undefined,
|
||||||
|
getParameterName(parameterDeclaration),
|
||||||
|
parameterDeclaration.initializer || isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : parameterDeclaration.questionToken,
|
||||||
|
parameterType,
|
||||||
|
/*initializer*/ undefined);
|
||||||
|
|
||||||
|
suppressLeadingAndTrailingTrivia(propertySignature);
|
||||||
|
copyComments(parameterDeclaration.name, propertySignature.name);
|
||||||
|
if (parameterDeclaration.type && propertySignature.type) {
|
||||||
|
copyComments(parameterDeclaration.type, propertySignature.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertySignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypeNode(node: Node): TypeNode | undefined {
|
||||||
|
const checker = program.getTypeChecker();
|
||||||
|
const type = checker.getTypeAtLocation(node);
|
||||||
|
return getTypeNodeIfAccessible(type, node, program, host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBindingElementFromParameterDeclaration(parameterDeclaration: ValidParameterDeclaration): BindingElement {
|
||||||
|
const element = createBindingElement(
|
||||||
|
/*dotDotDotToken*/ undefined,
|
||||||
|
/*propertyName*/ undefined,
|
||||||
|
getParameterName(parameterDeclaration),
|
||||||
|
isRestParameter(parameterDeclaration) ? createArrayLiteral() : parameterDeclaration.initializer);
|
||||||
|
|
||||||
|
suppressLeadingAndTrailingTrivia(element);
|
||||||
|
if (parameterDeclaration.initializer && element.initializer) {
|
||||||
|
copyComments(parameterDeclaration.initializer, element.initializer);
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyComments(sourceNode: Node, targetNode: Node) {
|
||||||
|
const sourceFile = sourceNode.getSourceFile();
|
||||||
|
const text = sourceFile.text;
|
||||||
|
if (hasLeadingLineBreak(sourceNode, text)) {
|
||||||
|
copyLeadingComments(sourceNode, targetNode, sourceFile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copyTrailingAsLeadingComments(sourceNode, targetNode, sourceFile);
|
||||||
|
}
|
||||||
|
copyTrailingComments(sourceNode, targetNode, sourceFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasLeadingLineBreak(node: Node, text: string) {
|
||||||
|
const start = node.getFullStart();
|
||||||
|
const end = node.getStart();
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParameterName(paramDeclaration: ValidParameterDeclaration) {
|
||||||
|
return getTextOfIdentifierOrLiteral(paramDeclaration.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClassNames(constructorDeclaration: ValidConstructor): Identifier[] {
|
||||||
|
switch (constructorDeclaration.parent.kind) {
|
||||||
|
case SyntaxKind.ClassDeclaration:
|
||||||
|
const classDeclaration = constructorDeclaration.parent;
|
||||||
|
return [classDeclaration.name];
|
||||||
|
case SyntaxKind.ClassExpression:
|
||||||
|
const classExpression = constructorDeclaration.parent;
|
||||||
|
const variableDeclaration = constructorDeclaration.parent.parent;
|
||||||
|
const className = classExpression.name;
|
||||||
|
if (className) return [className, variableDeclaration.name];
|
||||||
|
return [variableDeclaration.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFunctionNames(functionDeclaration: ValidFunctionDeclaration): Node[] {
|
||||||
|
switch (functionDeclaration.kind) {
|
||||||
|
case SyntaxKind.FunctionDeclaration:
|
||||||
|
case SyntaxKind.MethodDeclaration:
|
||||||
|
return [functionDeclaration.name];
|
||||||
|
case SyntaxKind.Constructor:
|
||||||
|
const ctrKeyword = findChildOfKind(functionDeclaration, SyntaxKind.ConstructorKeyword, functionDeclaration.getSourceFile())!;
|
||||||
|
if (functionDeclaration.parent.kind === SyntaxKind.ClassExpression) {
|
||||||
|
const variableDeclaration = functionDeclaration.parent.parent;
|
||||||
|
return [variableDeclaration.name, ctrKeyword];
|
||||||
|
}
|
||||||
|
return [ctrKeyword];
|
||||||
|
case SyntaxKind.ArrowFunction:
|
||||||
|
return [functionDeclaration.parent.name];
|
||||||
|
case SyntaxKind.FunctionExpression:
|
||||||
|
if (functionDeclaration.name) return [functionDeclaration.name, functionDeclaration.parent.name];
|
||||||
|
return [functionDeclaration.parent.name];
|
||||||
|
default:
|
||||||
|
return Debug.assertNever(functionDeclaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidParameterNodeArray = NodeArray<ValidParameterDeclaration>;
|
||||||
|
|
||||||
|
interface ValidVariableDeclaration extends VariableDeclaration {
|
||||||
|
name: Identifier;
|
||||||
|
type: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValidConstructor extends ConstructorDeclaration {
|
||||||
|
parent: (ClassDeclaration & { name: Identifier }) | (ClassExpression & { parent: ValidVariableDeclaration });
|
||||||
|
parameters: NodeArray<ValidParameterDeclaration>;
|
||||||
|
body: FunctionBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValidFunction extends FunctionDeclaration {
|
||||||
|
name: Identifier;
|
||||||
|
parameters: NodeArray<ValidParameterDeclaration>;
|
||||||
|
body: FunctionBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValidMethod extends MethodDeclaration {
|
||||||
|
parameters: NodeArray<ValidParameterDeclaration>;
|
||||||
|
body: FunctionBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValidFunctionExpression extends FunctionExpression {
|
||||||
|
parent: ValidVariableDeclaration;
|
||||||
|
parameters: NodeArray<ValidParameterDeclaration>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ValidArrowFunction extends ArrowFunction {
|
||||||
|
parent: ValidVariableDeclaration;
|
||||||
|
parameters: NodeArray<ValidParameterDeclaration>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidFunctionDeclaration = ValidConstructor | ValidFunction | ValidMethod | ValidArrowFunction | ValidFunctionExpression;
|
||||||
|
|
||||||
|
interface ValidParameterDeclaration extends ParameterDeclaration {
|
||||||
|
name: Identifier;
|
||||||
|
modifiers: undefined;
|
||||||
|
decorators: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GroupedReferences {
|
||||||
|
functionCalls: (CallExpression | NewExpression)[];
|
||||||
|
declarations: Node[];
|
||||||
|
classReferences?: ClassReferences;
|
||||||
|
valid: boolean;
|
||||||
|
}
|
||||||
|
interface ClassReferences {
|
||||||
|
accessExpressions: Node[];
|
||||||
|
typeUsages: Node[];
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,17 +28,27 @@ namespace ts.textChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigurableStart {
|
export interface ConfigurableStart {
|
||||||
/** True to use getStart() (NB, not getFullStart()) without adjustment. */
|
leadingTriviaOption?: LeadingTriviaOption;
|
||||||
useNonAdjustedStartPosition?: boolean;
|
|
||||||
}
|
}
|
||||||
export interface ConfigurableEnd {
|
export interface ConfigurableEnd {
|
||||||
/** True to use getEnd() without adjustment. */
|
trailingTriviaOption?: TrailingTriviaOption;
|
||||||
useNonAdjustedEndPosition?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Position {
|
export enum LeadingTriviaOption {
|
||||||
FullStart,
|
/** Exclude all leading trivia (use getStart()) */
|
||||||
Start
|
Exclude,
|
||||||
|
/** Include leading trivia and,
|
||||||
|
* if there are no line breaks between the node and the previous token,
|
||||||
|
* include all trivia between the node and the previous token
|
||||||
|
*/
|
||||||
|
IncludeAll,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TrailingTriviaOption {
|
||||||
|
/** Exclude all trailing trivia (use getEnd()) */
|
||||||
|
Exclude,
|
||||||
|
/** Include trailing trivia */
|
||||||
|
Include,
|
||||||
}
|
}
|
||||||
|
|
||||||
function skipWhitespacesAndLineBreaks(text: string, start: number) {
|
function skipWhitespacesAndLineBreaks(text: string, start: number) {
|
||||||
|
@ -68,13 +78,14 @@ namespace ts.textChanges {
|
||||||
* Usually leading trivia of the variable declaration 'y' should not include trailing trivia (whitespace, comment 'this is x' and newline) from the preceding
|
* Usually leading trivia of the variable declaration 'y' should not include trailing trivia (whitespace, comment 'this is x' and newline) from the preceding
|
||||||
* variable declaration and trailing trivia for 'y' should include (whitespace, comment 'this is y', newline).
|
* variable declaration and trailing trivia for 'y' should include (whitespace, comment 'this is y', newline).
|
||||||
* By default when removing nodes we adjust start and end positions to respect specification of the trivia above.
|
* By default when removing nodes we adjust start and end positions to respect specification of the trivia above.
|
||||||
* If pos\end should be interpreted literally 'useNonAdjustedStartPosition' or 'useNonAdjustedEndPosition' should be set to true
|
* If pos\end should be interpreted literally (that is, withouth including leading and trailing trivia), `leadingTriviaOption` should be set to `LeadingTriviaOption.Exclude`
|
||||||
|
* and `trailingTriviaOption` to `TrailingTriviaOption.Exclude`.
|
||||||
*/
|
*/
|
||||||
export interface ConfigurableStartEnd extends ConfigurableStart, ConfigurableEnd {}
|
export interface ConfigurableStartEnd extends ConfigurableStart, ConfigurableEnd {}
|
||||||
|
|
||||||
export const useNonAdjustedPositions: ConfigurableStartEnd = {
|
const useNonAdjustedPositions: ConfigurableStartEnd = {
|
||||||
useNonAdjustedStartPosition: true,
|
leadingTriviaOption: LeadingTriviaOption.Exclude,
|
||||||
useNonAdjustedEndPosition: true,
|
trailingTriviaOption: TrailingTriviaOption.Exclude,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface InsertNodeOptions {
|
export interface InsertNodeOptions {
|
||||||
|
@ -143,11 +154,12 @@ namespace ts.textChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd): TextRange {
|
function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd): TextRange {
|
||||||
return { pos: getAdjustedStartPosition(sourceFile, startNode, options, Position.Start), end: getAdjustedEndPosition(sourceFile, endNode, options) };
|
return { pos: getAdjustedStartPosition(sourceFile, startNode, options), end: getAdjustedEndPosition(sourceFile, endNode, options) };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart, position: Position) {
|
function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart) {
|
||||||
if (options.useNonAdjustedStartPosition) {
|
const { leadingTriviaOption } = options;
|
||||||
|
if (leadingTriviaOption === LeadingTriviaOption.Exclude) {
|
||||||
return node.getStart(sourceFile);
|
return node.getStart(sourceFile);
|
||||||
}
|
}
|
||||||
const fullStart = node.getFullStart();
|
const fullStart = node.getFullStart();
|
||||||
|
@ -165,7 +177,7 @@ namespace ts.textChanges {
|
||||||
// fullstart
|
// fullstart
|
||||||
// when b is replaced - we usually want to keep the leading trvia
|
// when b is replaced - we usually want to keep the leading trvia
|
||||||
// when b is deleted - we delete it
|
// when b is deleted - we delete it
|
||||||
return position === Position.Start ? start : fullStart;
|
return leadingTriviaOption === LeadingTriviaOption.IncludeAll ? fullStart : start;
|
||||||
}
|
}
|
||||||
// get start position of the line following the line that contains fullstart position
|
// get start position of the line following the line that contains fullstart position
|
||||||
// (but only if the fullstart isn't the very beginning of the file)
|
// (but only if the fullstart isn't the very beginning of the file)
|
||||||
|
@ -178,11 +190,12 @@ namespace ts.textChanges {
|
||||||
|
|
||||||
function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) {
|
function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) {
|
||||||
const { end } = node;
|
const { end } = node;
|
||||||
if (options.useNonAdjustedEndPosition || isExpression(node)) {
|
const { trailingTriviaOption } = options;
|
||||||
|
if (trailingTriviaOption === TrailingTriviaOption.Exclude || (isExpression(node) && trailingTriviaOption !== TrailingTriviaOption.Include)) {
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true);
|
const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true);
|
||||||
return newEnd !== end && isLineBreak(sourceFile.text.charCodeAt(newEnd - 1))
|
return newEnd !== end && (trailingTriviaOption === TrailingTriviaOption.Include || isLineBreak(sourceFile.text.charCodeAt(newEnd - 1)))
|
||||||
? newEnd
|
? newEnd
|
||||||
: end;
|
: end;
|
||||||
}
|
}
|
||||||
|
@ -240,15 +253,15 @@ namespace ts.textChanges {
|
||||||
this.deleteRange(sourceFile, { pos: modifier.getStart(sourceFile), end: skipTrivia(sourceFile.text, modifier.end, /*stopAfterLineBreak*/ true) });
|
this.deleteRange(sourceFile, { pos: modifier.getStart(sourceFile), end: skipTrivia(sourceFile.text, modifier.end, /*stopAfterLineBreak*/ true) });
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}): void {
|
public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void {
|
||||||
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart);
|
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options);
|
||||||
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||||
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = {}): void {
|
public deleteNodeRangeExcludingEnd(sourceFile: SourceFile, startNode: Node, afterEndNode: Node | undefined, options: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void {
|
||||||
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart);
|
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options);
|
||||||
const endPosition = afterEndNode === undefined ? sourceFile.text.length : getAdjustedStartPosition(sourceFile, afterEndNode, options, Position.FullStart);
|
const endPosition = afterEndNode === undefined ? sourceFile.text.length : getAdjustedStartPosition(sourceFile, afterEndNode, options);
|
||||||
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +320,7 @@ namespace ts.textChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false): void {
|
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false): void {
|
||||||
this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}, Position.Start), newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
|
this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}), newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
|
public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
|
||||||
|
@ -427,7 +440,7 @@ namespace ts.textChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void {
|
public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void {
|
||||||
const pos = getAdjustedStartPosition(sourceFile, scope.getLastToken()!, {}, Position.Start);
|
const pos = getAdjustedStartPosition(sourceFile, scope.getLastToken()!, {});
|
||||||
this.insertNodeAt(sourceFile, pos, newNode, {
|
this.insertNodeAt(sourceFile, pos, newNode, {
|
||||||
prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken()!.pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter,
|
prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken()!.pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter,
|
||||||
suffix: this.newLineCharacter
|
suffix: this.newLineCharacter
|
||||||
|
@ -736,7 +749,7 @@ namespace ts.textChanges {
|
||||||
|
|
||||||
// find first non-whitespace position in the leading trivia of the node
|
// find first non-whitespace position in the leading trivia of the node
|
||||||
function startPositionToDeleteNodeInList(sourceFile: SourceFile, node: Node): number {
|
function startPositionToDeleteNodeInList(sourceFile: SourceFile, node: Node): number {
|
||||||
return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, {}, Position.FullStart), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, { leadingTriviaOption: LeadingTriviaOption.IncludeAll }), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number, number] {
|
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number, number] {
|
||||||
|
@ -1090,7 +1103,7 @@ namespace ts.textChanges {
|
||||||
case SyntaxKind.ImportDeclaration:
|
case SyntaxKind.ImportDeclaration:
|
||||||
deleteNode(changes, sourceFile, node,
|
deleteNode(changes, sourceFile, node,
|
||||||
// For first import, leave header comment in place
|
// For first import, leave header comment in place
|
||||||
node === sourceFile.imports[0].parent ? { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: false } : undefined);
|
node === sourceFile.imports[0].parent ? { leadingTriviaOption: LeadingTriviaOption.Exclude } : undefined);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SyntaxKind.BindingElement:
|
case SyntaxKind.BindingElement:
|
||||||
|
@ -1134,7 +1147,7 @@ namespace ts.textChanges {
|
||||||
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deleteNode(changes, sourceFile, node, node.kind === SyntaxKind.SemicolonToken ? { useNonAdjustedEndPosition: true } : undefined);
|
deleteNode(changes, sourceFile, node, node.kind === SyntaxKind.SemicolonToken ? { trailingTriviaOption: TrailingTriviaOption.Exclude } : undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1213,8 +1226,8 @@ namespace ts.textChanges {
|
||||||
|
|
||||||
/** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */
|
/** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */
|
||||||
// Exported for tests only! (TODO: improve tests to not need this)
|
// Exported for tests only! (TODO: improve tests to not need this)
|
||||||
export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}): void {
|
export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void {
|
||||||
const startPosition = getAdjustedStartPosition(sourceFile, node, options, Position.FullStart);
|
const startPosition = getAdjustedStartPosition(sourceFile, node, options);
|
||||||
const endPosition = getAdjustedEndPosition(sourceFile, node, options);
|
const endPosition = getAdjustedEndPosition(sourceFile, node, options);
|
||||||
changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
"refactors/generateGetAccessorAndSetAccessor.ts",
|
"refactors/generateGetAccessorAndSetAccessor.ts",
|
||||||
"refactors/moveToNewFile.ts",
|
"refactors/moveToNewFile.ts",
|
||||||
"refactors/addOrRemoveBracesToArrowFunction.ts",
|
"refactors/addOrRemoveBracesToArrowFunction.ts",
|
||||||
|
"refactors/convertToNamedParameters.ts",
|
||||||
"services.ts",
|
"services.ts",
|
||||||
"breakpoints.ts",
|
"breakpoints.ts",
|
||||||
"transform.ts",
|
"transform.ts",
|
||||||
|
|
|
@ -1664,6 +1664,18 @@ namespace ts {
|
||||||
return ensureScriptKind(fileName, host && host.getScriptKind && host.getScriptKind(fileName));
|
return ensureScriptKind(fileName, host && host.getScriptKind && host.getScriptKind(fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSymbolTarget(symbol: Symbol): Symbol {
|
||||||
|
let next: Symbol = symbol;
|
||||||
|
while (isTransientSymbol(next) && next.target) {
|
||||||
|
next = next.target;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
|
||||||
|
return (symbol.flags & SymbolFlags.Transient) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function getUniqueSymbolId(symbol: Symbol, checker: TypeChecker) {
|
export function getUniqueSymbolId(symbol: Symbol, checker: TypeChecker) {
|
||||||
return getSymbolId(skipAlias(symbol, checker));
|
return getSymbolId(skipAlias(symbol, checker));
|
||||||
}
|
}
|
||||||
|
@ -1821,8 +1833,28 @@ namespace ts {
|
||||||
return lastPos;
|
return lastPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copyComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
|
export function copyLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
|
||||||
forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => {
|
forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticLeadingComment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function copyTrailingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
|
||||||
|
forEachTrailingCommentRange(sourceFile.text, sourceNode.end, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticTrailingComment));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function copies the trailing comments for the token that comes before `sourceNode`, as leading comments of `targetNode`.
|
||||||
|
* This is useful because sometimes a comment that refers to `sourceNode` will be a leading comment for `sourceNode`, according to the
|
||||||
|
* notion of trivia ownership, and instead will be a trailing comment for the token before `sourceNode`, e.g.:
|
||||||
|
* `function foo(\* not leading comment for a *\ a: string) {}`
|
||||||
|
* The comment refers to `a` but belongs to the `(` token, but we might want to copy it.
|
||||||
|
*/
|
||||||
|
export function copyTrailingAsLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
|
||||||
|
forEachTrailingCommentRange(sourceFile.text, sourceNode.pos, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticLeadingComment));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAddCommentsFunction(targetNode: Node, sourceFile: SourceFile, commentKind: CommentKind | undefined, hasTrailingNewLine: boolean | undefined, cb: (node: Node, kind: CommentKind, text: string, hasTrailingNewLine?: boolean) => void) {
|
||||||
|
return (pos: number, end: number, kind: CommentKind, htnl: boolean) => {
|
||||||
if (kind === SyntaxKind.MultiLineCommentTrivia) {
|
if (kind === SyntaxKind.MultiLineCommentTrivia) {
|
||||||
// Remove leading /*
|
// Remove leading /*
|
||||||
pos += 2;
|
pos += 2;
|
||||||
|
@ -1833,8 +1865,8 @@ namespace ts {
|
||||||
// Remove leading //
|
// Remove leading //
|
||||||
pos += 2;
|
pos += 2;
|
||||||
}
|
}
|
||||||
addSyntheticLeadingComment(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl);
|
cb(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexInTextChange(change: string, name: string): number {
|
function indexInTextChange(change: string, name: string): number {
|
||||||
|
@ -1914,4 +1946,28 @@ namespace ts {
|
||||||
export function getSwitchedType(caseClause: CaseClause, checker: TypeChecker): Type | undefined {
|
export function getSwitchedType(caseClause: CaseClause, checker: TypeChecker): Type | undefined {
|
||||||
return checker.getTypeAtLocation(caseClause.parent.parent.expression);
|
return checker.getTypeAtLocation(caseClause.parent.parent.expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined {
|
||||||
|
const checker = program.getTypeChecker();
|
||||||
|
let typeIsAccessible = true;
|
||||||
|
const notAccessible = () => { typeIsAccessible = false; };
|
||||||
|
const res = checker.typeToTypeNode(type, enclosingScope, /*flags*/ undefined, {
|
||||||
|
trackSymbol: (symbol, declaration, meaning) => {
|
||||||
|
// TODO: GH#18217
|
||||||
|
typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning!, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible;
|
||||||
|
},
|
||||||
|
reportInaccessibleThisError: notAccessible,
|
||||||
|
reportPrivateInBaseOfClassExpression: notAccessible,
|
||||||
|
reportInaccessibleUniqueSymbolError: notAccessible,
|
||||||
|
moduleResolverHost: {
|
||||||
|
readFile: host.readFile,
|
||||||
|
fileExists: host.fileExists,
|
||||||
|
directoryExists: host.directoryExists,
|
||||||
|
getSourceFiles: program.getSourceFiles,
|
||||||
|
getCurrentDirectory: program.getCurrentDirectory,
|
||||||
|
getCommonSourceDirectory: program.getCommonSourceDirectory,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return typeIsAccessible ? res : undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,13 +140,13 @@ var z = 3; // comment 4
|
||||||
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile));
|
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile));
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNode2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNode2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedStartPosition: true });
|
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNode3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNode3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedEndPosition: true });
|
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNode4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNode4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNode5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNode5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("x", sourceFile));
|
deleteNode(changeTracker, sourceFile, findVariableStatementContaining("x", sourceFile));
|
||||||
|
@ -167,15 +167,15 @@ var a = 4; // comment 7
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
||||||
{ useNonAdjustedStartPosition: true });
|
{ leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
||||||
{ useNonAdjustedEndPosition: true });
|
{ trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
runSingleFileTest("deleteNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
runSingleFileTest("deleteNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||||
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
|
||||||
{ useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
{ leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function createTestVariableDeclaration(name: string) {
|
function createTestVariableDeclaration(name: string) {
|
||||||
|
@ -254,16 +254,16 @@ var a = 4; // comment 7`;
|
||||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNode2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNode2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, suffix: newLineCharacter, prefix: newLineCharacter });
|
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNode3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNode3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedEndPosition: true, suffix: newLineCharacter });
|
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNode4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNode4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNode5", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNode5", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -279,13 +279,13 @@ var a = 4; // comment 7`;
|
||||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, suffix: newLineCharacter, prefix: newLineCharacter });
|
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedEndPosition: true, suffix: newLineCharacter });
|
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter });
|
||||||
});
|
});
|
||||||
runSingleFileTest("replaceNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
runSingleFileTest("replaceNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||||
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { useNonAdjustedStartPosition: true, useNonAdjustedEndPosition: true });
|
changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function f(/*a*/a?: number, b: string = "1"/*b*/): string {
|
||||||
|
//// return b;
|
||||||
|
////}
|
||||||
|
////f();
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function f({ a, b = "1" }: { a?: number; b?: string; } = {}): string {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
f();`
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const foo = /*a*/(a: number, b: number)/*b*/ => { };
|
||||||
|
////foo(1, 2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `const foo = ({ a, b }: { a: number; b: number; }) => { };
|
||||||
|
foo({ a: 1, b: 2 });`
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const foo: (a: number, b: number) => number = /*a*/(a: number, b: number)/*b*/ => a + b;
|
||||||
|
////foo(1, 2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
verify.not.refactorAvailable("Convert to named parameters");
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function /*a*/foo/*b*/(a: number, b: number, ...rest: number[]) {
|
||||||
|
//// return a + b;
|
||||||
|
////}
|
||||||
|
////foo(/**a*/ 1 /**b*/, /**c*/ 2 /**d*/, /**e*/ 3 /**f*/, /**g*/ 4 /**h*/);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
foo({ /**a*/ a: 1 /**b*/, /**c*/ b: 2 /**d*/, rest: [/**e*/ 3 /**f*/, /**g*/ 4 /**h*/] });`
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function /*a*/foo/*b*/(a: number, b: number, ...rest: number[]) {
|
||||||
|
//// return a + b;
|
||||||
|
////}
|
||||||
|
////foo(
|
||||||
|
//// /**a*/
|
||||||
|
//// 1,
|
||||||
|
//// /**c*/
|
||||||
|
//// 2,
|
||||||
|
//// /**e*/
|
||||||
|
//// 3,
|
||||||
|
//// /**g*/
|
||||||
|
//// 4);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
/* The expected content is currently wrong. The new argument object has the wrong formatting. */
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
foo(
|
||||||
|
{ /**a*/
|
||||||
|
a: 1, /**c*/
|
||||||
|
b: 2, rest: [
|
||||||
|
/**e*/
|
||||||
|
3,
|
||||||
|
/**g*/
|
||||||
|
4]
|
||||||
|
});`
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function foo(/*a*/a: number, b: number/*b*/) {
|
||||||
|
//// return { bar: () => a + b };
|
||||||
|
////}
|
||||||
|
////var x = foo(1, 2).bar();
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo({ a, b }: { a: number; b: number; }) {
|
||||||
|
return { bar: () => a + b };
|
||||||
|
}
|
||||||
|
var x = foo({ a: 1, b: 2 }).bar();`
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// /*a*/constructor/*b*/(a: number, b: number) { }
|
||||||
|
////}
|
||||||
|
////const fooAlias = Foo;
|
||||||
|
////const newFoo = new fooAlias(1, 2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
// Refactor should not make changes
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
constructor(a: number, b: number) { }
|
||||||
|
}
|
||||||
|
const fooAlias = Foo;
|
||||||
|
const newFoo = new fooAlias(1, 2);`
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class C {
|
||||||
|
//// static a: number = 2;
|
||||||
|
//// /*a*/constructor/*b*/(a: number, b: number) { }
|
||||||
|
////}
|
||||||
|
////const newC = new C(1, 2);
|
||||||
|
////const b = C.a;
|
||||||
|
////C["a"] = 3;
|
||||||
|
////let c: C;
|
||||||
|
////function f(c: C) { }
|
||||||
|
////class B extends C { }
|
||||||
|
////class A implements C { }
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class C {
|
||||||
|
static a: number = 2;
|
||||||
|
constructor({ a, b }: { a: number; b: number; }) { }
|
||||||
|
}
|
||||||
|
const newC = new C({ a: 1, b: 2 });
|
||||||
|
const b = C.a;
|
||||||
|
C["a"] = 3;
|
||||||
|
let c: C;
|
||||||
|
function f(c: C) { }
|
||||||
|
class B extends C { }
|
||||||
|
class A implements C { }`
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const c = class {
|
||||||
|
//// constructor(/*a*/a: number, b = { x: 1 }/*b*/) { }
|
||||||
|
////}
|
||||||
|
////var x = new c(2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `const c = class {
|
||||||
|
constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number; }; }) { }
|
||||||
|
}
|
||||||
|
var x = new c({ a: 2 });`
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const c = class C {
|
||||||
|
//// static a: number = 2;
|
||||||
|
//// /*a*/constructor/*b*/(a: number, b: number) { }
|
||||||
|
////}
|
||||||
|
////const a = new c(0, 1);
|
||||||
|
////const b = c.a;
|
||||||
|
////c["a"] = 3;
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `const c = class C {
|
||||||
|
static a: number = 2;
|
||||||
|
constructor({ a, b }: { a: number; b: number; }) { }
|
||||||
|
}
|
||||||
|
const a = new c({ a: 0, b: 1 });
|
||||||
|
const b = c.a;
|
||||||
|
c["a"] = 3;`
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const foo = class Foo {
|
||||||
|
//// /*a*/constructor/*b*/(a: number, b: number) { }
|
||||||
|
////}
|
||||||
|
////class Bar extends foo {
|
||||||
|
//// constructor() {
|
||||||
|
//// super(1, 2);
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
// Refactor should not make changes
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `const foo = class Foo {
|
||||||
|
constructor(a: number, b: number) { }
|
||||||
|
}
|
||||||
|
class Bar extends foo {
|
||||||
|
constructor() {
|
||||||
|
super(1, 2);
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo<T> {
|
||||||
|
//// /*a*/bar/*b*/(t: T, s: T) {
|
||||||
|
//// return s;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////var foo = new Foo();
|
||||||
|
////foo.bar("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo<T> {
|
||||||
|
bar({ t, s }: { t: T; s: T; }) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var foo = new Foo();
|
||||||
|
foo.bar({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,27 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// t: string;
|
||||||
|
//// s: string;
|
||||||
|
//// /*a*/constructor/*b*/(t: string, s: string) {
|
||||||
|
//// this.t = t;
|
||||||
|
//// this.s = s;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////var foo = new Foo("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
t: string;
|
||||||
|
s: string;
|
||||||
|
constructor({ t, s }: { t: string; s: string; }) {
|
||||||
|
this.t = t;
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var foo = new Foo({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,8 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
/////export default class {
|
||||||
|
//// constructor(/*a*/a: number, b = { x: 1 }/*b*/) {}
|
||||||
|
////}
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
verify.not.refactorAvailable("Convert to named parameters");
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function f(/*a*/a: number, b: string/*b*/): string {
|
||||||
|
//// return b;
|
||||||
|
////}
|
||||||
|
////f(4, "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function f({ a, b }: { a: number; b: string; }): string {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
f({ a: 4, b: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////foo(1, 2); /**a*/
|
||||||
|
/////**b*/ function /*a*/foo/*b*/(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, /**c*/ a /**d*/: /**e*/ number /**f*/, /**g*/ b /**h*/: /**i*/ number /**j*/ = /**k*/ 1 /**l*/) {
|
||||||
|
//// // m
|
||||||
|
//// /**n*/ return a + b; // o
|
||||||
|
//// // p
|
||||||
|
////} // q
|
||||||
|
/////**r*/ foo(1);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `foo({ a: 1, b: 2 }); /**a*/
|
||||||
|
/**b*/ function foo(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, { a, b = /**k*/ 1 /**l*/ }: { /**c*/ a /**d*/: /**e*/ number /**f*/; /**g*/ b /**h*/?: /**i*/ number /**j*/; }) {
|
||||||
|
// m
|
||||||
|
/**n*/ return a + b; // o
|
||||||
|
// p
|
||||||
|
} // q
|
||||||
|
/**r*/ foo({ a: 1 });`
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function /*a*/foo/*b*/(a: number /** a */, b: number /** b */) {
|
||||||
|
//// return a + b;
|
||||||
|
////}
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo({ a, b }: { a: number /** a */; b: number /** b */; }) {
|
||||||
|
return a + b;
|
||||||
|
}`
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function /*a*/foo/*b*/(// comment
|
||||||
|
//// // a comment
|
||||||
|
//// a: number,
|
||||||
|
//// // b comment
|
||||||
|
//// b: number
|
||||||
|
////) {
|
||||||
|
//// return a + b;
|
||||||
|
////}
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo(// comment
|
||||||
|
{ a, b }: {
|
||||||
|
// a comment
|
||||||
|
a: number;
|
||||||
|
// b comment
|
||||||
|
b: number;
|
||||||
|
}) {
|
||||||
|
return a + b;
|
||||||
|
}`
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const foo = /*a*/function/*b*/(a: number, b: number) { };
|
||||||
|
////foo(1, 2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `const foo = function({ a, b }: { a: number; b: number; }) { };
|
||||||
|
foo({ a: 1, b: 2 });`
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function foo<T, S>(/*a*/t: T, s: S/*b*/) {
|
||||||
|
//// return s;
|
||||||
|
////}
|
||||||
|
////foo("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo<T, S>({ t, s }: { t: T; s: S; }) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
foo({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// /*a*/constructor/*b*/(t: string, s: string) { }
|
||||||
|
////}
|
||||||
|
////class Bar extends Foo { }
|
||||||
|
////var bar = new Bar("a", "b");
|
||||||
|
////var foo = new Foo("c", "d");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
/* The expected new content is currently wrong.
|
||||||
|
`new Bar("a", "b")` should be modified by the refactor to be `new Bar({ t: "a", s: "b" })`
|
||||||
|
*/
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
constructor({ t, s }: { t: string; s: string; }) { }
|
||||||
|
}
|
||||||
|
class Bar extends Foo { }
|
||||||
|
var bar = new Bar("a", "b");
|
||||||
|
var foo = new Foo({ t: "c", s: "d" });`
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// /*a*/bar/*b*/(t: string, s: string): string {
|
||||||
|
//// return s + t;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////class Bar extends Foo { }
|
||||||
|
////var bar = new Bar();
|
||||||
|
////bar.bar("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
bar({ t, s }: { t: string; s: string; }): string {
|
||||||
|
return s + t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Bar extends Foo { }
|
||||||
|
var bar = new Bar();
|
||||||
|
bar.bar({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function f(/*a*/a: number, b: string = "1"/*b*/): string {
|
||||||
|
//// return b;
|
||||||
|
////}
|
||||||
|
////f(4, "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function f({ a, b = "1" }: { a: number; b?: string; }): string {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
f({ a: 4, b: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function f(/*a*/a: number, b = { x: 1, z: { s: true } }/*b*/) {
|
||||||
|
//// return b;
|
||||||
|
////}
|
||||||
|
////f(2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function f({ a, b = { x: 1, z: { s: true } } }: { a: number; b?: { x: number; z: { s: boolean; }; }; }) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
f({ a: 2 });`
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// /*a*/bar/*b*/(t: string, s: string): string {
|
||||||
|
//// return s + t;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////var foo = new Foo();
|
||||||
|
////foo.bar("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
bar({ t, s }: { t: string; s: string; }): string {
|
||||||
|
return s + t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var foo = new Foo();
|
||||||
|
foo.bar({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class A {
|
||||||
|
//// /*a*/foo/*b*/(a: number, b: number) { return a + b; }
|
||||||
|
////}
|
||||||
|
////class B {
|
||||||
|
//// foo(c: number, d: number) { return c + d; }
|
||||||
|
////}
|
||||||
|
////function foo(ab: A | B) {
|
||||||
|
//// return ab.foo(1, 2);
|
||||||
|
////}
|
||||||
|
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
// Refactor should not make changes
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class A {
|
||||||
|
foo(a: number, b: number) { return a + b; }
|
||||||
|
}
|
||||||
|
class B {
|
||||||
|
foo(c: number, d: number) { return c + d; }
|
||||||
|
}
|
||||||
|
function foo(ab: A | B) {
|
||||||
|
return ab.foo(1, 2);
|
||||||
|
}`
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// /*a*/bar/*b*/(t: string, s: string): string {
|
||||||
|
//// return s + t;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////var foo = new Foo();
|
||||||
|
////foo['bar']("a", "b");
|
||||||
|
////foo.bar("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
bar({ t, s }: { t: string; s: string; }): string {
|
||||||
|
return s + t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var foo = new Foo();
|
||||||
|
foo['bar']({ t: "a", s: "b" });
|
||||||
|
foo.bar({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,45 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class A {
|
||||||
|
//// /*a*/foo/*b*/(a: number, b: number) { }
|
||||||
|
////}
|
||||||
|
////class B extends A {
|
||||||
|
//// /*c*/foo/*d*/(c: number, d: number) { }
|
||||||
|
////}
|
||||||
|
////var a = new A();
|
||||||
|
////a.foo(3, 4);
|
||||||
|
////var b = new B();
|
||||||
|
////b.foo(5, 6);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class A {
|
||||||
|
foo(a: number, b: number) { }
|
||||||
|
}
|
||||||
|
class B extends A {
|
||||||
|
foo(c: number, d: number) { }
|
||||||
|
}
|
||||||
|
var a = new A();
|
||||||
|
a.foo(3, 4);
|
||||||
|
var b = new B();
|
||||||
|
b.foo(5, 6);`
|
||||||
|
});
|
||||||
|
goTo.select("c", "d");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class A {
|
||||||
|
foo(a: number, b: number) { }
|
||||||
|
}
|
||||||
|
class B extends A {
|
||||||
|
foo(c: number, d: number) { }
|
||||||
|
}
|
||||||
|
var a = new A();
|
||||||
|
a.foo(3, 4);
|
||||||
|
var b = new B();
|
||||||
|
b.foo(5, 6);`
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function f(a: number, b: number);
|
||||||
|
////function f(/*a*/a: number, b = 1/*b*/) {
|
||||||
|
//// return b;
|
||||||
|
////}
|
||||||
|
////f(2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
verify.not.refactorAvailable("Convert to named parameters");
|
|
@ -0,0 +1,11 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////declare function required(target: Object, propertyKey: string | symbol, parameterIndex: number)
|
||||||
|
////class C {
|
||||||
|
//// /*a*/bar/*b*/(@required a: number, b: number) {
|
||||||
|
////
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
verify.not.refactorAvailable("Convert to named parameters");
|
|
@ -0,0 +1,20 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////const f = function foo(/*a*/a: number, b: number/*b*/) {
|
||||||
|
//// foo(1, 2);
|
||||||
|
////}
|
||||||
|
////function foo(a: number, b: number) { }
|
||||||
|
////foo(3, 4);
|
||||||
|
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `const f = function foo({ a, b }: { a: number; b: number; }) {
|
||||||
|
foo({ a: 1, b: 2 });
|
||||||
|
}
|
||||||
|
function foo(a: number, b: number) { }
|
||||||
|
foo(3, 4);`
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function log(/*a*/a: number, b: number, ...args/*b*/) { }
|
||||||
|
////let l = log(-1, -2, 3, 4, 5);
|
||||||
|
////let k = log(1, 2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function log({ a, b, args = [] }: { a: number; b: number; args?: any[]; }) { }
|
||||||
|
let l = log({ a: -1, b: -2, args: [3, 4, 5] });
|
||||||
|
let k = log({ a: 1, b: 2 });`
|
||||||
|
});
|
|
@ -0,0 +1,21 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class Foo {
|
||||||
|
//// static /*a*/bar/*b*/(t: string, s: string): string {
|
||||||
|
//// return s + t;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////Foo.bar("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class Foo {
|
||||||
|
static bar({ t, s }: { t: string; s: string; }): string {
|
||||||
|
return s + t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Foo.bar({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////class A {
|
||||||
|
//// constructor(/*a*/a: string, b: string/*b*/) { }
|
||||||
|
////}
|
||||||
|
////class B extends A {
|
||||||
|
//// constructor(a: string, b: string, c: string) {
|
||||||
|
//// super(a, b);
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `class A {
|
||||||
|
constructor({ a, b }: { a: string; b: string; }) { }
|
||||||
|
}
|
||||||
|
class B extends A {
|
||||||
|
constructor(a: string, b: string, c: string) {
|
||||||
|
super({ a: a, b: b });
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function foo(this: void, /*a*/t: string, s: string/*b*/) {
|
||||||
|
//// return s;
|
||||||
|
////}
|
||||||
|
////foo("a", "b");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function foo(this: void, { t, s }: { t: string; s: string; }) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
foo({ t: "a", s: "b" });`
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////function /*a*/buildName/*b*/(firstName: string, middleName?: string, ...restOfName: string[]) { }
|
||||||
|
////let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
|
||||||
|
////let myName = buildName("Joseph");
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
edit.applyRefactor({
|
||||||
|
refactorName: "Convert to named parameters",
|
||||||
|
actionName: "Convert to named parameters",
|
||||||
|
actionDescription: "Convert to named parameters",
|
||||||
|
newContent: `function buildName({ firstName, middleName, restOfName = [] }: { firstName: string; middleName?: string; restOfName?: string[]; }) { }
|
||||||
|
let employeeName = buildName({ firstName: "Joseph", middleName: "Samuel", restOfName: ["Lucas", "MacKinzie"] });
|
||||||
|
let myName = buildName({ firstName: "Joseph" });`
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////var foo = /*a*/(a: number, b: number)/*b*/ => {};
|
||||||
|
////foo(1, 2);
|
||||||
|
|
||||||
|
goTo.select("a", "b");
|
||||||
|
verify.not.refactorAvailable("Convert to named parameters");
|
Loading…
Reference in a new issue