Introduce Property: Initial support (properties with getter)

This commit is contained in:
Alexey Sedunov
2015-03-04 18:01:06 +03:00
parent 48914e70ff
commit 03fada71d7
36 changed files with 396 additions and 158 deletions
@@ -681,6 +681,7 @@ fun main(args: Array<String>) {
testClass(javaClass<AbstractJetExtractionTest>()) {
model("refactoring/introduceVariable", extension = "kt", testMethod = "doIntroduceVariableTest")
model("refactoring/extractFunction", extension = "kt", testMethod = "doExtractFunctionTest")
model("refactoring/introduceProperty", extension = "kt", testMethod = "doIntroducePropertyTest")
}
testClass(javaClass<AbstractSelectExpressionForDebuggerTest>()) {
+5
View File
@@ -101,6 +101,11 @@
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractMethod"/>
</action>
<action id="IntroduceProperty" class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.IntroducePropertyAction"
text="Introduce _Property..." use-shortcut-of="IntroduceField">
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceField"/>
</action>
<action id="CopyAsDiagnosticTest" class="org.jetbrains.kotlin.idea.actions.internal.CopyAsDiagnosticTestAction"
text="Copy Current File As Diagnostic Test">
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift T"/>
@@ -88,9 +88,10 @@ fun getFunctionForExtractedFragment(
val targetSibling = tmpFile.getDeclarations().firstOrNull()
if (targetSibling == null) return null
val analysisResult = ExtractionData(
tmpFile, newDebugExpression.toRange(), targetSibling, ExtractionOptions(false, true, true)
).performAnalysis()
val options = ExtractionOptions(inferUnitTypeForUnusedValues = false,
enableListBoxing = true,
allowSpecialClassNames = true)
val analysisResult = ExtractionData(tmpFile, newDebugExpression.toRange(), targetSibling, options).performAnalysis()
if (analysisResult.status != Status.SUCCESS) {
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
}
@@ -19,11 +19,15 @@ package org.jetbrains.kotlin.idea.refactoring;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.JetElement;
import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.psi.JetVisitorVoid;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
@@ -72,7 +76,9 @@ public class JetNameValidatorImpl extends JetNameValidator {
private boolean checkElement(String name, PsiElement sibling, final Set<JetScope> visitedScopes) {
if (!(sibling instanceof JetElement)) return true;
final BindingContext bindingContext = ResolvePackage.analyze((JetElement) sibling);
AnalysisResult analysisResult = ResolvePackage.analyzeAndGetResult((JetElement) sibling);
final BindingContext bindingContext = analysisResult.getBindingContext();
final ModuleDescriptor module = analysisResult.getModuleDescriptor();
final Name identifier = Name.identifier(name);
final Ref<Boolean> result = new Ref<Boolean>(true);
@@ -84,12 +90,33 @@ public class JetNameValidatorImpl extends JetNameValidator {
}
}
@Nullable
private JetScope getScope(@NotNull JetExpression expression) {
PsiElement parent = expression.getParent();
if (parent instanceof JetClassBody) {
JetClassOrObject classOrObject = (JetClassOrObject) parent.getParent();
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject);
return classDescriptor instanceof ClassDescriptorWithResolutionScopes
? ((ClassDescriptorWithResolutionScopes) classDescriptor).getScopeForMemberDeclarationResolution()
: null;
}
if (parent instanceof JetFile) {
PackageViewDescriptor packageViewDescriptor = module.getPackage(((JetFile) parent).getPackageFqName());
return packageViewDescriptor != null ? packageViewDescriptor.getMemberScope() : null;
}
return bindingContext.get(BindingContext.RESOLUTION_SCOPE, expression);
}
@Override
public void visitExpression(@NotNull JetExpression expression) {
JetScope resolutionScope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expression);
if (!visitedScopes.add(resolutionScope)) return;
JetScope resolutionScope = getScope(expression);
if (resolutionScope != null) {
if (!visitedScopes.add(resolutionScope)) return;
boolean noConflict;
if (myTarget == Target.PROPERTIES) {
noConflict = resolutionScope.getProperties(identifier).isEmpty()
@@ -2,6 +2,7 @@ cannot.refactor.not.expression=Cannot find an expression to introduce
cannot.refactor.not.expression.to.extract=Cannot find an expression to extract
expressions.title=Expressions
introduce.variable=Introduce Variable
introduce.property=Introduce Property
cannot.refactor.no.container=Cannot refactor in this place
cannot.refactor.no.expression=Cannot perform refactoring without an expression
cannot.refactor.expression.has.unit.type=Cannot introduce expression of unit type
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureHandler;
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler;
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler;
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler;
import org.jetbrains.kotlin.idea.refactoring.safeDelete.SafeDeletePackage;
import org.jetbrains.kotlin.psi.*;
@@ -39,6 +40,11 @@ public class JetRefactoringSupportProvider extends RefactoringSupportProvider {
return new KotlinIntroduceVariableHandler();
}
@NotNull
public RefactoringActionHandler getIntroducePropertyHandler() {
return new KotlinIntroducePropertyHandler();
}
@NotNull
public RefactoringActionHandler getExtractFunctionHandler() {
return new ExtractKotlinFunctionHandler();
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.KotlinExtractFunctionDialog">
<grid id="27dc6" binding="contentPane" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="contentPane" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="522" height="351"/>
<xy x="20" y="20" width="522" height="318"/>
</constraints>
<properties/>
<border type="none"/>
@@ -11,7 +11,7 @@
<grid id="34b30" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
@@ -38,7 +38,7 @@
<grid id="b45d1" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="-1" height="200"/>
</grid>
</constraints>
@@ -127,32 +127,9 @@
</grid>
<vspacer id="cd10f">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="ad40b" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1b96b" class="javax.swing.JCheckBox" binding="propertyCheckBox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Extract as property"/>
</properties>
</component>
<hspacer id="df17e">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
</children>
</grid>
</children>
</grid>
</form>
@@ -36,11 +36,10 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -51,7 +50,6 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
private KotlinFunctionSignatureComponent signaturePreviewField;
private EditorTextField functionNameField;
private JLabel functionNameLabel;
private JCheckBox propertyCheckBox;
private KotlinParameterTablePanel parameterTablePanel;
private final Project project;
@@ -146,18 +144,6 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
}
);
propertyCheckBox.setEnabled(ExtractionEnginePackage.canGenerateProperty(originalDescriptor.getDescriptor()));
if (propertyCheckBox.isEnabled()) {
propertyCheckBox.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
update();
}
}
);
}
parameterTablePanel = new KotlinParameterTablePanel() {
@Override
protected void updateSignature() {
@@ -260,7 +246,7 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
return new ExtractableCodeDescriptor(
descriptor.getExtractionData(),
descriptor.getOriginalContext(),
getFunctionName(),
Collections.singletonList(getFunctionName()),
getVisibility(),
ContainerUtil.newArrayList(oldToNewParameters.values()),
descriptor.getReceiverParameter(),
@@ -272,11 +258,6 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
@NotNull
public ExtractionGeneratorConfiguration getCurrentConfiguration() {
return new ExtractionGeneratorConfiguration(currentDescriptor, getGeneratorOptions());
}
@NotNull
public ExtractionGeneratorOptions getGeneratorOptions() {
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected(), false);
return new ExtractionGeneratorConfiguration(currentDescriptor, ExtractionGeneratorOptions.DEFAULT);
}
}
@@ -305,14 +305,16 @@ fun ControlFlow.toDefault(): ControlFlow =
data class ExtractableCodeDescriptor(
val extractionData: ExtractionData,
val originalContext: BindingContext,
val name: String,
val suggestedNames: List<String>,
val visibility: String,
val parameters: List<Parameter>,
val receiverParameter: Parameter?,
val typeParameters: List<TypeParameter>,
val replacementMap: Map<Int, Replacement>,
val controlFlow: ControlFlow
)
) {
val name: String get() = suggestedNames.firstOrNull() ?: ""
}
data class ExtractionGeneratorOptions(
val inTempFile: Boolean = false,
@@ -54,12 +54,13 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.idea.refactoring.compareDescriptors
data class ExtractionOptions(
val inferUnitTypeForUnusedValues: Boolean,
val enableListBoxing: Boolean,
val allowSpecialClassNames: Boolean
val inferUnitTypeForUnusedValues: Boolean = true,
val enableListBoxing: Boolean = false,
val extractAsProperty: Boolean = false,
val allowSpecialClassNames: Boolean = false
) {
class object {
val DEFAULT = ExtractionOptions(true, false, false)
val DEFAULT = ExtractionOptions()
}
}
@@ -796,10 +796,13 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
JetNameValidatorImpl(
targetSibling.getParent(),
if (targetSibling is JetClassInitializer) targetSibling.getParent() else targetSibling,
JetNameValidatorImpl.Target.FUNCTIONS_AND_CLASSES
if (options.extractAsProperty) JetNameValidatorImpl.Target.PROPERTIES else JetNameValidatorImpl.Target.FUNCTIONS_AND_CLASSES
)
val functionName = if (returnType.isDefault()) "" else {
JetNameSuggester.suggestNames(returnType, functionNameValidator, DEFAULT_FUNCTION_NAME).first()
val functionNames = if (returnType.isDefault()) {
Collections.emptyList<String>()
}
else {
JetNameSuggester.suggestNames(returnType, functionNameValidator, DEFAULT_FUNCTION_NAME).toList()
}
controlFlow.jumpOutputValue?.elementToInsertAfterCall?.accept(
@@ -819,7 +822,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
ExtractableCodeDescriptor(
this,
bindingContext,
functionName,
functionNames,
if (isVisibilityApplicable()) "private" else "",
adjustedParameters.sortBy { it.name },
receiverParameter,
@@ -66,15 +66,18 @@ fun ExtractionGeneratorConfiguration.getDeclarationText(
return if (isSpecial()) DEBUG_TYPE_REFERENCE_STRING else descriptorRenderer.renderType(this)
}
receiverParameter?.let { builder.receiver(it.getParameterType(extractionData.options.allowSpecialClassNames).typeAsString()) }
builder.name(if (name != "") name else DEFAULT_FUNCTION_NAME)
parameters.forEach { parameter ->
builder.param(parameter.name, parameter.getParameterType(extractionData.options.allowSpecialClassNames).typeAsString())
descriptor.receiverParameter?.let {
builder.receiver(it.getParameterType(descriptor.extractionData.options.allowSpecialClassNames).typeAsString())
}
with(controlFlow.outputValueBoxer.returnType) {
builder.name(if (descriptor.name != "") descriptor.name else DEFAULT_FUNCTION_NAME)
descriptor.parameters.forEach { parameter ->
builder.param(parameter.name,
parameter.getParameterType(descriptor.extractionData.options.allowSpecialClassNames).typeAsString())
}
with(descriptor.controlFlow.outputValueBoxer.returnType) {
if (isDefault() || isError()) builder.noReturnType() else builder.returnType(this.typeAsString())
}
@@ -521,10 +524,10 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{
adjustDeclarationBody(declaration)
if (declaration is JetNamedFunction && declaration.getContainingJetFile().suppressDiagnosticsInDebugMode) {
declaration.getReceiverTypeReference()?.debugTypeInfo = receiverParameter?.getParameterType(true)
declaration.getReceiverTypeReference()?.debugTypeInfo = descriptor.receiverParameter?.getParameterType(true)
for ((i, param) in declaration.getValueParameters().withIndex()) {
param.getTypeReference()?.debugTypeInfo = parameters[i].getParameterType(true)
param.getTypeReference()?.debugTypeInfo = descriptor.parameters[i].getParameterType(true)
}
if (declaration.getTypeReference() != null) {
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable
import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.lang.refactoring.RefactoringSupportProvider
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringSupportProvider
import org.jetbrains.kotlin.idea.refactoring.introduce.*
public class IntroducePropertyAction : AbstractIntroduceAction() {
override fun getRefactoringHandler(provider: RefactoringSupportProvider): RefactoringActionHandler? =
(provider as? JetRefactoringSupportProvider)?.getIntroducePropertyHandler()
}
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty
import com.intellij.openapi.project.*
import com.intellij.psi.*
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.editor.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.idea.refactoring.introduce.*
import org.jetbrains.kotlin.idea.refactoring.*
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.util.psi.patternMatching.*
import kotlin.test.*
import com.intellij.openapi.application.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.*
import java.util.*
public class KotlinIntroducePropertyHandler(
val helper: ExtractionEngineHelper = KotlinIntroducePropertyHandler.InteractiveExtractionHelper
): KotlinIntroduceHandlerBase() {
object InteractiveExtractionHelper : ExtractionEngineHelper() {
override fun configureInteractively(
project: Project,
editor: Editor,
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
continuation: (ExtractionGeneratorConfiguration) -> Unit
) {
val descriptor = descriptorWithConflicts.descriptor
if (descriptor.canGenerateProperty()) {
continuation(ExtractionGeneratorConfiguration(descriptor, ExtractionGeneratorOptions.DEFAULT.copy(extractAsProperty = true)))
}
else {
showErrorHint(project, editor, "Can't introduce property for this expression", INTRODUCE_PROPERTY)
}
}
}
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
if (file !is JetFile) return
selectElements(
operationName = INTRODUCE_PROPERTY,
editor = editor,
file = file,
getContainers = {(elements, parent) ->
parent.getExtractionContainers(strict = true, includeAll = true).filter { it is JetClassBody || it is JetFile }
}
) { (elements, targetSibling) ->
val adjustedElements = (elements.singleOrNull() as? JetBlockExpression)?.getStatements() ?: elements
if (adjustedElements.isNotEmpty()) {
val options = ExtractionOptions(extractAsProperty = true)
val extractionData = ExtractionData(file, adjustedElements.toRange(), targetSibling, options)
ExtractionEngine(INTRODUCE_PROPERTY, helper).run(editor, extractionData) {
val property = it.declaration as JetProperty
val descriptor = it.config.descriptor
editor.getCaretModel().moveToOffset(property.getTextOffset())
editor.getSelectionModel().removeSelection()
if (editor.getSettings().isVariableInplaceRenameEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
with(PsiDocumentManager.getInstance(project)) {
commitDocument(editor.getDocument())
doPostponedOperationsAndUnblockDocument(editor.getDocument())
}
val introducer = KotlinInplaceVariableIntroducer(
property,
editor,
project,
INTRODUCE_PROPERTY, // title
JetExpression.EMPTY_ARRAY, // occurrences
null, // expr
false, // replaceOccurrence
property,
false, // isVar
true, // doNotChangeVar
descriptor.controlFlow.outputValueBoxer.returnType,
true // noTypeInference
)
introducer.performInplaceRefactoring(LinkedHashSet(descriptor.suggestedNames))
}
}
}
else {
showErrorHintByKey(project, editor, "cannot.refactor.no.expression", INTRODUCE_PROPERTY)
}
}
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
fail("$INTRODUCE_PROPERTY can only be invoked from editor")
}
}
private val INTRODUCE_PROPERTY: String = JetRefactoringBundle.message("introduce.property")
@@ -28,6 +28,10 @@ fun showErrorHint(project: Project, editor: Editor, message: String, title: Stri
CodeInsightUtils.showErrorHint(project, editor, message, title, null)
}
fun showErrorHintByKey(project: Project, editor: Editor, messageKey: String, title: String) {
showErrorHint(project, editor, JetRefactoringBundle.message(messageKey), title)
}
fun selectElements(
operationName: String,
editor: Editor,
@@ -36,7 +40,7 @@ fun selectElements(
continuation: (elements: List<PsiElement>, targetSibling: PsiElement) -> Unit
) {
fun showErrorHintByKey(key: String) {
showErrorHint(file.getProject(), editor, JetRefactoringBundle.message(key), operationName)
showErrorHintByKey(file.getProject(), editor, key, operationName)
}
fun onSelectionComplete(parent: PsiElement, elements: List<PsiElement>, targetContainer: PsiElement) {
@@ -1,11 +0,0 @@
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: val m: kotlin.Int defined in foo
// EXTRACT_AS_PROPERTY
val n: Int = 1
// SIBLING:
fun foo(): Int {
val m = 1
return <selection>n + m + 1</selection>
}
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// OPTIONS: true, true, false
// OPTIONS: true, true, false, false
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// OPTIONS: true, true, false
// OPTIONS: true, true, false, false
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
class A {
val i = 1
fun foo(): Int {
return <selection>1 + 2</selection>
}
}
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
class A {
val i = 1
fun foo(): Int {
return i1
}
private val i1 = 1 + 2
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
val i = 1
fun foo(): Int {
return <selection>1 + 2</selection>
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val i = 1
fun foo(): Int {
return i1
}
private val i1 = 1 + 2
@@ -1,6 +1,4 @@
// EXTRACT_AS_PROPERTY
// EXTRACTION_TARGET: property with getter
fun foo(n: Int): Int {
// SIBLING:
return {<selection>n + 1</selection>}()
}
@@ -1,5 +1,4 @@
// EXTRACT_AS_PROPERTY
// SIBLING:
// EXTRACTION_TARGET: property with getter
fun foo() {
<selection>1 + 1</selection>
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with getter
class A {
fun foo(): Int {
<selection>val a = 1 + 2
val b = a*2</selection>
val c = b - 1
}
}
@@ -0,0 +1,15 @@
// EXTRACTION_TARGET: property with getter
class A {
fun foo(): Int {
val b = i
val c = b - 1
}
private val i: Int
get() {
val a = 1 + 2
val b = a * 2
return b
}
}
@@ -1,8 +1,7 @@
// EXTRACT_AS_PROPERTY
// EXTRACTION_TARGET: property with getter
class A(val n: Int = 1) {
val m: Int = 2
// SIBLING:
fun foo(): Int {
return <selection>m + n + 1</selection>
}
@@ -1,8 +1,7 @@
// EXTRACT_AS_PROPERTY
// EXTRACTION_TARGET: property with getter
class A(val n: Int = 1) {
val m: Int = 2
// SIBLING:
fun foo(): Int {
return i
}
@@ -1,8 +1,6 @@
// EXTRACT_AS_PROPERTY
// EXTRACTION_TARGET: property with getter
val n: Int = 1
// SIBLING:
fun foo(): Int {
return <selection>n + 1</selection>
}
@@ -1,8 +1,6 @@
// EXTRACT_AS_PROPERTY
// EXTRACTION_TARGET: property with getter
val n: Int = 1
// SIBLING:
fun foo(): Int {
return i
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with getter
val n: Int = 1
fun foo(): Int {
val m = 1
return <selection>n + m + 1</selection>
}
@@ -39,6 +39,8 @@ import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.psi.JetPackageDirective
import org.jetbrains.kotlin.utils.emptyOrSingletonList
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
import java.util.*
import kotlin.test.assertTrue
public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTestCase() {
@@ -57,6 +59,28 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
}
}
protected fun doIntroducePropertyTest(path: String) {
doTest(path) { file ->
val helper = object : ExtractionEngineHelper() {
override fun configure(
descriptor: ExtractableCodeDescriptor,
generatorOptions: ExtractionGeneratorOptions
): ExtractionGeneratorConfiguration {
return ExtractionGeneratorConfiguration(
descriptor,
generatorOptions.copy(extractAsProperty = true)
)
}
}
KotlinIntroducePropertyHandler(helper).invoke(
fixture.getProject(),
fixture.getEditor(),
file,
DataManager.getInstance().getDataContext(fixture.getEditor().getComponent())
)
}
}
protected fun doExtractFunctionTest(path: String) {
doTest(path) { file ->
var explicitPreviousSibling: PsiElement? = null
@@ -86,15 +110,12 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PARAM_DESCRIPTOR: ").joinToString()
val expectedTypes =
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PARAM_TYPES: ").map { "[$it]" }.joinToString()
val extractAsProperty = InTextDirectivesUtils.isDirectiveDefined(fileText, "// EXTRACT_AS_PROPERTY")
val extractionOptions = InTextDirectivesUtils.findListWithPrefixes(fileText, "// OPTIONS: ").let {
if (it.isNotEmpty()) {
[suppress("CAST_NEVER_SUCCEEDS")]
val args = it.map { it.toBoolean() }.copyToArray() as Array<Any?>
val constructor = javaClass<ExtractionOptions>().getConstructors()[0]
assertTrue(constructor.getParameterTypes().size() == args.size(), "Wrong number of parameters was passed for ExtractOptions constructor: expected = ${constructor.getParameterTypes().size()}, actual = ${args.size()}. \nTest directive: // OPTIONS: $it")
constructor.newInstance(*args) as ExtractionOptions
javaClass<ExtractionOptions>().getConstructors().first { it.getParameterTypes().size() == args.size() }.newInstance(*args) as ExtractionOptions
} else ExtractionOptions.DEFAULT
}
@@ -114,16 +135,19 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
val allParameters = emptyOrSingletonList(descriptor.receiverParameter) + descriptor.parameters
val actualDescriptors = allParameters.map { renderer.render(it.originalDescriptor) }.joinToString()
val actualTypes = allParameters.map {
it.parameterTypeCandidates.map { renderer.renderType(it) }.joinToString(", ", "[", "]")
it.getParameterTypeCandidates(false).map { renderer.renderType(it) }.joinToString(", ", "[", "]")
}.joinToString()
assertEquals(expectedDescriptors, actualDescriptors, "Expected descriptors mismatch.")
assertEquals(expectedTypes, actualTypes, "Expected types mismatch.")
return ExtractionGeneratorConfiguration(
if (descriptor.name == "") descriptor.copy(name = "__dummyTestFun__") else descriptor,
generatorOptions.copy(extractAsProperty = extractAsProperty)
)
val newDescriptor = if (descriptor.name == "") {
descriptor.copy(suggestedNames = Collections.singletonList("__dummyTestFun__"))
}
else {
descriptor
}
return ExtractionGeneratorConfiguration(newDescriptor, generatorOptions)
}
}
)
@@ -31,6 +31,7 @@ import java.util.regex.Pattern;
@InnerTestClasses({
JetExtractionTestGenerated.IntroduceVariable.class,
JetExtractionTestGenerated.ExtractFunction.class,
JetExtractionTestGenerated.IntroduceProperty.class,
})
@RunWith(JUnit3RunnerWithInners.class)
public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
@@ -316,7 +317,6 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
@TestMetadata("idea/testData/refactoring/extractFunction")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({
ExtractFunction.AsProperty.class,
ExtractFunction.Basic.class,
ExtractFunction.ControlFlow.class,
ExtractFunction.DefaultContainer.class,
@@ -332,45 +332,6 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractFunction"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("idea/testData/refactoring/extractFunction/asProperty")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AsProperty extends AbstractJetExtractionTest {
public void testAllFilesPresentInAsProperty() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractFunction/asProperty"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("extractToClass.kt")
public void testExtractToClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/asProperty/extractToClass.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("extractToFile.kt")
public void testExtractToFile() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/asProperty/extractToFile.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("extractToFunction.kt")
public void testExtractToFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/asProperty/extractToFunction.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("extractUnit.kt")
public void testExtractUnit() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/asProperty/extractUnit.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("extractWithParams.kt")
public void testExtractWithParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/asProperty/extractWithParams.kt");
doExtractFunctionTest(fileName);
}
}
@TestMetadata("idea/testData/refactoring/extractFunction/basic")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1953,4 +1914,62 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
}
}
}
@TestMetadata("idea/testData/refactoring/introduceProperty")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class IntroduceProperty extends AbstractJetExtractionTest {
public void testAllFilesPresentInIntroduceProperty() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceProperty"),
Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("extractToClassWithNameClash.kt")
public void testExtractToClassWithNameClash() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractToClassWithNameClash.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractToFileWithNameClash.kt")
public void testExtractToFileWithNameClash() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractToFileWithNameClash.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractToFunction.kt")
public void testExtractToFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractToFunction.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractUnit.kt")
public void testExtractUnit() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractUnit.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractWithGetterMultipleExpressions.kt")
public void testExtractWithGetterMultipleExpressions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithGetterMultipleExpressions.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractWithGetterToClass.kt")
public void testExtractWithGetterToClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithGetterToClass.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractWithGetterToFile.kt")
public void testExtractWithGetterToFile() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithGetterToFile.kt");
doIntroducePropertyTest(fileName);
}
@TestMetadata("extractWithParams.kt")
public void testExtractWithParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithParams.kt");
doIntroducePropertyTest(fileName);
}
}
}