diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 09b0f72129255..678a9dd753919 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4871,5 +4871,21 @@ "Enable the 'experimentalDecorators' option in your configuration file": { "category": "Message", "code": 95074 + }, + "Inline local": { + "category": "Message", + "code": 95080 + }, + "Inline here": { + "category": "Message", + "code": 95081 + }, + "Inline all": { + "category": "Message", + "code": 95082 + }, + "Inline function": { + "category": "Message", + "code": 95083 } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index aa2ebe3d60c07..48c690e47b11b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -137,6 +137,25 @@ namespace ts { } } + /** + * Collects all descendants whose predicate results in a truthy value. + * If no descendant is found, returns empty array. + */ + export function findDescendants(node: Node | undefined, predicate: (element: Node) => element is T): ReadonlyArray; + export function findDescendants(node: Node | undefined, predicate: (element: Node) => boolean): ReadonlyArray; + export function findDescendants(node: Node, predicate: (n: Node) => boolean) { + const nodes: Node[] = []; + collectDescendants(node); + return nodes; + + function collectDescendants(node: Node) { + forEachChild(node, n => { + if (predicate(n)) nodes.push(n); + collectDescendants(n); + }); + } + } + /** * Calls `callback` for each entry in the map, returning the first truthy result. * Use `map.forEach` instead for normal iteration. diff --git a/src/services/refactors/inlineSymbol.ts b/src/services/refactors/inlineSymbol.ts new file mode 100644 index 0000000000000..65bfd03950cd8 --- /dev/null +++ b/src/services/refactors/inlineSymbol.ts @@ -0,0 +1,547 @@ +/* @internal */ +namespace ts.refactor.inlineSymbol { + const inlineHereActionName = "Inline here"; + const inlineAllActionName = "Inline all"; + + const inlineHereActionDescription = getLocaleSpecificMessage(Diagnostics.Inline_here); + const inlineAllActionDescription = getLocaleSpecificMessage(Diagnostics.Inline_all); + + namespace inlineLocal { + export const refactorName = "Inline local"; + const refactorDescription = getLocaleSpecificMessage(Diagnostics.Inline_local); + + interface Info { + readonly declaration: VariableDeclaration; + readonly usages: ReadonlyArray; + readonly selectedUsage: Identifier | undefined; + } + + export function getAvailableActions(context: RefactorContext): ReadonlyArray { + const { file, program, startPosition } = context; + const info = getLocalInfo(file, program, startPosition); + if (!info) return emptyArray; + const { selectedUsage } = info; + const refactorInfo = { + name: refactorName, + description: refactorDescription, + actions: [{ + name: inlineAllActionName, + description: inlineAllActionDescription + }] + }; + if (selectedUsage) { + refactorInfo.actions.push({ + name: inlineHereActionName, + description: inlineHereActionDescription + }); + } + return [refactorInfo]; + } + + function getLocalInfo(file: SourceFile, program: Program, startPosition: number): Info | undefined { + const token = getTokenAtPosition(file, startPosition); + const maybeDeclaration = token.parent; + const checker = program.getTypeChecker(); + if (isLocalVariable(maybeDeclaration)) { + return createInfo(checker, maybeDeclaration); + } + if (isIdentifier(token)) { + const symbol = checker.getSymbolAtLocation(token); + if (!symbol) return undefined; + const declaration = symbol.valueDeclaration; + if (!isLocalVariable(declaration)) return undefined; + return createInfo(checker, declaration, token); + } + } + + function isLocalVariable(node: Node): node is VariableDeclaration { + return node && isVariableDeclaration(node) && isVariableDeclarationInVariableStatement(node); + } + + function createInfo( + checker: TypeChecker, + declaration: VariableDeclaration, + selectedUsage?: Identifier + ): Info | undefined { + const name = declaration.name; + const usages = getUsagesInScope(getEnclosingBlockScopeContainer(name), name, checker); + return canInline(declaration, usages) ? { + declaration, + usages, + selectedUsage + } : undefined; + } + + function canInline(declaration: VariableDeclaration, usages: ReadonlyArray) { + let hasErrors = false; + if (!declaration.initializer) hasErrors = true; + if (containsProhibitedModifiers(declaration.parent.parent.modifiers)) hasErrors = true; + forEach(usages, usage => { + if (isAssigned(usage)) hasErrors = true; + }); + return !hasErrors; + } + + function isAssigned(usage: Identifier) { + const assignment = findAncestor(usage, isAssignmentExpression)!; + return assignment && assignment.left === usage; + } + + function containsProhibitedModifiers(modifiers?: NodeArray) { + return !!modifiers && !!find(modifiers, mod => mod.kind === SyntaxKind.ExportKeyword); + } + + export function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + const { file, program, startPosition } = context; + const info = getLocalInfo(file, program, startPosition); + if (!info) return undefined; + const { declaration, usages, selectedUsage } = info; + switch (actionName) { + case inlineAllActionName: + return { edits: getInlineAllEdits(context, declaration, usages) }; + case inlineHereActionName: + return { edits: getInlineHereEdits(context, declaration, selectedUsage!) }; + default: + return Debug.fail("invalid action"); + } + } + + function getInlineAllEdits( + context: RefactorContext, + declaration: VariableDeclaration, + usages: ReadonlyArray): FileTextChanges[] { + const { file } = context; + return textChanges.ChangeTracker.with(context, t => { + forEach(usages, oldNode => { + const { initializer } = declaration; + const clone = getSynthesizedDeepClone(initializer!); + const expression = parenthesizeIfNecessary(oldNode, clone); + t.replaceNode(file, oldNode, expression); + }); + t.delete(file, declaration); + }); + } + + function getInlineHereEdits( + context: RefactorContext, + declaration: VariableDeclaration, + selectedUsage: Identifier): FileTextChanges[] { + const { file } = context; + return textChanges.ChangeTracker.with(context, t => { + const { initializer } = declaration; + const clone = getSynthesizedDeepClone(initializer!); + const expression = parenthesizeIfNecessary(selectedUsage, clone); + t.replaceNode(file, selectedUsage, expression); + }); + } + + function getUsagesInScope(scope: Node, target: Node, checker: TypeChecker): ReadonlyArray { + const symbol = checker.getSymbolAtLocation(target); + return findDescendants(scope, n => + checker.getSymbolAtLocation(n) === symbol && + !isDeclaration(n.parent)) as Identifier[]; + } + } + + namespace inlineFunction { + export const refactorName = "Inline function"; + const refactorDescription = getLocaleSpecificMessage(Diagnostics.Inline_function); + + type InlineableFunction = FunctionDeclaration | MethodDeclaration; + + interface Info { + readonly declaration: InlineableFunction; + readonly usages: ReadonlyArray; + readonly selectedUsage: CallExpression | undefined; + readonly allAvailable: boolean; + readonly selectedAvailable: boolean; + } + + export function getAvailableActions(context: RefactorContext): ReadonlyArray { + const { program, file, startPosition } = context; + const info = getInfo(program, file, startPosition); + if (!info) return emptyArray; + const { allAvailable, selectedAvailable } = info; + const refactorInfo: ApplicableRefactorInfo = { + name: refactorName, + description: refactorDescription, + actions: [] + }; + if (allAvailable) { + refactorInfo.actions.push({ + name: inlineAllActionName, + description: inlineAllActionDescription + }); + } + if (selectedAvailable) { + refactorInfo.actions.push({ + name: inlineHereActionName, + description: inlineHereActionDescription + }); + } + return [refactorInfo]; + } + + function areSymbolsAccessible(checker: TypeChecker, symbols: ReadonlyArray, target: Node) { + return every(symbols, symbol => { + const symbolAccessibility = checker.isSymbolAccessible( + symbol, + target, + SymbolFlags.All, + /* shouldComputeAliasesToMakeVisible */ false).accessibility; + return symbolAccessibility === SymbolAccessibility.Accessible; + }); + } + + function getExternalSymbolsReferencedInScope(declaration: InlineableFunction, checker: TypeChecker) { + const ids = findDescendants(declaration, isIdentifier); + const visited = createMap(); + forEach(ids, id => { + const symbol = checker.getSymbolAtLocation(id); + if (!symbol) return; + const symbolId = String(getSymbolId(symbol)); + if (!visited.has(symbolId)) visited.set(symbolId, symbol); + }); + const symbols = arrayFrom(visited.values()); + return filter(symbols, s => { + if (s.valueDeclaration) { + const symbolScope = getEnclosingBlockScopeContainer(s.valueDeclaration); + return !(declaration === symbolScope || isAncestor(symbolScope, declaration)); + } + return false; + }); + } + + function getInfo(program: Program, file: SourceFile, startPosition: number): Info | undefined { + const token = getTokenAtPosition(file, startPosition); + const checker = program.getTypeChecker(); + if (isNameOfFunctionDeclaration(token)) { + return createInfo(checker, token.parent); + } + + const call = findAncestor(token, isCallExpression); + if (!call) return undefined; + const symbol = checker.getSymbolAtLocation(call.expression); + if (!symbol) return undefined; + const declaration = symbol.valueDeclaration; + if (!isInlineableFunction(declaration)) return undefined; + return createInfo(checker, declaration, call); + } + + function createInfo( + checker: TypeChecker, + declaration: InlineableFunction, + call?: CallExpression + ): Info | undefined { + const usages = getCallsInFile( + declaration.getSourceFile(), + declaration, + checker); + const { allAvailable, selectedAvailable } = canInline(checker, declaration, usages, call); + return { + declaration, + usages, + selectedUsage: call, + allAvailable, + selectedAvailable + }; + } + + function getCallsInFile( + file: SourceFile, + target: InlineableFunction, + checker: TypeChecker + ): ReadonlyArray { + const calls: CallExpression[] = []; + FindAllReferences.Core.eachSignatureCall(target, [file], checker, c => { calls.push(c); }); + return calls; + } + + function canInline( + checker: TypeChecker, + declaration: InlineableFunction, + usages: ReadonlyArray, + call?: CallExpression + ) { + let anyHaveErrors = false; + let selectedHasErrors = false; + const body = declaration.body; + if (!body || body.statements.length === 0) { + anyHaveErrors = true; + selectedHasErrors = true; + } + if (containsExportKeyword(declaration.modifiers)) { + anyHaveErrors = true; + } + const symbols = getExternalSymbolsReferencedInScope(declaration, checker); + if (!every(usages, u => areSymbolsAccessible(checker, symbols, u))) anyHaveErrors = true; + if (!call || !areSymbolsAccessible(checker, symbols, call)) selectedHasErrors = true; + return { allAvailable: !anyHaveErrors, selectedAvailable: !selectedHasErrors }; + } + + function containsExportKeyword(modifiers?: NodeArray): boolean { + return !!modifiers && !!find(modifiers, mod => mod.kind === SyntaxKind.ExportKeyword); + } + + export function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + const { file, program, startPosition } = context; + const info = getInfo(program, file, startPosition); + if (!info) return undefined; + const { declaration, usages, selectedUsage } = info; + + switch (actionName) { + case inlineAllActionName: + return { edits: getInlineAllEdits(context, declaration, usages) }; + case inlineHereActionName: + return { edits: getInlineHereEdits(context, declaration, selectedUsage!) }; + default: + return Debug.fail("invalid action"); + } + } + + function getInlineAllEdits( + context: RefactorContext, + declaration: InlineableFunction, + usages: ReadonlyArray + ): FileTextChanges[] { + const { file } = context; + return textChanges.ChangeTracker.with(context, t => { + forEach(usages, oldNode => { + inlineAt(context, t, oldNode, declaration); + }); + t.delete(file, declaration); + }); + } + + function getInlineHereEdits( + context: RefactorContext, + declaration: InlineableFunction, + selectedUsage: CallExpression + ): FileTextChanges[] { + return textChanges.ChangeTracker.with(context, t => { + inlineAt(context, t, selectedUsage, declaration); + }); + } + + function inlineAt( + context: RefactorContext, + t: textChanges.ChangeTracker, + targetNode: CallExpression, + declaration: InlineableFunction + ) { + const { file } = context; + const statement = findAncestor(targetNode, isStatement)!; + const { statements, returnExpression } = getInlineInfo(context, declaration, targetNode); + + forEach(statements, st => { t.insertNodeBefore(file, statement, st); }); + + if (returnExpression) { + t.replaceNode(file, targetNode, returnExpression); + } + else { + /* deleteNode does not work here, because it deletes from fullStart to end, + * but replaceNode only replaces from start. This creates a runtime error of + * overlapping edits because of the inserts made before. + */ + t.deleteRange(file, { pos: statement.getStart(), end: statement.getEnd() }); + } + } + + function getInlineInfo(context: RefactorContext, declaration: InlineableFunction, targetNode: CallExpression) { + const { file, program } = context; + const { parameters, body } = declaration; + const checker = program.getTypeChecker(); + const renameMap = getConflictingNames(checker, declaration, targetNode); + const isVoid = returnTypeIsVoidLike(checker, declaration); + const returns = findDescendants(body!, n => isReturnStatement(n) && getEnclosingBlockScopeContainer(n) === declaration); + const nofReturns = returns.length; + const transformedBody = visitEachChild(body!, transformVisitor, nullTransformationContext); + + const statements = [] as Statement[]; + statements.push(...getVariableDeclarationsFromParameters(parameters, targetNode, transformVisitor)); + const returnVariableStatement = getVariableDeclarationFromReturn(file, declaration, nofReturns, isVoid); + if (returnVariableStatement) statements.push(returnVariableStatement); + statements.push(...filter(transformedBody.statements, s => !isReturnStatement(s))); + const returnExpression = getReturnExpression(file, targetNode, transformedBody, isVoid, nofReturns); + return { statements, returnExpression }; + + function transformVisitor(node: Node): VisitResult { + if (isIdentifier(node)) { + const symbol = checker.getSymbolAtLocation(node); + if (symbol) return getSafeName(renameMap, node, symbol); + } + if (isObjectBindingElementWithoutPropertyName(node)) { + const name = node.name; + const symbol = checker.getSymbolAtLocation(name); + if (symbol) { + const safeName = getSafeName(renameMap, name, symbol); + if (safeName !== name) { + return createBindingElement(node.dotDotDotToken, name, safeName, node.initializer); + } + } + } + if (isMethodDeclaration(declaration) && isThisProperty(node)) { + const expr = targetNode.expression; + if (isPropertyAccessExpression(expr)) { + const propertyAccess = node; + return createPropertyAccess(expr.expression, propertyAccess.name); + } + } + if (isReturnStatement(node) && nofReturns > 1) { + const expression = node.expression; + if (expression) { + const safeExpression = visitNode(expression, transformVisitor); + const assignment = createAssignment(createReturnVariableName(file), safeExpression); + return createExpressionStatement(assignment); + } + } + return visitEachChild(node, transformVisitor, nullTransformationContext); + } + } + + function getSafeName(renameMap: Map, name: Identifier, { id }: Symbol): Identifier { + if (renameMap.has(String(id))) { + return createIdentifier(renameMap.get(String(id))!); + } + return name; + } + + function getReturnExpression( + file: SourceFile, + targetNode: Node, + transformedBody: Block, + isVoid: boolean, + nofReturns: number + ) { + if (nofReturns === 1 && !isVoid) { + const returnExpression = forEachReturnStatement(transformedBody, r => r.expression)!; + return parenthesizeIfNecessary(targetNode, returnExpression); + } + else if (nofReturns > 1 && !isVoid) { + return createReturnVariableName(file); + } + return undefined; + } + + function createReturnVariableName(file: SourceFile) { + return createIdentifier(getUniqueName("returnValue", file)); + } + + function getVariableDeclarationsFromParameters( + parameters: NodeArray, + targetNode: CallExpression, + transformVisitor: (node: Node) => VisitResult + ) { + return map(parameters, (p, i) => { + const oldName = p.name; + const typeArguments = targetNode.typeArguments; + const typeNode = typeArguments && typeArguments[i]; + const argument = targetNode.arguments[i]; + const initializer = argument ? argument : p.initializer; + const name = visitNode(oldName, transformVisitor); + return createVariable(name, typeNode, NodeFlags.Const, initializer); + }); + } + + function getVariableDeclarationFromReturn( + file: SourceFile, + declaration: InlineableFunction, + nofReturns: number, + isVoid: boolean + ) { + if (nofReturns > 1 && !isVoid) { + const typeNode = getEffectiveReturnTypeNode(declaration); + return createVariable( + createReturnVariableName(file), + typeNode, + NodeFlags.Let, + /* initializer */ undefined); + } + return undefined; + } + + function returnTypeIsVoidLike(checker: TypeChecker, declaration: InlineableFunction) { + const signature = checker.getSignatureFromDeclaration(declaration)!; + const returnType = checker.getReturnTypeOfSignature(signature); + const isVoid = !!(returnType.flags & TypeFlags.VoidLike); + return isVoid; + } + + function getConflictingNames(checker: TypeChecker, scope: Node, targetNode: Node) { + const renameMap = createMap(); + const sourceSymbols = checker.getSymbolsInScope(scope, SymbolFlags.All); + const localSymbols = filter(sourceSymbols, s => { + if (s.valueDeclaration) { + const declScope = getEnclosingBlockScopeContainer(s.valueDeclaration); + return scope === declScope || isAncestor(declScope, scope); + } + return false; + }); + const symbols = checker.getSymbolsInScope(targetNode, SymbolFlags.All); + forEach(localSymbols, s => { + const declaration = s.valueDeclaration; + if (!isNamedDeclaration(declaration)) return; + const name = declaration.name; + if (!isIdentifier(name)) return; + if (nameIsTaken(symbols, s)) { + const safeName = getUniqueName(name.text, targetNode.getSourceFile()); + renameMap.set(String(getSymbolId(s)), safeName); + } + }); + return renameMap; + } + + function isAncestor(node: Node, ancestor: Node) { + return !!findAncestor(node, n => n === ancestor); + } + + function createVariable( + name: BindingName, + type?: TypeNode | undefined, + flags?: NodeFlags, + initializer?: Expression + ) { + const declaration = createVariableDeclaration(name, type, initializer); + const declarationList = createVariableDeclarationList([declaration], flags); + const variableStatement = createVariableStatement(/* modifiers */ undefined, declarationList); + return variableStatement; + } + + function nameIsTaken(symbols: Symbol[], symbol: Symbol) { + return some(symbols, s => s.name === symbol.name && s !== symbol); + } + + function isInlineableFunction(node: Node): node is InlineableFunction { + return isFunctionDeclaration(node) || isMethodDeclaration(node); + } + } + + registerRefactor(inlineLocal.refactorName, { getEditsForAction: inlineLocal.getEditsForAction, getAvailableActions: inlineLocal.getAvailableActions }); + registerRefactor(inlineFunction.refactorName, { getEditsForAction: inlineFunction.getEditsForAction, getAvailableActions: inlineFunction.getAvailableActions }); + + function parenthesizeIfNecessary(target: Node, expression: Expression): Expression { + const parent = target.parent; + if (isBinaryExpression(parent)) { + const parentOperatorKind = parent.operatorToken.kind; + if (parentOperatorKind === SyntaxKind.AsteriskAsteriskToken && isUnaryExpression(expression)) { + return createParen(expression); + } + return parenthesizeBinaryOperand( + parentOperatorKind, + expression, + target === parent.left, + parent.left); + } + if (isExpression(parent)) { + const parentPrecedence = getExpressionPrecedence(parent); + const expressionPrecedence = getExpressionPrecedence(expression); + if (parentPrecedence > expressionPrecedence) { + return createParen(expression); + } + else { + return expression; + } + } + return expression; + } +} \ No newline at end of file diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 21be663055ae4..c432e452760b2 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -81,6 +81,7 @@ "refactors/convertExport.ts", "refactors/convertImport.ts", "refactors/extractSymbol.ts", + "refactors/inlineSymbol.ts", "refactors/generateGetAccessorAndSetAccessor.ts", "refactors/moveToNewFile.ts", "refactors/addOrRemoveBracesToArrowFunction.ts", diff --git a/tests/cases/fourslash/refactorInlineFunction_Availability_Closure.ts b/tests/cases/fourslash/refactorInlineFunction_Availability_Closure.ts new file mode 100644 index 0000000000000..defcb4fc0f018 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Availability_Closure.ts @@ -0,0 +1,15 @@ +/// + +//// namespace a { +//// const meaningOfLife = 42; +//// export function /*z*/foo/*y*/() { return meaningOfLife; } +//// } +//// function bar() { const meaningOfLife = /*x*/a.foo/*w*/(); } + +goTo.select("z", "y"); +verify.not.refactorAvailable("Inline function", "Inline all"); +verify.not.refactorAvailable("Inline function", "Inline here"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Inline function", "Inline all"); +verify.not.refactorAvailable("Inline function", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineFunction_Availability_EmptyBody.ts b/tests/cases/fourslash/refactorInlineFunction_Availability_EmptyBody.ts new file mode 100644 index 0000000000000..5fa37e3fb1d3d --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Availability_EmptyBody.ts @@ -0,0 +1,12 @@ +/// + +//// function /*z*/foo/*y*/() {} +//// function bar() { /*x*/foo/*w*/(); } + +goTo.select("z", "y"); +verify.not.refactorAvailable("Inline function", "Inline all"); +verify.not.refactorAvailable("Inline function", "Inline here"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Inline function", "Inline all"); +verify.not.refactorAvailable("Inline function", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineFunction_Availability_Export.ts b/tests/cases/fourslash/refactorInlineFunction_Availability_Export.ts new file mode 100644 index 0000000000000..f0bad12154d01 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Availability_Export.ts @@ -0,0 +1,12 @@ +/// + +//// export function /*z*/foo/*y*/() { return 42; } +//// function bar() { const meaningOfLife = /*x*/foo/*w*/(); } + +goTo.select("z", "y"); +verify.not.refactorAvailable("Inline function", "Inline all"); +verify.not.refactorAvailable("Inline function", "Inline here"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Inline function", "Inline all"); +verify.refactorAvailable("Inline function", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineFunction_Availability_Simple1.ts b/tests/cases/fourslash/refactorInlineFunction_Availability_Simple1.ts new file mode 100644 index 0000000000000..687026d118c15 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Availability_Simple1.ts @@ -0,0 +1,12 @@ +/// + +//// function /*z*/foo/*y*/() { return 42; } +//// function bar() { const meaningOfLife = /*x*/foo/*w*/(); } + +goTo.select("z", "y"); +verify.refactorAvailable("Inline function", "Inline all"); +verify.not.refactorAvailable("Inline function", "Inline here"); + +goTo.select("x", "w"); +verify.refactorAvailable("Inline function", "Inline all"); +verify.refactorAvailable("Inline function", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineFunction_Destructuring_Locals.ts b/tests/cases/fourslash/refactorInlineFunction_Destructuring_Locals.ts new file mode 100644 index 0000000000000..9e136374a3862 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Destructuring_Locals.ts @@ -0,0 +1,20 @@ +/// + +//// function /*z*/square/*y*/() { +//// const { arg, val } = { arg: 0, val: 2 }; +//// return arg * val; +//// } +//// function bar() { +//// const someValue = square(); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + const { arg, val } = { arg: 0, val: 2 }; + const someValue = arg * val; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Destructuring_Parameters.ts b/tests/cases/fourslash/refactorInlineFunction_Destructuring_Parameters.ts new file mode 100644 index 0000000000000..88dbb4b3dde64 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Destructuring_Parameters.ts @@ -0,0 +1,17 @@ +/// + +//// function /*z*/square/*y*/({ arg, val }: { arg: number, val: number }) { return arg * val; } +//// function bar() { +//// const someValue = square({ arg: 0, val: 2 }); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + const { arg, val } = { arg: 0, val: 2 }; + const someValue = arg * val; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Destructuring_Parameters_Array.ts b/tests/cases/fourslash/refactorInlineFunction_Destructuring_Parameters_Array.ts new file mode 100644 index 0000000000000..51c12af734bf0 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Destructuring_Parameters_Array.ts @@ -0,0 +1,17 @@ +/// + +//// function /*z*/square/*y*/([arg, val]) { return arg * val; } +//// function bar() { +//// const someValue = square([0, 2]); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + const [arg, val] = [0, 2]; + const someValue = arg * val; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Method.ts b/tests/cases/fourslash/refactorInlineFunction_Method.ts new file mode 100644 index 0000000000000..b0dbd7b7a8799 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Method.ts @@ -0,0 +1,16 @@ +/// + +//// class Car { +//// /*z*/drive/*y*/() { return "vroom"; } +//// drinkAndDrive() { return "glug " + this.drive(); } +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `class Car { + drinkAndDrive() { return "glug " + "vroom"; } +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Method_AtUsage.ts b/tests/cases/fourslash/refactorInlineFunction_Method_AtUsage.ts new file mode 100644 index 0000000000000..4beb5ffee6585 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Method_AtUsage.ts @@ -0,0 +1,16 @@ +/// + +//// class Car { +//// drive() { return "vroom"; } +//// drinkAndDrive() { return "glug " + this./*z*/drive/*y*/(); } +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `class Car { + drinkAndDrive() { return "glug " + "vroom"; } +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Method_Foreign.ts b/tests/cases/fourslash/refactorInlineFunction_Method_Foreign.ts new file mode 100644 index 0000000000000..f1b073fa90a15 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Method_Foreign.ts @@ -0,0 +1,24 @@ +/// + +//// class Car { +//// drive() { return "vroom"; } +//// /*z*/drinkAndDrive/*y*/() { return "glug " + this.drive(); } +//// } +//// class Driver { +//// car: Car = new Car(); +//// drinkAndDrive() { return this.car.drinkAndDrive(); } +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `class Car { + drive() { return "vroom"; } +} +class Driver { + car: Car = new Car(); + drinkAndDrive() { return "glug " + this.car.drive(); } +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Method_Foreign_AtUsage.ts b/tests/cases/fourslash/refactorInlineFunction_Method_Foreign_AtUsage.ts new file mode 100644 index 0000000000000..775688f84398e --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Method_Foreign_AtUsage.ts @@ -0,0 +1,25 @@ +/// + +//// class Car { +//// drive() { return "vroom"; } +//// drinkAndDrive() { return "glug " + this.drive(); } +//// } +//// class Driver { +//// car: Car = new Car(); +//// drinkAndDrive() { return this.car./*z*/drinkAndDrive/*y*/(); } +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline here", + actionDescription: "Inline here", + newContent: `class Car { + drive() { return "vroom"; } + drinkAndDrive() { return "glug " + this.drive(); } +} +class Driver { + car: Car = new Car(); + drinkAndDrive() { return "glug " + this.car.drive(); } +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_MultiReturn.ts b/tests/cases/fourslash/refactorInlineFunction_MultiReturn.ts new file mode 100644 index 0000000000000..8bb82956fe7d0 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_MultiReturn.ts @@ -0,0 +1,28 @@ +/// + +//// function /*z*/foo/*y*/() { +//// const x = 2; +//// if (x < 0) return 42; +//// else return 69; +//// } +//// function bar(arg: number) { +//// const someValue = foo(); +//// return arg * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number) { + let returnValue; + const x = 2; + if (x < 0) + returnValue = 42; + else + returnValue = 69; + const someValue = returnValue; + return arg * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Array.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Array.ts new file mode 100644 index 0000000000000..2c247abc84b2b --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Array.ts @@ -0,0 +1,19 @@ +/// + +//// function /*z*/square/*y*/([arg, val]) { return arg * val; } +//// function bar(arg: number, val: number) { +//// const someValue = square([0, 2]); +//// return (arg + val) * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number, val: number) { + const [arg_1, val_1] = [0, 2]; + const someValue = arg_1 * val_1; + return (arg + val) * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Locals.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Locals.ts new file mode 100644 index 0000000000000..4ec3cc837dbb0 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Locals.ts @@ -0,0 +1,22 @@ +/// + +//// function /*z*/square/*y*/() { +//// const { arg, val } = { arg: 0, val: 2 }; +//// return arg * val; +//// } +//// function bar(arg: number, val: number) { +//// const someValue = square(); +//// return (arg + val) * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number, val: number) { + const { arg: arg_1, val: val_1 } = { arg: 0, val: 2 }; + const someValue = arg_1 * val_1; + return (arg + val) * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Parameter.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Parameter.ts new file mode 100644 index 0000000000000..f4f6e2bb5348b --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Destructuring_Parameter.ts @@ -0,0 +1,19 @@ +/// + +//// function /*z*/square/*y*/({ arg, val }: { arg: number, val: number }) { return arg * val; } +//// function bar(arg: number, val: number) { +//// const someValue = square({ arg: 2, val: 4 }); +//// return (arg + val) * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number, val: number) { + const { arg: arg_1, val: val_1 } = { arg: 2, val: 4 }; + const someValue = arg_1 * val_1; + return (arg + val) * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_LocalInMultiReturn.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_LocalInMultiReturn.ts new file mode 100644 index 0000000000000..a7ef9e79f87d5 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_LocalInMultiReturn.ts @@ -0,0 +1,26 @@ +/// + +//// function /*z*/foo/*y*/() { +//// const arg = 2; +//// if (arg < 0) return 42 + arg; +//// else return arg; +//// } +//// function bar(arg: number) { +//// return arg * foo(); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number) { + let returnValue; + const arg_1 = 2; + if (arg_1 < 0) + returnValue = 42 + arg_1; + else + returnValue = arg_1; + return arg * returnValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_Locals.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Locals.ts new file mode 100644 index 0000000000000..4530f917ea207 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Locals.ts @@ -0,0 +1,22 @@ +/// + +//// function /*z*/square/*y*/() { +//// const arg = 2; +//// return arg*arg; +//// } +//// function bar(arg: number) { +//// const someValue = square(); +//// return arg * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number) { + const arg_1 = 2; + const someValue = arg_1 * arg_1; + return arg * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_MultiParameters.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_MultiParameters.ts new file mode 100644 index 0000000000000..65306a5b19060 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_MultiParameters.ts @@ -0,0 +1,20 @@ +/// + +//// function /*z*/square/*y*/(arg: number, val: number) { return arg*val; } +//// function bar(arg: number, val: number) { +//// const someValue = square(2, 4); +//// return (arg + val) * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number, val: number) { + const arg_1 = 2; + const val_1 = 4; + const someValue = arg_1 * val_1; + return (arg + val) * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_MultiReturn.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_MultiReturn.ts new file mode 100644 index 0000000000000..07b7797b27aba --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_MultiReturn.ts @@ -0,0 +1,28 @@ +/// + +//// function /*z*/foo/*y*/() { +//// const x = 2; +//// if (x < 0) return 42; +//// else return 69; +//// } +//// function bar(arg: number) { +//// const returnValue = arg * foo(); +//// return returnValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number) { + let returnValue_1; + const x = 2; + if (x < 0) + returnValue_1 = 42; + else + returnValue_1 = 69; + const returnValue = arg * returnValue_1; + return returnValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_NestedFunction.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_NestedFunction.ts new file mode 100644 index 0000000000000..84d2f1550ad95 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_NestedFunction.ts @@ -0,0 +1,21 @@ +/// + +//// function /*z*/square/*y*/(arg: number) { +//// return arg*arg; +//// function foo (parm: number) { const loc = 42; return loc + parm; } +//// } +//// function bar (parm: number, loc: number) { +//// const someValue = square(2); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar (parm: number, loc: number) { + const arg = 2; + function foo(parm: number) { const loc = 42; return loc + parm; } + const someValue = arg * arg; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NameConflict_Parameter.ts b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Parameter.ts new file mode 100644 index 0000000000000..442e3f1e315aa --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NameConflict_Parameter.ts @@ -0,0 +1,19 @@ +/// + +//// function /*z*/square/*y*/(arg: number) { return arg*arg; } +//// function bar(arg: number) { +//// const someValue = square(2); +//// return arg * someValue; +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar(arg: number) { + const arg_1 = 2; + const someValue = arg_1 * arg_1; + return arg * someValue; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_NestedFunction.ts b/tests/cases/fourslash/refactorInlineFunction_NestedFunction.ts new file mode 100644 index 0000000000000..a381f242a5cc1 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_NestedFunction.ts @@ -0,0 +1,21 @@ +/// + +//// function /*z*/square/*y*/(arg: number) { +//// return arg*arg; +//// function foo (parm: number) { const loc = 42; return loc + parm; } +//// } +//// function bar () { +//// const someValue = square(2); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar () { + const arg = 2; + function foo(parm: number) { const loc = 42; return loc + parm; } + const someValue = arg * arg; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Parameters.ts b/tests/cases/fourslash/refactorInlineFunction_Parameters.ts new file mode 100644 index 0000000000000..bb322409920fb --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Parameters.ts @@ -0,0 +1,17 @@ +/// + +//// function /*z*/square/*y*/(arg: number) { return arg*arg; } +//// function bar() { +//// const someValue = square(2); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + const arg = 2; + const someValue = arg * arg; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Parameters_Multiple.ts b/tests/cases/fourslash/refactorInlineFunction_Parameters_Multiple.ts new file mode 100644 index 0000000000000..547862e1e9ceb --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Parameters_Multiple.ts @@ -0,0 +1,18 @@ +/// + +//// function /*z*/mult/*y*/(arg0: number, arg1: number) { return arg0*arg1; } +//// function bar() { +//// const someValue = mult(21, 2); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + const arg0 = 21; + const arg1 = 2; + const someValue = arg0 * arg1; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_ReturnNothing.ts b/tests/cases/fourslash/refactorInlineFunction_ReturnNothing.ts new file mode 100644 index 0000000000000..d3b3fbd574513 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_ReturnNothing.ts @@ -0,0 +1,19 @@ +/// + +//// function /*z*/foo/*y*/() { +//// console.log("noot noot"); +//// } +//// function bar() { +//// /*x*/foo/*w*/(); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + console.log("noot noot"); + +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_ReturnVoid.ts b/tests/cases/fourslash/refactorInlineFunction_ReturnVoid.ts new file mode 100644 index 0000000000000..fae3d15611dbd --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_ReturnVoid.ts @@ -0,0 +1,20 @@ +/// + +//// function /*z*/foo/*y*/(): void { +//// console.log("noot noot"); +//// return 0; +//// } +//// function bar() { +//// /*x*/foo/*w*/(); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + console.log("noot noot"); + +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Simple1_Declaration.ts b/tests/cases/fourslash/refactorInlineFunction_Simple1_Declaration.ts new file mode 100644 index 0000000000000..7b9fdfe174916 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Simple1_Declaration.ts @@ -0,0 +1,12 @@ +/// + +//// function /*z*/foo/*y*/() { return 42; } +//// function bar() { const meaningOfLife = /*x*/foo/*w*/(); } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { const meaningOfLife = 42; }` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Simple1_Usage_InlineAll.ts b/tests/cases/fourslash/refactorInlineFunction_Simple1_Usage_InlineAll.ts new file mode 100644 index 0000000000000..1e71a827a57c4 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Simple1_Usage_InlineAll.ts @@ -0,0 +1,12 @@ +/// + +//// function /*z*/foo/*y*/() { return 42; } +//// function bar() { const meaningOfLife = /*x*/foo/*w*/(); } + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { const meaningOfLife = 42; }` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Simple1_Usage_InlineHere.ts b/tests/cases/fourslash/refactorInlineFunction_Simple1_Usage_InlineHere.ts new file mode 100644 index 0000000000000..cb8af9b0fae85 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Simple1_Usage_InlineHere.ts @@ -0,0 +1,13 @@ +/// + +//// function /*z*/foo/*y*/() { return 42; } +//// function bar() { const meaningOfLife = /*x*/foo/*w*/(); } + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline here", + actionDescription: "Inline here", + newContent: `function foo() { return 42; } +function bar() { const meaningOfLife = 42; }` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Simple2_Declaration.ts b/tests/cases/fourslash/refactorInlineFunction_Simple2_Declaration.ts new file mode 100644 index 0000000000000..24dbb3ab108ef --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Simple2_Declaration.ts @@ -0,0 +1,22 @@ +/// + +//// function /*z*/foo/*y*/() { +//// let num = 42; +//// num++; +//// return num; +//// } +//// function bar() { +//// const meaningOfLife = /*x*/foo/*w*/(); +//// } + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + let num = 42; + num++; + const meaningOfLife = num; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Simple2_Usage_InlineAll.ts b/tests/cases/fourslash/refactorInlineFunction_Simple2_Usage_InlineAll.ts new file mode 100644 index 0000000000000..fa7a5b2970907 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Simple2_Usage_InlineAll.ts @@ -0,0 +1,22 @@ +/// + +//// function /*z*/foo/*y*/() { +//// let num = 42; +//// num++; +//// return num; +//// } +//// function bar() { +//// const meaningOfLife = /*x*/foo/*w*/(); +//// } + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function bar() { + let num = 42; + num++; + const meaningOfLife = num; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineFunction_Simple2_Usage_InlineHere.ts b/tests/cases/fourslash/refactorInlineFunction_Simple2_Usage_InlineHere.ts new file mode 100644 index 0000000000000..a4e78f8d51e21 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineFunction_Simple2_Usage_InlineHere.ts @@ -0,0 +1,27 @@ +/// + +//// function /*z*/foo/*y*/() { +//// let num = 42; +//// num++; +//// return num; +//// } +//// function bar() { +//// const meaningOfLife = /*x*/foo/*w*/(); +//// } + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline function", + actionName: "Inline here", + actionDescription: "Inline here", + newContent: `function foo() { + let num = 42; + num++; + return num; +} +function bar() { + let num = 42; + num++; + const meaningOfLife = num; +}` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Availability_Export.ts b/tests/cases/fourslash/refactorInlineLocal_Availability_Export.ts new file mode 100644 index 0000000000000..cb6d68bb5a064 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Availability_Export.ts @@ -0,0 +1,12 @@ +/// + +//// export const /*z*/a/*y*/ = 42-3; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("z", "y"); +verify.not.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineLocal_Availability_MultiAssign.ts b/tests/cases/fourslash/refactorInlineLocal_Availability_MultiAssign.ts new file mode 100644 index 0000000000000..f8f5be18b090b --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Availability_MultiAssign.ts @@ -0,0 +1,13 @@ +/// + +//// let /*z*/a/*y*/ = 42-3; +//// a = 23; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("z", "y"); +verify.not.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineLocal_Availability_Simple.ts b/tests/cases/fourslash/refactorInlineLocal_Availability_Simple.ts new file mode 100644 index 0000000000000..2ea28c60a4462 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Availability_Simple.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42-3; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("z", "y"); +verify.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); + +goTo.select("x", "w"); +verify.refactorAvailable("Inline local", "Inline all"); +verify.refactorAvailable("Inline local", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineLocal_Availability_Uninitialized.ts b/tests/cases/fourslash/refactorInlineLocal_Availability_Uninitialized.ts new file mode 100644 index 0000000000000..b9474b3c423b9 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Availability_Uninitialized.ts @@ -0,0 +1,13 @@ +/// + +//// let /*z*/a/*y*/; +//// const b = 2 * /*x*/a/*w*/; + + +goTo.select("z", "y"); +verify.not.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Inline local", "Inline all"); +verify.not.refactorAvailable("Inline local", "Inline here"); diff --git a/tests/cases/fourslash/refactorInlineLocal_FunctionCall_NoUnnecessaryParentheses.ts b/tests/cases/fourslash/refactorInlineLocal_FunctionCall_NoUnnecessaryParentheses.ts new file mode 100644 index 0000000000000..da730df64b9c2 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_FunctionCall_NoUnnecessaryParentheses.ts @@ -0,0 +1,14 @@ +/// + +//// const /*z*/a/*y*/ = 42; +//// function b (arg: number) { return arg; } +//// b(a); + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `function b (arg: number) { return arg; } +b(42);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_MultiUse_Declaration.ts b/tests/cases/fourslash/refactorInlineLocal_MultiUse_Declaration.ts new file mode 100644 index 0000000000000..d38019a1e1130 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_MultiUse_Declaration.ts @@ -0,0 +1,14 @@ +/// + +//// const /*z*/a/*y*/ = 42 - 3; +//// const b = 2 * /*x*/a/*w*/; +//// const c = 2 + a; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 * (42 - 3); +const c = 2 + (42 - 3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_MultiUse_Usage_InlineAll.ts b/tests/cases/fourslash/refactorInlineLocal_MultiUse_Usage_InlineAll.ts new file mode 100644 index 0000000000000..72ae093d03d5d --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_MultiUse_Usage_InlineAll.ts @@ -0,0 +1,14 @@ +/// + +//// const /*z*/a/*y*/ = 42 - 3; +//// const b = 2 * /*x*/a/*w*/; +//// const c = 2 + a; + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 * (42 - 3); +const c = 2 + (42 - 3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_MultiUse_Usage_InlineHere.ts b/tests/cases/fourslash/refactorInlineLocal_MultiUse_Usage_InlineHere.ts new file mode 100644 index 0000000000000..feb58e0904bdb --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_MultiUse_Usage_InlineHere.ts @@ -0,0 +1,15 @@ +/// + +//// const /*z*/a/*y*/ = 42 - 3; +//// const b = 2 * /*x*/a/*w*/; +//// const c = 2 + a; + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline here", + actionDescription: "Inline here", + newContent: `const a = 42 - 3; +const b = 2 * (42 - 3); +const c = 2 + a;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_1.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_1.ts new file mode 100644 index 0000000000000..f76dd383278c3 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_1.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = /*x*/a/*w*/ + 2; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 42 + 3 + 2;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_2.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_2.ts new file mode 100644 index 0000000000000..09a3bb0322d79 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_2.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = /*x*/a/*w*/ * 2; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = (42 + 3) * 2;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_3.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_3.ts new file mode 100644 index 0000000000000..71282d0104333 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_3.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = /*x*/a/*w*/ - 2; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 42 + 3 - 2;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_4.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_4.ts new file mode 100644 index 0000000000000..bc16c3ec83cea --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_4.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 == 3; +//// const b = /*x*/a/*w*/ != false; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 42 == 3 != false;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_5.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_5.ts new file mode 100644 index 0000000000000..0af0832e4405e --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_5.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 * 3; +//// const b = /*x*/a/*w*/ * 2; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 42 * 3 * 2;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_6.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_6.ts new file mode 100644 index 0000000000000..fb310c503c1fb --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_6.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 ** 3; +//// const b = /*x*/a/*w*/ ** 2; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = (42 ** 3) ** 2;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_7.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_7.ts new file mode 100644 index 0000000000000..f76dd383278c3 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_7.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = /*x*/a/*w*/ + 2; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 42 + 3 + 2;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_8.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_8.ts new file mode 100644 index 0000000000000..57c2b62776faf --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Left_8.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = -3; +//// const b = /*x*/a/*w*/ ** 9; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = (-3) ** 9;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_1.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_1.ts new file mode 100644 index 0000000000000..2227104e86533 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_1.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 * (42 + 3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_2.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_2.ts new file mode 100644 index 0000000000000..87d118c9f5797 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_2.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 * 3; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 * 42 * 3;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_3.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_3.ts new file mode 100644 index 0000000000000..4bbcecce4db22 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_3.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 * 3; +//// const b = 2 + /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 + 42 * 3;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_4.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_4.ts new file mode 100644 index 0000000000000..d57017595ab9b --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_4.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = 2 - /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 - (42 + 3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_5.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_5.ts new file mode 100644 index 0000000000000..c66cd6f915637 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_5.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = 2 + /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 + 42 + 3;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_6.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_6.ts new file mode 100644 index 0000000000000..6cf454ca23913 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_6.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 ** 3; +//// const b = 2 ** /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 ** 42 ** 3;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_7.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_7.ts new file mode 100644 index 0000000000000..fc01e5b1f4c1b --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_7.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 == 3; +//// const b = false != /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = false != (42 == 3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_8.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_8.ts new file mode 100644 index 0000000000000..e1873d6bef2fe --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Binary_Right_8.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = -3; +//// const b = 9 ** /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 9 ** (-3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Unary.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Unary.ts new file mode 100644 index 0000000000000..bb04afb0342ff --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Unary.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42 + 3; +//// const b = !/*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = !(42 + 3);` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Precedence_Unary2.ts b/tests/cases/fourslash/refactorInlineLocal_Precedence_Unary2.ts new file mode 100644 index 0000000000000..8dea3329586bd --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Precedence_Unary2.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = !42; +//// const b = !/*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = !!42;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Simple_Declaration.ts b/tests/cases/fourslash/refactorInlineLocal_Simple_Declaration.ts new file mode 100644 index 0000000000000..39a9e76ea4fc9 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Simple_Declaration.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("z", "y"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 * 42;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Simple_Usage_InlineAll.ts b/tests/cases/fourslash/refactorInlineLocal_Simple_Usage_InlineAll.ts new file mode 100644 index 0000000000000..49c5316fc3018 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Simple_Usage_InlineAll.ts @@ -0,0 +1,12 @@ +/// + +//// const /*z*/a/*y*/ = 42; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline all", + actionDescription: "Inline all", + newContent: `const b = 2 * 42;` +}); diff --git a/tests/cases/fourslash/refactorInlineLocal_Simple_Usage_InlineHere.ts b/tests/cases/fourslash/refactorInlineLocal_Simple_Usage_InlineHere.ts new file mode 100644 index 0000000000000..8e28ba414a355 --- /dev/null +++ b/tests/cases/fourslash/refactorInlineLocal_Simple_Usage_InlineHere.ts @@ -0,0 +1,13 @@ +/// + +//// const /*z*/a/*y*/ = 42; +//// const b = 2 * /*x*/a/*w*/; + +goTo.select("x", "w"); +edit.applyRefactor({ + refactorName: "Inline local", + actionName: "Inline here", + actionDescription: "Inline here", + newContent: `const a = 42; +const b = 2 * 42;` +});