diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 598cfc2fd7e76..d89a4677ef90a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2758,11 +2758,11 @@ namespace FourSlash { } } - private getSelection() { - return ({ + private getSelection(): ts.TextRange { + return { pos: this.currentCaretPosition, end: this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd - }); + }; } public verifyRefactorAvailable(negative: boolean, name: string, actionName?: string) { @@ -2803,7 +2803,7 @@ namespace FourSlash { } } - public applyRefactor({ refactorName, actionName, actionDescription }: FourSlashInterface.ApplyRefactorOptions) { + public applyRefactor({ refactorName, actionName, actionDescription, newContent: newContentWithRenameMarker }: FourSlashInterface.ApplyRefactorOptions) { const range = this.getSelection(); const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range); const refactor = refactors.find(r => r.name === refactorName); @@ -2823,6 +2823,35 @@ namespace FourSlash { for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); } + + const { renamePosition, newContent } = parseNewContent(); + + this.verifyCurrentFileContent(newContent); + + if (renamePosition === undefined) { + if (editInfo.renameLocation !== undefined) { + this.raiseError(`Did not expect a rename location, got ${editInfo.renameLocation}`); + } + } + else { + // TODO: test editInfo.renameFilename value + assert.isDefined(editInfo.renameFilename); + if (renamePosition !== editInfo.renameLocation) { + this.raiseError(`Expected rename position of ${renamePosition}, but got ${editInfo.renameLocation}`); + } + } + + function parseNewContent(): { renamePosition: number | undefined, newContent: string } { + const renamePosition = newContentWithRenameMarker.indexOf("/*RENAME*/"); + if (renamePosition === -1) { + return { renamePosition: undefined, newContent: newContentWithRenameMarker }; + } + else { + const newContent = newContentWithRenameMarker.slice(0, renamePosition) + newContentWithRenameMarker.slice(renamePosition + "/*RENAME*/".length); + return { renamePosition, newContent }; + } + } + } public verifyFileAfterApplyingRefactorAtMarker( @@ -4319,6 +4348,7 @@ namespace FourSlashInterface { refactorName: string; actionName: string; actionDescription: string; + newContent: string; } export interface CompletionsAtOptions { diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts index edcc80cd57cc5..c836698aeb429 100644 --- a/src/harness/unittests/extractMethods.ts +++ b/src/harness/unittests/extractMethods.ts @@ -745,9 +745,12 @@ function M3() { }`); data.push(`// ==ORIGINAL==`); data.push(sourceFile.text); for (const r of results) { - const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes; + const { renameLocation, edits } = refactor.extractMethod.getExtractionAtIndex(result.targetRange, context, results.indexOf(r)); + assert.lengthOf(edits, 1); data.push(`// ==SCOPE::${r.scopeDescription}==`); - data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges)); + const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); + const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); + data.push(newTextWithRename); } return data.join(newLineCharacter); }); diff --git a/src/server/client.ts b/src/server/client.ts index 4acd862a89a24..0ffac42dae4d4 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -586,9 +586,7 @@ namespace ts.server { const response = this.processResponse(request); if (!response.body) { - return { - edits: [] - }; + return { edits: [], renameFilename: undefined, renameLocation: undefined }; } const edits: FileTextChanges[] = this.convertCodeEditsToTextChanges(response.body.edits); diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index 40ef4ed2a2e1c..e4cd1a420832c 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -97,7 +97,9 @@ namespace ts.refactor.convertFunctionToES6Class { } return { - edits: changeTracker.getChanges() + edits: changeTracker.getChanges(), + renameFilename: undefined, + renameLocation: undefined, }; function deleteNode(node: Node, inList = false) { diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractMethod.ts index b055413813436..7b5e1e40c7e66 100644 --- a/src/services/refactors/extractMethod.ts +++ b/src/services/refactors/extractMethod.ts @@ -31,16 +31,16 @@ namespace ts.refactor.extractMethod { const usedNames: Map = createMap(); let i = 0; - for (const extr of extractions) { + for (const { scopeDescription, errors } of extractions) { // Skip these since we don't have a way to report errors yet - if (extr.errors && extr.errors.length) { + if (errors.length) { continue; } // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will // preferentially go into nearer scopes - const description = formatStringFromArgs(Diagnostics.Extract_to_0.message, [extr.scopeDescription]); + const description = formatStringFromArgs(Diagnostics.Extract_to_0.message, [scopeDescription]); if (!usedNames.has(description)) { usedNames.set(description, true); actions.push({ @@ -75,10 +75,7 @@ namespace ts.refactor.extractMethod { const index = +parsedIndexMatch[1]; Debug.assert(isFinite(index), "Expected to parse a finite number from the scope index"); - const extractions = getPossibleExtractions(targetRange, context, index); - // Scope is no longer valid from when the user issued the refactor (??) - Debug.assert(extractions !== undefined, "The extraction went missing? How?"); - return ({ edits: extractions[0].changes }); + return getExtractionAtIndex(targetRange, context, index); } // Move these into diagnostic messages if they become user-facing @@ -102,7 +99,7 @@ namespace ts.refactor.extractMethod { export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts"); } - export enum RangeFacts { + enum RangeFacts { None = 0, HasReturn = 1 << 0, IsGenerator = 1 << 1, @@ -117,7 +114,7 @@ namespace ts.refactor.extractMethod { /** * Represents an expression or a list of statements that should be extracted with some extra information */ - export interface TargetRange { + interface TargetRange { readonly range: Expression | Statement[]; readonly facts: RangeFacts; /** @@ -130,7 +127,7 @@ namespace ts.refactor.extractMethod { /** * Result of 'getRangeToExtract' operation: contains either a range or a list of errors */ - export type RangeToExtract = { + type RangeToExtract = { readonly targetRange?: never; readonly errors: ReadonlyArray; } | { @@ -141,18 +138,7 @@ namespace ts.refactor.extractMethod { /* * Scopes that can store newly extracted method */ - export type Scope = FunctionLikeDeclaration | SourceFile | ModuleBlock | ClassLikeDeclaration; - - /** - * Result of 'extractRange' operation for a specific scope. - * Stores either a list of changes that should be applied to extract a range or a list of errors - */ - export interface ExtractResultForScope { - readonly scope: Scope; - readonly scopeDescription: string; - readonly changes?: FileTextChanges[]; - readonly errors?: Diagnostic[]; - } + type Scope = FunctionLikeDeclaration | SourceFile | ModuleBlock | ClassLikeDeclaration; /** * getRangeToExtract takes a span inside a text file and returns either an expression or an array @@ -160,6 +146,7 @@ namespace ts.refactor.extractMethod { * process may fail, in which case a set of errors is returned instead (these are currently * not shown to the user, but can be used by us diagnostically) */ + // exported only for tests export function getRangeToExtract(sourceFile: SourceFile, span: TextSpan): RangeToExtract { const length = span.length || 0; // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. @@ -458,7 +445,7 @@ namespace ts.refactor.extractMethod { * you may be able to extract into a class method *or* local closure *or* namespace function, * depending on what's in the extracted body. */ - export function collectEnclosingScopes(range: TargetRange): Scope[] | undefined { + function collectEnclosingScopes(range: TargetRange): Scope[] | undefined { let current: Node = isReadonlyArray(range.range) ? firstOrUndefined(range.range) : range.range; if (range.facts & RangeFacts.UsesThis) { // if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class @@ -494,12 +481,32 @@ namespace ts.refactor.extractMethod { return scopes; } + // exported only for tests + export function getExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo { + const { scopes, readsAndWrites: { target, usagesPerScope, errorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); + Debug.assert(!errorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?"); + context.cancellationToken.throwIfCancellationRequested(); + return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context); + } + + interface PossibleExtraction { + readonly scopeDescription: string; + readonly errors: ReadonlyArray; + } /** * Given a piece of text to extract ('targetRange'), computes a list of possible extractions. * Each returned ExtractResultForScope corresponds to a possible target scope and is either a set of changes * or an error explaining why we can't extract into that scope. */ - export function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number = undefined): ReadonlyArray | undefined { + // exported only for tests + export function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray | undefined { + const { scopes, readsAndWrites: { errorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); + // Need the inner type annotation to avoid https://github.com/Microsoft/TypeScript/issues/7547 + return scopes.map((scope, i): PossibleExtraction => + ({ scopeDescription: getDescriptionForScope(scope), errors: errorsPerScope[i] })); + } + + function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[], readonly readsAndWrites: ReadsAndWrites } { const { file: sourceFile } = context; if (targetRange === undefined) { @@ -512,35 +519,14 @@ namespace ts.refactor.extractMethod { } const enclosingTextRange = getEnclosingTextRange(targetRange, sourceFile); - const { target, usagesPerScope, errorsPerScope } = collectReadsAndWrites( + const readsAndWrites = collectReadsAndWrites( targetRange, scopes, enclosingTextRange, sourceFile, context.program.getTypeChecker(), context.cancellationToken); - - context.cancellationToken.throwIfCancellationRequested(); - - if (requestedChangesIndex !== undefined) { - if (errorsPerScope[requestedChangesIndex].length) { - return undefined; - } - return [extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context)]; - } - else { - return scopes.map((scope, i) => { - const errors = errorsPerScope[i]; - if (errors.length) { - return { - scope, - scopeDescription: getDescriptionForScope(scope), - errors - }; - } - return { scope, scopeDescription: getDescriptionForScope(scope) }; - }); - } + return { scopes, readsAndWrites }; } function getDescriptionForScope(scope: Scope): string { @@ -583,34 +569,33 @@ namespace ts.refactor.extractMethod { : scope.externalModuleIndicator ? "module scope" : "global scope"; } - function getUniqueName(isNameOkay: (name: string) => boolean) { + function getUniqueName(fileText: string): string { let functionNameText = "newFunction"; - if (isNameOkay(functionNameText)) { - return functionNameText; - } - let i = 1; - while (!isNameOkay(functionNameText = `newFunction_${i}`)) { - i++; + for (let i = 1; fileText.indexOf(functionNameText) !== -1; i++) { + functionNameText = `newFunction_${i}`; } return functionNameText; } - export function extractFunctionInScope( + /** + * Result of 'extractRange' operation for a specific scope. + * Stores either a list of changes that should be applied to extract a range or a list of errors + */ + function extractFunctionInScope( node: Statement | Expression | Block, scope: Scope, { usages: usagesInScope, typeParameterUsages, substitutions }: ScopeUsages, range: TargetRange, - context: RefactorContext): ExtractResultForScope { + context: RefactorContext): RefactorEditInfo { const checker = context.program.getTypeChecker(); // Make a unique name for the extracted function const file = scope.getSourceFile(); - const functionNameText: string = getUniqueName(n => !file.identifiers.has(n)); + const functionNameText = getUniqueName(file.text); const isJS = isInJavaScriptFile(scope); - const functionName = createIdentifier(functionNameText as string); - const functionReference = createIdentifier(functionNameText as string); + const functionName = createIdentifier(functionNameText); let returnType: TypeNode = undefined; const parameters: ParameterDeclaration[] = []; @@ -660,7 +645,7 @@ namespace ts.refactor.extractMethod { returnType = checker.typeToTypeNode(contextualType); } - const { body, returnValueProperty } = transformFunctionBody(node); + const { body, returnValueProperty } = transformFunctionBody(node, writes, substitutions, !!(range.facts & RangeFacts.HasReturn)); let newFunction: MethodDeclaration | FunctionDeclaration; if (isClassLike(scope)) { @@ -709,8 +694,10 @@ namespace ts.refactor.extractMethod { const newNodes: Node[] = []; // replace range with function call + const called = getCalledExpression(scope, range, functionNameText); + let call: Expression = createCall( - isClassLike(scope) ? createPropertyAccess(range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.getText()) : createThis(), functionReference) : functionReference, + called, callTypeArguments, // Note that no attempt is made to take advantage of type argument inference callArguments); if (range.facts & RangeFacts.IsGenerator) { @@ -779,149 +766,176 @@ namespace ts.refactor.extractMethod { changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter }); } - return { - scope, - scopeDescription: getDescriptionForScope(scope), - changes: changeTracker.getChanges() - }; + const edits = changeTracker.getChanges(); + const renameRange = isReadonlyArray(range.range) ? range.range[0] : range.range; - function getFirstDeclaration(type: Type): Declaration | undefined { - let firstDeclaration = undefined; + const renameFilename = renameRange.getSourceFile().fileName; + const renameLocation = getRenameLocation(edits, renameFilename, functionNameText); + return { renameFilename, renameLocation, edits }; + } - const symbol = type.symbol; - if (symbol && symbol.declarations) { - for (const declaration of symbol.declarations) { - if (firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) { - firstDeclaration = declaration; - } + function getRenameLocation(edits: ReadonlyArray, renameFilename: string, functionNameText: string): number { + let delta = 0; + for (const { fileName, textChanges } of edits) { + Debug.assert(fileName === renameFilename); + for (const change of textChanges) { + const { span, newText } = change; + // TODO(acasey): We are assuming that the call expression comes before the function declaration, + // because we want the new cursor to be on the call expression, + // which is closer to where the user was before extracting the function. + const index = newText.indexOf(functionNameText); + if (index !== -1) { + return span.start + delta + index; } + delta += newText.length - span.length; } - - return firstDeclaration; } + throw new Error(); // Didn't find the text we inserted? + } - function compareTypesByDeclarationOrder( - {type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration}, - {type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) { + function getFirstDeclaration(type: Type): Declaration | undefined { + let firstDeclaration = undefined; - if (declaration1) { - if (declaration2) { - const positionDiff = declaration1.pos - declaration2.pos; - if (positionDiff !== 0) { - return positionDiff; - } - } - else { - return 1; // Sort undeclared type parameters to the front. + const symbol = type.symbol; + if (symbol && symbol.declarations) { + for (const declaration of symbol.declarations) { + if (firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) { + firstDeclaration = declaration; } } - else if (declaration2) { - return -1; // Sort undeclared type parameters to the front. - } - - const name1 = type1.symbol ? type1.symbol.getName() : ""; - const name2 = type2.symbol ? type2.symbol.getName() : ""; - const nameDiff = compareStrings(name1, name2); - if (nameDiff !== 0) { - return nameDiff; - } - - // IDs are guaranteed to be unique, so this ensures a total ordering. - return type1.id - type2.id; } - function getPropertyAssignmentsForWrites(writes: UsageEntry[]) { - return writes.map(w => createShorthandPropertyAssignment(w.symbol.name)); - } + return firstDeclaration; + } - function generateReturnValueProperty() { - return "__return"; - } + function compareTypesByDeclarationOrder( + {type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration}, + {type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) { - function getStatementsOrClassElements(scope: Scope): ReadonlyArray | ReadonlyArray { - if (isFunctionLike(scope)) { - const body = scope.body; - if (isBlock(body)) { - return body.statements; + if (declaration1) { + if (declaration2) { + const positionDiff = declaration1.pos - declaration2.pos; + if (positionDiff !== 0) { + return positionDiff; } } - else if (isModuleBlock(scope) || isSourceFile(scope)) { - return scope.statements; - } - else if (isClassLike(scope)) { - return scope.members; - } else { - assertTypeIsNever(scope); + return 1; // Sort undeclared type parameters to the front. } + } + else if (declaration2) { + return -1; // Sort undeclared type parameters to the front. + } - return emptyArray; + const name1 = type1.symbol ? type1.symbol.getName() : ""; + const name2 = type2.symbol ? type2.symbol.getName() : ""; + const nameDiff = compareStrings(name1, name2); + if (nameDiff !== 0) { + return nameDiff; } - /** - * If `scope` contains a function after `minPos`, then return the first such function. - * Otherwise, return `undefined`. - */ - function getNodeToInsertBefore(minPos: number, scope: Scope): Node | undefined { - const children = getStatementsOrClassElements(scope); - for (const child of children) { - if (child.pos >= minPos && isFunctionLike(child) && !isConstructorDeclaration(child)) { - return child; + // IDs are guaranteed to be unique, so this ensures a total ordering. + return type1.id - type2.id; + } + + function getCalledExpression(scope: Node, range: TargetRange, functionNameText: string): Expression { + const functionReference = createIdentifier(functionNameText); + if (isClassLike(scope)) { + const lhs = range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.text) : createThis(); + return createPropertyAccess(lhs, functionReference); + } + else { + return functionReference; + } + } + + function transformFunctionBody(body: Node, writes: ReadonlyArray, substitutions: ReadonlyMap, hasReturn: boolean): { body: Block, returnValueProperty: string } { + if (isBlock(body) && !writes && substitutions.size === 0) { + // already block, no writes to propagate back, no substitutions - can use node as is + return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined }; + } + let returnValueProperty: string; + const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(body)]); + // rewrite body if either there are writes that should be propagated back via return statements or there are substitutions + if (writes || substitutions.size) { + const rewrittenStatements = visitNodes(statements, visitor).slice(); + if (writes && !hasReturn && isStatement(body)) { + // add return at the end to propagate writes back in case if control flow falls out of the function body + // it is ok to know that range has at least one return since it we only allow unconditional returns + const assignments = getPropertyAssignmentsForWrites(writes); + if (assignments.length === 1) { + rewrittenStatements.push(createReturn(assignments[0].name)); + } + else { + rewrittenStatements.push(createReturn(createObjectLiteral(assignments))); } } + return { body: createBlock(rewrittenStatements, /*multiLine*/ true), returnValueProperty }; + } + else { + return { body: createBlock(statements, /*multiLine*/ true), returnValueProperty: undefined }; } - function transformFunctionBody(body: Node) { - if (isBlock(body) && !writes && substitutions.size === 0) { - // already block, no writes to propagate back, no substitutions - can use node as is - return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined }; - } - let returnValueProperty: string; - const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(body)]); - // rewrite body if either there are writes that should be propagated back via return statements or there are substitutions - if (writes || substitutions.size) { - const rewrittenStatements = visitNodes(statements, visitor).slice(); - if (writes && !(range.facts & RangeFacts.HasReturn) && isStatement(body)) { - // add return at the end to propagate writes back in case if control flow falls out of the function body - // it is ok to know that range has at least one return since it we only allow unconditional returns - const assignments = getPropertyAssignmentsForWrites(writes); - if (assignments.length === 1) { - rewrittenStatements.push(createReturn(assignments[0].name)); - } - else { - rewrittenStatements.push(createReturn(createObjectLiteral(assignments))); + function visitor(node: Node): VisitResult { + if (node.kind === SyntaxKind.ReturnStatement && writes) { + const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes); + if ((node).expression) { + if (!returnValueProperty) { + returnValueProperty = "__return"; } + assignments.unshift(createPropertyAssignment(returnValueProperty, visitNode((node).expression, visitor))); + } + if (assignments.length === 1) { + return createReturn(assignments[0].name as Expression); + } + else { + return createReturn(createObjectLiteral(assignments)); } - return { body: createBlock(rewrittenStatements, /*multiLine*/ true), returnValueProperty }; } else { - return { body: createBlock(statements, /*multiLine*/ true), returnValueProperty: undefined }; + const substitution = substitutions.get(getNodeId(node).toString()); + return substitution || visitEachChild(node, visitor, nullTransformationContext); } + } + } - function visitor(node: Node): VisitResult { - if (node.kind === SyntaxKind.ReturnStatement && writes) { - const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes); - if ((node).expression) { - if (!returnValueProperty) { - returnValueProperty = generateReturnValueProperty(); - } - assignments.unshift(createPropertyAssignment(returnValueProperty, visitNode((node).expression, visitor))); - } - if (assignments.length === 1) { - return createReturn(assignments[0].name as Expression); - } - else { - return createReturn(createObjectLiteral(assignments)); - } - } - else { - const substitution = substitutions.get(getNodeId(node).toString()); - return substitution || visitEachChild(node, visitor, nullTransformationContext); - } + function getStatementsOrClassElements(scope: Scope): ReadonlyArray | ReadonlyArray { + if (isFunctionLike(scope)) { + const body = scope.body; + if (isBlock(body)) { + return body.statements; + } + } + else if (isModuleBlock(scope) || isSourceFile(scope)) { + return scope.statements; + } + else if (isClassLike(scope)) { + return scope.members; + } + else { + assertTypeIsNever(scope); + } + + return emptyArray; + } + + /** + * If `scope` contains a function after `minPos`, then return the first such function. + * Otherwise, return `undefined`. + */ + function getNodeToInsertBefore(minPos: number, scope: Scope): Node | undefined { + const children = getStatementsOrClassElements(scope); + for (const child of children) { + if (child.pos >= minPos && isFunctionLike(child) && !isConstructorDeclaration(child)) { + return child; } } } + function getPropertyAssignmentsForWrites(writes: ReadonlyArray): ShorthandPropertyAssignment[] { + return writes.map(w => createShorthandPropertyAssignment(w.symbol.name)); + } + function isReadonlyArray(v: any): v is ReadonlyArray { return isArray(v); } @@ -948,25 +962,30 @@ namespace ts.refactor.extractMethod { Write = 2 } - export interface UsageEntry { + interface UsageEntry { readonly usage: Usage; readonly symbol: Symbol; readonly node: Node; } - export interface ScopeUsages { - usages: Map; - typeParameterUsages: Map; // Key is type ID - substitutions: Map; + interface ScopeUsages { + readonly usages: Map; + readonly typeParameterUsages: Map; // Key is type ID + readonly substitutions: Map; } + interface ReadsAndWrites { + readonly target: Expression | Block; + readonly usagesPerScope: ReadonlyArray; + readonly errorsPerScope: ReadonlyArray>; + } function collectReadsAndWrites( targetRange: TargetRange, scopes: Scope[], enclosingTextRange: TextRange, sourceFile: SourceFile, checker: TypeChecker, - cancellationToken: CancellationToken) { + cancellationToken: CancellationToken): ReadsAndWrites { const allTypeParameterUsages = createMap(); // Key is type ID const usagesPerScope: ScopeUsages[] = []; diff --git a/src/services/types.ts b/src/services/types.ts index 8a23bfec1c5c6..a971995f0503e 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -414,8 +414,8 @@ namespace ts { */ export interface RefactorEditInfo { edits: FileTextChanges[]; - renameFilename?: string; - renameLocation?: number; + renameFilename: string | undefined; + renameLocation: number | undefined; } export interface TextInsertion { diff --git a/tests/baselines/reference/extractMethod/extractMethod1.ts b/tests/baselines/reference/extractMethod/extractMethod1.ts index 660380c125368..86c28b5f4d288 100644 --- a/tests/baselines/reference/extractMethod/extractMethod1.ts +++ b/tests/baselines/reference/extractMethod/extractMethod1.ts @@ -23,7 +23,7 @@ namespace A { function a() { let a = 1; - newFunction(); + /*RENAME*/newFunction(); function newFunction() { let y = 5; @@ -43,7 +43,7 @@ namespace A { function a() { let a = 1; - a = newFunction(a); + a = /*RENAME*/newFunction(a); } function newFunction(a: number) { @@ -64,7 +64,7 @@ namespace A { function a() { let a = 1; - a = newFunction(a); + a = /*RENAME*/newFunction(a); } } @@ -85,7 +85,7 @@ namespace A { function a() { let a = 1; - a = newFunction(x, a, foo); + a = /*RENAME*/newFunction(x, a, foo); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod10.ts b/tests/baselines/reference/extractMethod/extractMethod10.ts index 13108a0813158..e3eb73b661ed1 100644 --- a/tests/baselines/reference/extractMethod/extractMethod10.ts +++ b/tests/baselines/reference/extractMethod/extractMethod10.ts @@ -15,7 +15,7 @@ namespace A { class C { a() { let z = 1; - return this.newFunction(); + return this./*RENAME*/newFunction(); } private newFunction() { @@ -30,7 +30,7 @@ namespace A { class C { a() { let z = 1; - return newFunction(); + return /*RENAME*/newFunction(); } } @@ -45,7 +45,7 @@ namespace A { class C { a() { let z = 1; - return newFunction(); + return /*RENAME*/newFunction(); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod11.ts b/tests/baselines/reference/extractMethod/extractMethod11.ts index 5a2e0da826ad7..43fdd75b76f93 100644 --- a/tests/baselines/reference/extractMethod/extractMethod11.ts +++ b/tests/baselines/reference/extractMethod/extractMethod11.ts @@ -18,7 +18,7 @@ namespace A { a() { let z = 1; var __return: any; - ({ __return, z } = this.newFunction(z)); + ({ __return, z } = this./*RENAME*/newFunction(z)); return __return; } @@ -37,7 +37,7 @@ namespace A { a() { let z = 1; var __return: any; - ({ __return, z } = newFunction(z)); + ({ __return, z } = /*RENAME*/newFunction(z)); return __return; } } @@ -56,7 +56,7 @@ namespace A { a() { let z = 1; var __return: any; - ({ __return, y, z } = newFunction(y, z)); + ({ __return, y, z } = /*RENAME*/newFunction(y, z)); return __return; } } diff --git a/tests/baselines/reference/extractMethod/extractMethod12.ts b/tests/baselines/reference/extractMethod/extractMethod12.ts index 98428a67bd0cb..2f3082cf28008 100644 --- a/tests/baselines/reference/extractMethod/extractMethod12.ts +++ b/tests/baselines/reference/extractMethod/extractMethod12.ts @@ -21,7 +21,7 @@ namespace A { a() { let z = 1; var __return: any; - ({ __return, z } = this.newFunction(z)); + ({ __return, z } = this./*RENAME*/newFunction(z)); return __return; } diff --git a/tests/baselines/reference/extractMethod/extractMethod13.ts b/tests/baselines/reference/extractMethod/extractMethod13.ts index 44968ac4dcfe8..121d7eeecfa7e 100644 --- a/tests/baselines/reference/extractMethod/extractMethod13.ts +++ b/tests/baselines/reference/extractMethod/extractMethod13.ts @@ -20,7 +20,7 @@ (u2a: U2a, u2b: U2b) => { function F2(t2a: T2a, t2b: T2b) { (u3a: U3a, u3b: U3b) => { - newFunction(u3a); + /*RENAME*/newFunction(u3a); } function newFunction(u3a: U3a) { @@ -40,7 +40,7 @@ (u2a: U2a, u2b: U2b) => { function F2(t2a: T2a, t2b: T2b) { (u3a: U3a, u3b: U3b) => { - newFunction(t2a, u2a, u3a); + /*RENAME*/newFunction(t2a, u2a, u3a); } } } @@ -60,7 +60,7 @@ (u2a: U2a, u2b: U2b) => { function F2(t2a: T2a, t2b: T2b) { (u3a: U3a, u3b: U3b) => { - newFunction(t1a, t2a, u1a, u2a, u3a); + /*RENAME*/newFunction(t1a, t2a, u1a, u2a, u3a); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod14.ts b/tests/baselines/reference/extractMethod/extractMethod14.ts index 4db0e748907cd..d3dcded2c42da 100644 --- a/tests/baselines/reference/extractMethod/extractMethod14.ts +++ b/tests/baselines/reference/extractMethod/extractMethod14.ts @@ -8,7 +8,7 @@ function F(t1: T) { // ==SCOPE::inner function in function 'F'== function F(t1: T) { function F(t2: T) { - newFunction(); + /*RENAME*/newFunction(); function newFunction() { t1.toString(); @@ -19,7 +19,7 @@ function F(t1: T) { // ==SCOPE::inner function in function 'F'== function F(t1: T) { function F(t2: T) { - newFunction(t2); + /*RENAME*/newFunction(t2); } function newFunction(t2: T) { @@ -30,7 +30,7 @@ function F(t1: T) { // ==SCOPE::function in global scope== function F(t1: T) { function F(t2: T) { - newFunction(t1, t2); + /*RENAME*/newFunction(t1, t2); } } function newFunction(t1: T, t2: T) { diff --git a/tests/baselines/reference/extractMethod/extractMethod15.ts b/tests/baselines/reference/extractMethod/extractMethod15.ts index 7d1c8aa45078c..50516445c8762 100644 --- a/tests/baselines/reference/extractMethod/extractMethod15.ts +++ b/tests/baselines/reference/extractMethod/extractMethod15.ts @@ -7,7 +7,7 @@ function F(t1: T) { // ==SCOPE::inner function in function 'F'== function F(t1: T) { function F(t2: U) { - newFunction(); + /*RENAME*/newFunction(); function newFunction() { t2.toString(); @@ -17,7 +17,7 @@ function F(t1: T) { // ==SCOPE::inner function in function 'F'== function F(t1: T) { function F(t2: U) { - newFunction(t2); + /*RENAME*/newFunction(t2); } function newFunction(t2: U) { @@ -27,7 +27,7 @@ function F(t1: T) { // ==SCOPE::function in global scope== function F(t1: T) { function F(t2: U) { - newFunction(t2); + /*RENAME*/newFunction(t2); } } function newFunction(t2: U) { diff --git a/tests/baselines/reference/extractMethod/extractMethod16.ts b/tests/baselines/reference/extractMethod/extractMethod16.ts index 2ecb070366059..e58bbf576c6f8 100644 --- a/tests/baselines/reference/extractMethod/extractMethod16.ts +++ b/tests/baselines/reference/extractMethod/extractMethod16.ts @@ -4,7 +4,7 @@ function F() { } // ==SCOPE::inner function in function 'F'== function F() { - const array: T[] = newFunction(); + const array: T[] = /*RENAME*/newFunction(); function newFunction(): T[] { return []; @@ -12,7 +12,7 @@ function F() { } // ==SCOPE::function in global scope== function F() { - const array: T[] = newFunction(); + const array: T[] = /*RENAME*/newFunction(); } function newFunction(): T[] { return []; diff --git a/tests/baselines/reference/extractMethod/extractMethod17.ts b/tests/baselines/reference/extractMethod/extractMethod17.ts index d0401b6b47202..f79abcca7924f 100644 --- a/tests/baselines/reference/extractMethod/extractMethod17.ts +++ b/tests/baselines/reference/extractMethod/extractMethod17.ts @@ -7,7 +7,7 @@ class C { // ==SCOPE::method in class 'C'== class C { M(t1: T1, t2: T2) { - this.newFunction(t1); + this./*RENAME*/newFunction(t1); } private newFunction(t1: T1) { @@ -17,7 +17,7 @@ class C { // ==SCOPE::function in global scope== class C { M(t1: T1, t2: T2) { - newFunction(t1); + /*RENAME*/newFunction(t1); } } function newFunction(t1: T1) { diff --git a/tests/baselines/reference/extractMethod/extractMethod18.ts b/tests/baselines/reference/extractMethod/extractMethod18.ts index 85ef9a5d5c0dd..122eced75d5f2 100644 --- a/tests/baselines/reference/extractMethod/extractMethod18.ts +++ b/tests/baselines/reference/extractMethod/extractMethod18.ts @@ -7,7 +7,7 @@ class C { // ==SCOPE::method in class 'C'== class C { M(t1: T1, t2: T2) { - this.newFunction(t1); + this./*RENAME*/newFunction(t1); } private newFunction(t1: T1) { @@ -17,7 +17,7 @@ class C { // ==SCOPE::function in global scope== class C { M(t1: T1, t2: T2) { - newFunction(t1); + /*RENAME*/newFunction(t1); } } function newFunction(t1: T1) { diff --git a/tests/baselines/reference/extractMethod/extractMethod19.ts b/tests/baselines/reference/extractMethod/extractMethod19.ts index 80d3c61d1ee94..61d35f97db5bc 100644 --- a/tests/baselines/reference/extractMethod/extractMethod19.ts +++ b/tests/baselines/reference/extractMethod/extractMethod19.ts @@ -4,7 +4,7 @@ function F(v: V) { } // ==SCOPE::inner function in function 'F'== function F(v: V) { - newFunction(); + /*RENAME*/newFunction(); function newFunction() { v.toString(); @@ -12,7 +12,7 @@ function F(v: V) { } // ==SCOPE::function in global scope== function F(v: V) { - newFunction(v); + /*RENAME*/newFunction(v); } function newFunction(v: V) { v.toString(); diff --git a/tests/baselines/reference/extractMethod/extractMethod2.ts b/tests/baselines/reference/extractMethod/extractMethod2.ts index 17ca6a6ba226f..b83cc6f32c6b1 100644 --- a/tests/baselines/reference/extractMethod/extractMethod2.ts +++ b/tests/baselines/reference/extractMethod/extractMethod2.ts @@ -20,7 +20,7 @@ namespace A { namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { let y = 5; @@ -38,7 +38,7 @@ namespace A { namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); } function newFunction() { @@ -56,7 +56,7 @@ namespace A { namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); } } @@ -74,7 +74,7 @@ namespace A { namespace B { function a() { - return newFunction(x, foo); + return /*RENAME*/newFunction(x, foo); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod20.ts b/tests/baselines/reference/extractMethod/extractMethod20.ts index 7d65bfca1a93b..6e0148e099197 100644 --- a/tests/baselines/reference/extractMethod/extractMethod20.ts +++ b/tests/baselines/reference/extractMethod/extractMethod20.ts @@ -8,7 +8,7 @@ const _ = class { // ==SCOPE::method in anonymous class expression== const _ = class { a() { - return this.newFunction(); + return this./*RENAME*/newFunction(); } private newFunction() { @@ -19,7 +19,7 @@ const _ = class { // ==SCOPE::function in global scope== const _ = class { a() { - return newFunction(); + return /*RENAME*/newFunction(); } } function newFunction() { diff --git a/tests/baselines/reference/extractMethod/extractMethod21.ts b/tests/baselines/reference/extractMethod/extractMethod21.ts index 6fb5fc4315526..2c4ffd1bdb831 100644 --- a/tests/baselines/reference/extractMethod/extractMethod21.ts +++ b/tests/baselines/reference/extractMethod/extractMethod21.ts @@ -7,7 +7,7 @@ function foo() { // ==SCOPE::inner function in function 'foo'== function foo() { let x = 10; - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { x++; @@ -17,7 +17,7 @@ function foo() { // ==SCOPE::function in global scope== function foo() { let x = 10; - x = newFunction(x); + x = /*RENAME*/newFunction(x); return; } function newFunction(x: number) { diff --git a/tests/baselines/reference/extractMethod/extractMethod22.ts b/tests/baselines/reference/extractMethod/extractMethod22.ts index 1bb76ef67ba3a..990bfdf0575b9 100644 --- a/tests/baselines/reference/extractMethod/extractMethod22.ts +++ b/tests/baselines/reference/extractMethod/extractMethod22.ts @@ -11,7 +11,7 @@ function test() { try { } finally { - return newFunction(); + return /*RENAME*/newFunction(); } function newFunction() { @@ -23,7 +23,7 @@ function test() { try { } finally { - return newFunction(); + return /*RENAME*/newFunction(); } } function newFunction() { diff --git a/tests/baselines/reference/extractMethod/extractMethod23.ts b/tests/baselines/reference/extractMethod/extractMethod23.ts index 0c56434447ebf..b9bc4264ea625 100644 --- a/tests/baselines/reference/extractMethod/extractMethod23.ts +++ b/tests/baselines/reference/extractMethod/extractMethod23.ts @@ -10,7 +10,7 @@ namespace NS { namespace NS { function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { return 1; @@ -22,7 +22,7 @@ namespace NS { namespace NS { function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); } function newFunction() { return 1; @@ -34,7 +34,7 @@ namespace NS { namespace NS { function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); } function M3() { } } diff --git a/tests/baselines/reference/extractMethod/extractMethod24.ts b/tests/baselines/reference/extractMethod/extractMethod24.ts index 0b33289708fbc..a9dc25d32ea89 100644 --- a/tests/baselines/reference/extractMethod/extractMethod24.ts +++ b/tests/baselines/reference/extractMethod/extractMethod24.ts @@ -10,7 +10,7 @@ function Outer() { function Outer() { function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { return 1; @@ -22,7 +22,7 @@ function Outer() { function Outer() { function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); } function newFunction() { return 1; @@ -34,7 +34,7 @@ function Outer() { function Outer() { function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); } function M3() { } } diff --git a/tests/baselines/reference/extractMethod/extractMethod25.ts b/tests/baselines/reference/extractMethod/extractMethod25.ts index c77ec1abbfd69..dc376781346c3 100644 --- a/tests/baselines/reference/extractMethod/extractMethod25.ts +++ b/tests/baselines/reference/extractMethod/extractMethod25.ts @@ -7,7 +7,7 @@ function M3() { } // ==SCOPE::inner function in function 'M2'== function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { return 1; @@ -17,7 +17,7 @@ function M3() { } // ==SCOPE::function in global scope== function M1() { } function M2() { - return newFunction(); + return /*RENAME*/newFunction(); } function newFunction() { return 1; diff --git a/tests/baselines/reference/extractMethod/extractMethod26.ts b/tests/baselines/reference/extractMethod/extractMethod26.ts index 84dc82f7fed8f..b49e8ab950838 100644 --- a/tests/baselines/reference/extractMethod/extractMethod26.ts +++ b/tests/baselines/reference/extractMethod/extractMethod26.ts @@ -10,7 +10,7 @@ class C { class C { M1() { } M2() { - return this.newFunction(); + return this./*RENAME*/newFunction(); } private newFunction() { return 1; @@ -22,7 +22,7 @@ class C { class C { M1() { } M2() { - return newFunction(); + return /*RENAME*/newFunction(); } M3() { } } diff --git a/tests/baselines/reference/extractMethod/extractMethod27.ts b/tests/baselines/reference/extractMethod/extractMethod27.ts index ce21f1e1fed58..0ec214bb5ed18 100644 --- a/tests/baselines/reference/extractMethod/extractMethod27.ts +++ b/tests/baselines/reference/extractMethod/extractMethod27.ts @@ -11,7 +11,7 @@ class C { class C { M1() { } M2() { - return this.newFunction(); + return this./*RENAME*/newFunction(); } constructor() { } private newFunction() { @@ -24,7 +24,7 @@ class C { class C { M1() { } M2() { - return newFunction(); + return /*RENAME*/newFunction(); } constructor() { } M3() { } diff --git a/tests/baselines/reference/extractMethod/extractMethod28.ts b/tests/baselines/reference/extractMethod/extractMethod28.ts index e3d0fc3b9ea94..ab6ce220bd76a 100644 --- a/tests/baselines/reference/extractMethod/extractMethod28.ts +++ b/tests/baselines/reference/extractMethod/extractMethod28.ts @@ -11,7 +11,7 @@ class C { class C { M1() { } M2() { - return this.newFunction(); + return this./*RENAME*/newFunction(); } private newFunction() { return 1; @@ -24,7 +24,7 @@ class C { class C { M1() { } M2() { - return newFunction(); + return /*RENAME*/newFunction(); } M3() { } constructor() { } diff --git a/tests/baselines/reference/extractMethod/extractMethod3.ts b/tests/baselines/reference/extractMethod/extractMethod3.ts index 0d8481c9841e7..0af79791fe7cb 100644 --- a/tests/baselines/reference/extractMethod/extractMethod3.ts +++ b/tests/baselines/reference/extractMethod/extractMethod3.ts @@ -18,7 +18,7 @@ namespace A { namespace B { function* a(z: number) { - return yield* newFunction(); + return yield* /*RENAME*/newFunction(); function* newFunction() { let y = 5; @@ -35,7 +35,7 @@ namespace A { namespace B { function* a(z: number) { - return yield* newFunction(z); + return yield* /*RENAME*/newFunction(z); } function* newFunction(z: number) { @@ -52,7 +52,7 @@ namespace A { namespace B { function* a(z: number) { - return yield* newFunction(z); + return yield* /*RENAME*/newFunction(z); } } @@ -69,7 +69,7 @@ namespace A { namespace B { function* a(z: number) { - return yield* newFunction(z, foo); + return yield* /*RENAME*/newFunction(z, foo); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod4.ts b/tests/baselines/reference/extractMethod/extractMethod4.ts index 4f6a5d85f89f5..e0a636135d45d 100644 --- a/tests/baselines/reference/extractMethod/extractMethod4.ts +++ b/tests/baselines/reference/extractMethod/extractMethod4.ts @@ -20,7 +20,7 @@ namespace A { namespace B { async function a(z: number, z1: any) { - return await newFunction(); + return await /*RENAME*/newFunction(); async function newFunction() { let y = 5; @@ -39,7 +39,7 @@ namespace A { namespace B { async function a(z: number, z1: any) { - return await newFunction(z, z1); + return await /*RENAME*/newFunction(z, z1); } async function newFunction(z: number, z1: any) { @@ -58,7 +58,7 @@ namespace A { namespace B { async function a(z: number, z1: any) { - return await newFunction(z, z1); + return await /*RENAME*/newFunction(z, z1); } } @@ -77,7 +77,7 @@ namespace A { namespace B { async function a(z: number, z1: any) { - return await newFunction(z, z1, foo); + return await /*RENAME*/newFunction(z, z1, foo); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod5.ts b/tests/baselines/reference/extractMethod/extractMethod5.ts index 10ff0005f73fe..fc15f6762e568 100644 --- a/tests/baselines/reference/extractMethod/extractMethod5.ts +++ b/tests/baselines/reference/extractMethod/extractMethod5.ts @@ -23,7 +23,7 @@ namespace A { function a() { let a = 1; - newFunction(); + /*RENAME*/newFunction(); function newFunction() { let y = 5; @@ -43,7 +43,7 @@ namespace A { function a() { let a = 1; - a = newFunction(a); + a = /*RENAME*/newFunction(a); } function newFunction(a: number) { @@ -64,7 +64,7 @@ namespace A { function a() { let a = 1; - a = newFunction(a); + a = /*RENAME*/newFunction(a); } } @@ -85,7 +85,7 @@ namespace A { function a() { let a = 1; - a = newFunction(x, a); + a = /*RENAME*/newFunction(x, a); } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod6.ts b/tests/baselines/reference/extractMethod/extractMethod6.ts index 40135e6ec9724..37112b80e9e13 100644 --- a/tests/baselines/reference/extractMethod/extractMethod6.ts +++ b/tests/baselines/reference/extractMethod/extractMethod6.ts @@ -23,7 +23,7 @@ namespace A { function a() { let a = 1; - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { let y = 5; @@ -44,7 +44,7 @@ namespace A { let a = 1; var __return: any; - ({ __return, a } = newFunction(a)); + ({ __return, a } = /*RENAME*/newFunction(a)); return __return; } @@ -66,7 +66,7 @@ namespace A { let a = 1; var __return: any; - ({ __return, a } = newFunction(a)); + ({ __return, a } = /*RENAME*/newFunction(a)); return __return; } } @@ -88,7 +88,7 @@ namespace A { let a = 1; var __return: any; - ({ __return, a } = newFunction(x, a)); + ({ __return, a } = /*RENAME*/newFunction(x, a)); return __return; } } diff --git a/tests/baselines/reference/extractMethod/extractMethod7.ts b/tests/baselines/reference/extractMethod/extractMethod7.ts index d01532da7969b..8859b7b4fdd1d 100644 --- a/tests/baselines/reference/extractMethod/extractMethod7.ts +++ b/tests/baselines/reference/extractMethod/extractMethod7.ts @@ -27,7 +27,7 @@ namespace A { function a() { let a = 1; - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { let y = 5; @@ -50,7 +50,7 @@ namespace A { let a = 1; var __return: any; - ({ __return, a } = newFunction(a)); + ({ __return, a } = /*RENAME*/newFunction(a)); return __return; } @@ -74,7 +74,7 @@ namespace A { let a = 1; var __return: any; - ({ __return, a } = newFunction(a)); + ({ __return, a } = /*RENAME*/newFunction(a)); return __return; } } @@ -98,7 +98,7 @@ namespace A { let a = 1; var __return: any; - ({ __return, a } = newFunction(x, a)); + ({ __return, a } = /*RENAME*/newFunction(x, a)); return __return; } } diff --git a/tests/baselines/reference/extractMethod/extractMethod8.ts b/tests/baselines/reference/extractMethod/extractMethod8.ts index d59ca5dc2383a..cb06470d3855f 100644 --- a/tests/baselines/reference/extractMethod/extractMethod8.ts +++ b/tests/baselines/reference/extractMethod/extractMethod8.ts @@ -14,7 +14,7 @@ namespace A { namespace B { function a() { let a1 = 1; - return newFunction() + 100; + return /*RENAME*/newFunction() + 100; function newFunction() { return 1 + a1 + x; @@ -28,7 +28,7 @@ namespace A { namespace B { function a() { let a1 = 1; - return newFunction(a1) + 100; + return /*RENAME*/newFunction(a1) + 100; } function newFunction(a1: number) { @@ -42,7 +42,7 @@ namespace A { namespace B { function a() { let a1 = 1; - return newFunction(a1) + 100; + return /*RENAME*/newFunction(a1) + 100; } } @@ -56,7 +56,7 @@ namespace A { namespace B { function a() { let a1 = 1; - return newFunction(a1, x) + 100; + return /*RENAME*/newFunction(a1, x) + 100; } } } diff --git a/tests/baselines/reference/extractMethod/extractMethod9.ts b/tests/baselines/reference/extractMethod/extractMethod9.ts index 342e3c10eeec7..022dab8236397 100644 --- a/tests/baselines/reference/extractMethod/extractMethod9.ts +++ b/tests/baselines/reference/extractMethod/extractMethod9.ts @@ -13,7 +13,7 @@ namespace A { export interface I { x: number }; namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); function newFunction() { let a1: I = { x: 1 }; @@ -27,7 +27,7 @@ namespace A { export interface I { x: number }; namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); } function newFunction() { @@ -41,7 +41,7 @@ namespace A { export interface I { x: number }; namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); } } @@ -55,7 +55,7 @@ namespace A { export interface I { x: number }; namespace B { function a() { - return newFunction(); + return /*RENAME*/newFunction(); } } } diff --git a/tests/cases/fourslash/extract-method-formatting.ts b/tests/cases/fourslash/extract-method-formatting.ts index a346ad3bbd995..e4193fd8db390 100644 --- a/tests/cases/fourslash/extract-method-formatting.ts +++ b/tests/cases/fourslash/extract-method-formatting.ts @@ -10,10 +10,8 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_1", actionDescription: "Extract to function in global scope", -}); -verify.currentFileContentIs( -`function f(x: number): number { - return newFunction(x); + newContent: `function f(x: number): number { + return /*RENAME*/newFunction(x); } function newFunction(x: number) { switch (x) { @@ -21,4 +19,5 @@ function newFunction(x: number) { return 0; } } -`); +` +}); diff --git a/tests/cases/fourslash/extract-method-uniqueName.ts b/tests/cases/fourslash/extract-method-uniqueName.ts new file mode 100644 index 0000000000000..44f026ae8a80e --- /dev/null +++ b/tests/cases/fourslash/extract-method-uniqueName.ts @@ -0,0 +1,20 @@ +/// + +////// newFunction +/////*start*/1 + 1/*end*/; + +goTo.select('start', 'end') +edit.applyRefactor({ + refactorName: "Extract Method", + actionName: "scope_0", + actionDescription: "Extract to function in global scope", + newContent: +`// newFunction +/*RENAME*/newFunction_1(); + +function newFunction_1() { + // newFunction + 1 + 1; +} +` +}); diff --git a/tests/cases/fourslash/extract-method1.ts b/tests/cases/fourslash/extract-method1.ts index 64dffc15a9059..a8d421923b14e 100644 --- a/tests/cases/fourslash/extract-method1.ts +++ b/tests/cases/fourslash/extract-method1.ts @@ -17,11 +17,10 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to method in class 'Foo'", -}); -verify.currentFileContentIs( + newContent: `class Foo { someMethod(m: number) { - this.newFunction(m); + this./*RENAME*/newFunction(m); var q = 10; return q; } @@ -33,4 +32,5 @@ verify.currentFileContentIs( var z = y + x; console.log(z); } -}`); +}` +}); diff --git a/tests/cases/fourslash/extract-method10.ts b/tests/cases/fourslash/extract-method10.ts index 73ef3029e24cd..215196d6c51dc 100644 --- a/tests/cases/fourslash/extract-method10.ts +++ b/tests/cases/fourslash/extract-method10.ts @@ -8,4 +8,11 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: 'scope_0', actionDescription: "Extract to function in module scope", + newContent: +`export {}; // Make this a module +(x => x)(/*RENAME*/newFunction())(1); +function newFunction(): (x: any) => any { + return x => x; +} +` }); diff --git a/tests/cases/fourslash/extract-method13.ts b/tests/cases/fourslash/extract-method13.ts index fa84ec4fb62d8..274753fd5cd11 100644 --- a/tests/cases/fourslash/extract-method13.ts +++ b/tests/cases/fourslash/extract-method13.ts @@ -14,6 +14,16 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to method in class 'C'", + newContent: +`class C { + static j = 1 + 1; + constructor(q: string = C./*RENAME*/newFunction()) { + } + + private static newFunction(): string { + return "a" + "b"; + } +}` }); verify.currentFileContentIs(`class C { @@ -31,10 +41,9 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to method in class 'C'", -}); - -verify.currentFileContentIs(`class C { - static j = C.newFunction_1(); + newContent: +`class C { + static j = C./*RENAME*/newFunction_1(); constructor(q: string = C.newFunction()) { } @@ -45,4 +54,5 @@ verify.currentFileContentIs(`class C { private static newFunction(): string { return "a" + "b"; } -}`); \ No newline at end of file +}` +}); diff --git a/tests/cases/fourslash/extract-method14.ts b/tests/cases/fourslash/extract-method14.ts index 770ed3d0fcbe5..27a561743c65e 100644 --- a/tests/cases/fourslash/extract-method14.ts +++ b/tests/cases/fourslash/extract-method14.ts @@ -15,14 +15,15 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_1", actionDescription: "Extract to function in global scope", -}); -verify.currentFileContentIs(`function foo() { + newContent: +`function foo() { var i = 10; var __return: any; - ({ __return, i } = newFunction(i)); + ({ __return, i } = n/*RENAME*/ewFunction(i)); return __return; } function newFunction(i) { return { __return: i++, i }; } -`); \ No newline at end of file +` +}); diff --git a/tests/cases/fourslash/extract-method15.ts b/tests/cases/fourslash/extract-method15.ts index 8d3db633b11e0..c3db3186cdf9d 100644 --- a/tests/cases/fourslash/extract-method15.ts +++ b/tests/cases/fourslash/extract-method15.ts @@ -13,14 +13,14 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_1", actionDescription: "Extract to function in global scope", -}); - -verify.currentFileContentIs(`function foo() { + newContent: +`function foo() { var i = 10; - i = newFunction(i); + i = /*RENAME*/newFunction(i); } function newFunction(i: number) { i++; return i; } -`); +` +}); diff --git a/tests/cases/fourslash/extract-method18.ts b/tests/cases/fourslash/extract-method18.ts index 6d4d06ca7bfef..8ff1cc3028df8 100644 --- a/tests/cases/fourslash/extract-method18.ts +++ b/tests/cases/fourslash/extract-method18.ts @@ -13,12 +13,13 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_1", actionDescription: "Extract to function in global scope", -}); -verify.currentFileContentIs(`function fn() { + newContent: +`function fn() { const x = { m: 1 }; - newFunction(x); + /*RENAME*/newFunction(x); } function newFunction(x: { m: number; }) { x.m = 3; } -`); +` +}); diff --git a/tests/cases/fourslash/extract-method19.ts b/tests/cases/fourslash/extract-method19.ts index da999fcc093e2..56d6b02560fdd 100644 --- a/tests/cases/fourslash/extract-method19.ts +++ b/tests/cases/fourslash/extract-method19.ts @@ -13,13 +13,14 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to inner function in function 'fn'", -}); -verify.currentFileContentIs(`function fn() { - newFunction_1(); + newContent: +`function fn() { + /*RENAME*/newFunction_1(); function newFunction_1() { console.log("hi"); } } -function newFunction() { }`); +function newFunction() { }` +}); diff --git a/tests/cases/fourslash/extract-method2.ts b/tests/cases/fourslash/extract-method2.ts index 021716b6e4881..6fbe7394c2f84 100644 --- a/tests/cases/fourslash/extract-method2.ts +++ b/tests/cases/fourslash/extract-method2.ts @@ -14,18 +14,18 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_2", actionDescription: "Extract to function in global scope", -}); -verify.currentFileContentIs( + newContent: `namespace NS { class Q { foo() { console.log('100'); const m = 10, j = "hello", k = {x: "what"}; - const q = newFunction(m, j, k); + const q = /*RENAME*/newFunction(m, j, k); } } } function newFunction(m: number, j: string, k: { x: string; }) { return m + j + k; } -`); +` +}); diff --git a/tests/cases/fourslash/extract-method21.ts b/tests/cases/fourslash/extract-method21.ts index f19d4b05912a8..8e3baf619496f 100644 --- a/tests/cases/fourslash/extract-method21.ts +++ b/tests/cases/fourslash/extract-method21.ts @@ -16,14 +16,14 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to method in class 'Foo'", -}); - -verify.currentFileContentIs(`class Foo { + newContent: +`class Foo { static method() { - return Foo.newFunction(); + return Foo./*RENAME*/newFunction(); } private static newFunction() { return 1; } -}`); \ No newline at end of file +}` +}); diff --git a/tests/cases/fourslash/extract-method24.ts b/tests/cases/fourslash/extract-method24.ts index e5f923bb80d59..5706c5c7a5453 100644 --- a/tests/cases/fourslash/extract-method24.ts +++ b/tests/cases/fourslash/extract-method24.ts @@ -11,13 +11,14 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_1", actionDescription: "Extract to function in global scope", -}); -verify.currentFileContentIs(`function M() { + newContent: +`function M() { let a = [1,2,3]; let x = 0; - console.log(newFunction(a, x)); + console.log(/*RENAME*/newFunction(a, x)); } function newFunction(a: number[], x: number): any { return a[x]; } -`); \ No newline at end of file +` +}); diff --git a/tests/cases/fourslash/extract-method25.ts b/tests/cases/fourslash/extract-method25.ts index d18d0691e6130..4fb2193adf3bf 100644 --- a/tests/cases/fourslash/extract-method25.ts +++ b/tests/cases/fourslash/extract-method25.ts @@ -12,12 +12,13 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to inner function in function 'fn'", -}); -verify.currentFileContentIs(`function fn() { - var q = newFunction() + newContent: +`function fn() { + var q = /*RENAME*/newFunction() q[0]++ function newFunction() { return [0]; } -}`); +}` +}); diff --git a/tests/cases/fourslash/extract-method5.ts b/tests/cases/fourslash/extract-method5.ts index 014dfb35d0814..b27d9a8209bda 100644 --- a/tests/cases/fourslash/extract-method5.ts +++ b/tests/cases/fourslash/extract-method5.ts @@ -13,13 +13,12 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to inner function in function 'f'", -}); -// TODO: GH#18091 (fix formatting to use `2 ? 1 :` and not `2?1:`) -verify.currentFileContentIs( + newContent: `function f() { - var x: 1 | 2 | 3 = newFunction(); + var x: 1 | 2 | 3 = /*RENAME*/newFunction(); function newFunction(): 1 | 2 | 3 { return 1 + 1 === 2 ? 1 : 2; } -}`); \ No newline at end of file +}` +}); diff --git a/tests/cases/fourslash/extract-method7.ts b/tests/cases/fourslash/extract-method7.ts index d8459bf77ad6d..c28e12dce8c8f 100644 --- a/tests/cases/fourslash/extract-method7.ts +++ b/tests/cases/fourslash/extract-method7.ts @@ -11,10 +11,11 @@ edit.applyRefactor({ refactorName: "Extract Method", actionName: "scope_0", actionDescription: "Extract to function in global scope", -}); -verify.currentFileContentIs(`function fn(x = newFunction()) { + newContent: +`function fn(x = /*RENAME*/newFunction()) { } function newFunction() { return 1 + 1; } -`); +` +}); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 119780872e9b4..7901d9550cb59 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -310,7 +310,7 @@ declare namespace FourSlashInterface { enableFormatting(): void; disableFormatting(): void; - applyRefactor(options: { refactorName: string, actionName: string, actionDescription: string }): void; + applyRefactor(options: { refactorName: string, actionName: string, actionDescription: string, newContent: string }): void; } class debug { printCurrentParameterHelp(): void;